from __future__ import annotations import argparse from pathlib import Path from ..formatting import h16, parse_int from .eeprom_image import write_eeprom_snapshot from .memory import describe_regions from .runner import H8536Emulator def discover_rom_path(root: Path) -> Path | None: candidates = [ root / "ROM" / "M27C512@DIP28_1.BIN", root / "rom.bin", ] candidates.extend(sorted((root / "ROM").glob("*.BIN")) if (root / "ROM").exists() else []) candidates.extend(sorted((root / "ROM").glob("*.bin")) if (root / "ROM").exists() else []) for candidate in candidates: if candidate.is_file(): return candidate return None def load_rom(path: Path | None = None, root: Path | None = None) -> tuple[bytes, Path]: root = root if root is not None else Path.cwd() rom_path = path if path is not None else discover_rom_path(root) if rom_path is None: raise FileNotFoundError( "could not discover ROM bytes; pass --rom PATH, expected ROM/M27C512@DIP28_1.BIN or another ROM/*.BIN" ) return rom_path.read_bytes(), rom_path def build_arg_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Minimal H8/536 emulation harness scaffold") parser.add_argument("--rom", type=Path, help="ROM image path; defaults to ROM/M27C512@DIP28_1.BIN when present") parser.add_argument("--max-steps", type=int, default=64, help="maximum CPU steps to execute") parser.add_argument("--trace", action="store_true", help="print decoded/executed instruction trace") parser.add_argument("--stop-on-heartbeat", action="store_true", help="stop only when 00 00 00 00 80 DA is emitted through SCI1 TDR") parser.add_argument("--memory-map", action="store_true", help="print the scaffolded memory map before running") parser.add_argument("--interval-steps", type=int, default=2048, help="rough step period for the scaffolded timer interrupt") parser.add_argument("--clock-hz", type=parse_int, default=10_000_000, help="CPU/phi clock in Hz for calibrated FRT timing") parser.add_argument("--frt1-ocia-steps", type=int, default=None, help="legacy step-period override for FRT1 OCIA") parser.add_argument("--frt2-ocia-steps", type=int, default=None, help="legacy step-period override for FRT2 OCIA") parser.add_argument("--p9-fast-path", action="store_true", help="shortcut known P9 bit-banged transfer routines for exploration") parser.add_argument("--p9-fast-input", type=parse_int, default=0xFF, help="default byte returned by the P9 fast-path read routine") parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="legacy fallback for older wrapper experiments; known BFE0/BFFE wrappers use the X24164 model") parser.add_argument("--p7-input", type=parse_int, default=0xFF, help="external P7 pin state for input bits; DIP-off board default is 0xFF") parser.add_argument("--eeprom-seed", choices=("blank", "factory"), default="blank", help="initial X24164/shadow state before reset") parser.add_argument("--eeprom-load", type=Path, help="load a 0x1000-byte logical EEPROM image before running") parser.add_argument("--eeprom-save", type=Path, help="save the final 0x1000-byte logical EEPROM image after running") parser.add_argument("--eeprom-report", type=Path, help="write a readable EEPROM snapshot report after running") parser.add_argument("--eeprom-report-json", type=Path, help="write a structured EEPROM snapshot report after running") parser.add_argument("--eeprom-report-include-image", action="store_true", help="include the full EEPROM image as hex in JSON reports") return parser def main(argv: list[str] | None = None) -> int: args = build_arg_parser().parse_args(argv) try: rom_bytes, rom_path = load_rom(args.rom) except FileNotFoundError as exc: print(str(exc)) return 2 emulator = H8536Emulator( rom_bytes, interval_steps=args.interval_steps, frt1_ocia_steps=args.frt1_ocia_steps, frt2_ocia_steps=args.frt2_ocia_steps, clock_hz=args.clock_hz, p9_fast_path_enabled=args.p9_fast_path, p9_fast_default_input_byte=args.p9_fast_input, p9_fast_default_wrapper_success=args.p9_fast_optimistic_wrapper, p7_input=args.p7_input, eeprom_seed=args.eeprom_seed, ) if args.eeprom_load: emulator.memory.load_eeprom_image(args.eeprom_load.read_bytes()) print(f"eeprom_loaded={args.eeprom_load}") print(f"rom={rom_path}") print(f"reset_vector={h16(emulator.reset_vector())}") if args.memory_map: print(describe_regions()) report = emulator.run(args.max_steps, trace=args.trace, stop_on_heartbeat=args.stop_on_heartbeat) if args.trace: for line in report.trace: print(line) for line in report.summary_lines(): print(line) if not report.heartbeat_seen: print("heartbeat_status=not reached; no heartbeat is reported unless bytes are emitted via SCI1_TDR") if args.eeprom_save: args.eeprom_save.parent.mkdir(parents=True, exist_ok=True) args.eeprom_save.write_bytes(emulator.memory.dump_eeprom_image()) print(f"eeprom_saved={args.eeprom_save}") if args.eeprom_report: write_eeprom_snapshot(emulator.memory, args.eeprom_report, rom_bytes=rom_bytes) print(f"eeprom_report={args.eeprom_report}") if args.eeprom_report_json: write_eeprom_snapshot( emulator.memory, args.eeprom_report_json, rom_bytes=rom_bytes, as_json=True, include_image_hex=args.eeprom_report_include_image, ) print(f"eeprom_report_json={args.eeprom_report_json}") return 0