Balloon Fight (JU) - verified complete (in 7d7d78d).
Donkey Kong Jr. (JU) - no source changes needed. Full byte-accounting audit of
prg.s: all 16,384 PRG bytes emitted exactly once (6,199 instructions + 3,889
.byte + 16 .word), every emitted value equals the ROM byte, zero audit-comment
mismatches, zero duplicate addresses, no .incbin remains. The complete-opcode
tracer reaches 6,135 instructions / 12,358 code bytes, all of which are already
in the file; the file carries 121 further code bytes the tracer does not find,
reached through the DeferredTaskHandlerTable dispatch at $BFFE.
Donkey Kong 3 (JUE) - real bug fix. The project was recorded as BYTE-IDENTICAL
but had never built byte-identical: its committed reconstructed.nes also
differed from rom.nes. The bne at CPU $C14C (PRG $014C) is annotated 'D0 12'
(target $C160) but assembled as 'D0 0D', because it referenced
StoreSelectedPlayerAndScreenFlags ($C15B) instead of the routine actually at
$C160. Retargeted to ClearAudioSetGameMode, which is also the semantically
correct destination for a 'mode != $01' test. cmp now passes.
Root OVERVIEW.md: added a Pass 1 methodology section recording both Pass 1
limitations (documented-opcodes-only, and no runtime dispatch) with the measured
per-project impact, and flagged Joypad Test Cartridge as the one project still
carrying the truncated Pass 1.
All 15 top-level projects verified byte-identical.
Balloon Fight's Pass 1 was already complete and hand-done to a higher standard
than a static tracer: 5,303 instructions / 11,260 code bytes, versus 5,277 /
11,202 from the complete-opcode tracer. The 166 bytes the tracer finds 'missing'
are genuinely data (command scripts at $D437/$D45A referenced by
'lda $D45A,y', the $C3B5 pointer table, the arithmetic table at $FBCA), so no
code was added.
The real remaining Pass 1 gap was the repo rule that no ROM byte stays hidden
in an .incbin. Two spans converted:
- $F4C5-$F4FF (59 bytes) -> TrailingFillBeforeApuTables: $FF padding after
the sound-init RTS at $F4C4, unreached
- $F5FC-$F5FF (4 bytes) -> SoundEngineTailBytes: unreachable filler between
the sound engine's final JMP ($F5F9) and ApuPeriodTable ($F600)
Verified: symbol layout restored (ApuPeriodTable $F600, UpdateAudioFrame $FFF7),
both new blocks byte-exact against the ROM, no .incbin remains.
Byte-identical ROM verified.
The original tracer followed only vectors and direct jsr/jmp/branch targets and
knew just the 151 documented 6502 opcodes. Wild Gunman dispatches at runtime, so
~4.9 KB of real code was filed as .byte data.
Root cause: Label_C341 ($C341) is a computed goto that pulls the CALLER's return
address with two 'pla' instructions, so its jump table physically follows each
'jsr $C341' (opcode 20 41 C3) and is indexed by A*2. Seven call sites exist; the
table at $C114 (after 'lda $33 / jsr $C341') decodes to 14 valid targets.
Changes:
- Tracer rebuilt with all 54 undocumented opcodes + 26 unofficial NOPs (these
ROMs use them as 2/3-byte padding; a tracer that halts on them strands code)
- Iterative expansion seeded from inline $C341 tables, in-trace control-flow
references, and explicit pointer tables (e.g. the 5-entry table at $DEDD)
- prg.s updated SURGICALLY: all 249 existing routine headers, semantic labels
and locals.inc aliases preserved; only data->code promotion applied
- Fixed a byte-dropping parser bug: some .byte lines use symbolic values
(Zp01, NmiPhase, Ctrl1), which a $XX-only regex silently skipped
- Branch targets landing mid-instruction now force a split so labels land on
real instruction boundaries
- Bytes that cannot be assembled stay .byte: ca65's 6502 has no
SLO/RLA/SRE/RRA/ISC/DCP/LAS/TAS/ANE/AXS/LXA mnemonics
Verified: 1,597 -> 3,956 instructions, 3,352 -> 8,085 code bytes (49.3%).
Every one of the 16,384 PRG bytes emitted exactly once, every value equal to the
ROM, zero audit-comment mismatches. Byte-identical ROM verified.
- Decoded the VramWrite ($E1CC) stream format: <addr hi> <addr lo> <count> <bytes>, $00 terminator
- Named all 9 payload streams ($E4CC-$E7C2, no gaps/overlaps) plus VramWriteSourcePointerTable at $F000
- Split PadBitTableLives into Player1TileTable ($E4B4) and Player2TileTable ($E4C0), 12 entries each (Y=0..11)
- Decoded CHR font (digits $30-$39, letters $3A-$52, space $5F; 'W' missing from the A-Z run)
and recovered on-screen text including 'PRESS A TO TEST SIDE-A' / 'SIDE-B'
- Documented that the 16 KiB PRG is two byte-identical 8 KiB halves; $C000 copy is dead
- Documented 7 unreached subroutines inside the live code region (kept as .byte per Pass-1 policy)
- Fixed a stray duplicate sentence and stale _wip paths in root OVERVIEW.md
Byte-identical ROM verified.
Moves NES PowerPad Test Card (U) from _wip/ to NES PowerPad Test Card (U)/ with full ROM build:
- Adds header.s, chr.s, linker.cfg (HEADER+PRG+CHR segments)
- Updates Makefile to build and verify reconstructed.nes vs rom.nes
- Adds rom.nes copy + copilot/rom.nes symlink per repo convention
- Verified byte-identical build
Pass 1 complete: full code/data classification (~7.2% code / ~92.8% data)
Pass 2 complete: routine headers for all ~50 labels, semantic naming for key
routines (Reset, NmiHandler, VramWrite, ReadPad, ProcessPads, etc.),
zero-page aliases in locals.inc (Zp00-Zp15)
Pass 3 complete: data tables named (PaletteData at ,
PadBitTableLives at )
Root OVERVIEW.md updated:
- Removes NES PowerPad Test Card from _wip skeleton table (21 → 20 skeletons)
- Adds NES PowerPad Test Card as #15 in numbered inventory (BYTE-IDENTICAL, Pass 2/3)
- Updates summary count from 14 to 15 projects