R.O.B. (Reverse-engineering Opcode Buddy)
A 6502 disassembler for NROM NES games that writes one hyperlinked HTML listing, plus a scripted-emulator harness for discovering what RAM bytes and routines actually mean. Developed against Capcom's 1942.

R.O.B. takes an iNES ROM and produces a single self-contained HTML page: address, bytes, label, mnemonic, operand and comment columns, in the style of the well-known Super Mario Bros. disassembly, with every jump target and data reference turned into an in-page link. The test subject is 1942 (Capcom, 1985). The ROM isn’t in the repo; a pre-rendered 1942.html is.
Disassembly
disasm.py is plain Python with no dependencies.
- Recursive descent from the NMI, RESET and IRQ vectors, following
JSR,JMP absand branches. - Routines reached only through dispatch tables are recovered heuristically: the byte after every
RTS/RTI/JMP/BRKis tried as an entry point, and so is every 16-bit word in unclaimed data that points into PRG. A tentative trace only counts if it runs at least six clean instructions, ends in a terminator and contains noBRK. - Anything not reached is emitted as
.bytedata. Hardware registers get names (PPUCTRL,OAMDMA, …). roundtrip.pyreparses the HTML and checks it rebuilds the PRG exactly: 32768 of 32768 bytes on 1942.
Limits: mapper 0 only, no bank switching, no indirect-jump tracing, no CHR decoding.
Finding out what things mean
Auto-labels like L_c2e8 only get you so far. scenarios.py boots the ROM in nes-py, plays scripted button sequences (press start, autofire, idle, die once, …), snapshots RAM at checkpoints, and joins every changed byte to the store instructions in the disassembly that write it. drift.py adds per-byte timelines tagged UP, DOWN, SAW or FLIP.
There’s Game Genie tooling too: genie.py decodes codes to an address in the listing, and genie_candidates.py ranks patchable bytes (counter decrements, comparisons, dispatch-table entries) and suggests codes to try. fiber_trace.py walks 1942’s coroutine-style entity system one fiber at a time. Results go into labels.json, which is up to 211 named addresses.

python3 disasm.py 1942.nes -o 1942.html
python3 scenarios.py press_start