#!/usr/bin/env python3 # Convert PDP-8 .rim format files to ILDEC .oct 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_rim_to_oct(input_path, output_path): # Initialize memory to 0 memory = [0] * 4096 try: with open(input_path, "rb") as f: raw_data = f.read() except Exception as e: print_error(f"Failed to read input file '{input_path}': {e}") return False # Filter out bytes with MSB set (bit 7) data = [b for b in raw_data if not (b & 0x80)] i = 0 words_loaded = 0 while i < len(data) - 1: word = ((data[i] & 0x3F) << 6) | (data[i+1] & 0x3F) if data[i] & 0x40 and i + 3 < len(data): addr = word val = ((data[i+2] & 0x3F) << 6) | (data[i+3] & 0x3F) memory[addr] = val words_loaded += 1 i += 4 else: i += 2 # Output to .oct format try: with open(output_path, 'w') as f: for addr in range(4096): if memory[addr] != 0: f.write(f"{addr:04o}: {memory[addr]:04o}\n") except Exception as e: print_error(f"Failed to write output file: {e}") return False print(f"Successfully converted '{input_path}' to '{output_path}' ({words_loaded} words loaded).") return True def main(): args = sys.argv if hasattr(sys, 'argv') else [] if len(args) < 2 or len(args) > 3: print("Usage: python3 rim2oct.py [output_oct_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 .oct 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] + ".oct" else: output_path = input_path + ".oct" success = convert_rim_to_oct(input_path, output_path) if not success: sys.exit(1) if __name__ == '__main__': main()