#!/usr/bin/env python3 # Convert ILDEC .oct format files to PDP-8 .rim format # Created by Luxferre in 2026, released into the public domain import sys def print_error(msg): sys.stderr.write(f"Error: {msg}\n") def convert_octaddr_to_rim(input_path, output_path): pairs = [] line_num = 0 try: with open(input_path, 'r') as f: for line in f: line_num += 1 # Strip comments starting with /, #, ; clean_line = line for comment_char in ('/', '#', ';'): if comment_char in clean_line: clean_line = clean_line.split(comment_char, 1)[0] clean_line = clean_line.strip() if not clean_line: continue if ':' not in clean_line: print_error(f"Line {line_num}: Missing colon separator (format is 'addr: data').") return False parts = clean_line.split(':', 1) addr_str = parts[0].strip() data_str = parts[1].strip() try: addr = int(addr_str, 8) except ValueError: print_error(f"Line {line_num}: Invalid octal address '{addr_str}'.") return False try: data = int(data_str, 8) except ValueError: print_error(f"Line {line_num}: Invalid octal data '{data_str}'.") return False if not (0 <= addr <= 0o7777): print_error(f"Line {line_num}: Address '{addr_str}' ({oct(addr)}) out of 12-bit range (0-7777).") return False if not (0 <= data <= 0o7777): print_error(f"Line {line_num}: Data '{data_str}' ({oct(data)}) out of 12-bit range (0-7777).") return False pairs.append((addr, data)) except Exception as e: print_error(f"Failed to read input file '{input_path}': {e}") return False if not pairs: print_error("No valid address:data pairs found in the input file.") return False # Write RIM format # Leader: 2 bytes of 0xFF # For each pair: # addr high: ((addr >> 6) & 0x3F) | 0x40 # addr low: addr & 0x3F # data high: (data >> 6) & 0x3F # data low: data & 0x3F # Trailer: 2 bytes of 0xFF try: with open(output_path, 'wb') as f: # Write leader f.write(bytes([0xFF] * 2)) for addr, data in pairs: addr_hi = ((addr >> 6) & 0x3F) | 0x40 addr_lo = addr & 0x3F data_hi = (data >> 6) & 0x3F data_lo = data & 0x3F f.write(bytes([addr_hi, addr_lo, data_hi, data_lo])) # Write trailer f.write(bytes([0xFF] * 2)) except Exception as e: print_error(f"Failed to write output file: {e}") return False print(f"Successfully converted '{input_path}' to '{output_path}' ({len(pairs)} words).") return True def main(): args = sys.argv if hasattr(sys, 'argv') else [] if len(args) < 2 or len(args) > 3: print("Usage: python3 oct2rim.py [output_rim_file]") sys.exit(1) input_path = args[1] if len(args) == 3: output_path = args[2] else: # Generate default output name by replacing extension with .rim dot_idx = input_path.rfind('.') slash_idx = max(input_path.rfind('/'), input_path.rfind('\\')) if dot_idx > slash_idx: output_path = input_path[:dot_idx] + ".rim" else: output_path = input_path + ".rim" success = convert_octaddr_to_rim(input_path, output_path) if not success: sys.exit(1) if __name__ == '__main__': main()