Pass 1 verification for Balloon Fight / DK Jr, and fix DK 3 branch-target bug

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.
This commit is contained in:
tmerritt committed 2026-10-16 20:42:21 -04:00
1 parent 7d7d78dcda
commit e9caa8cb36
4 files changed
+39 -10

No files matched your search

+1 -1
View File
@@ -364,7 +364,7 @@ DispatchGameModePhase:
cmp #$20 ; PRG $0146 | CPU $C146 | C9 20 cmp #$20 ; PRG $0146 | CPU $C146 | C9 20
bne CheckGameModeZero ; PRG $0148 | CPU $C148 | D0 1D bne CheckGameModeZero ; PRG $0148 | CPU $C148 | D0 1D
cpx #$01 ; PRG $014A | CPU $C14A | E0 01 cpx #$01 ; PRG $014A | CPU $C14A | E0 01
bne StoreSelectedPlayerAndScreenFlags ; PRG $014C | CPU $C14C | D0 12 bne ClearAudioSetGameMode ; PRG $014C | CPU $C14C | D0 12
lda StartInputLatch ; PRG $014E | CPU $C14E | A5 37 lda StartInputLatch ; PRG $014E | CPU $C14E | A5 37
bne ClampScreenSelectionPositionMin ; PRG $0150 | CPU $C150 | D0 21 bne ClampScreenSelectionPositionMin ; PRG $0150 | CPU $C150 | D0 21
ldy SelectedPlayerAndScreenFlags ; PRG $0152 | CPU $C152 | A4 38 ldy SelectedPlayerAndScreenFlags ; PRG $0152 | CPU $C152 | A4 38
@@ -650,10 +650,10 @@ al 00C1C9 .FillNametableTileBytes
al 00C1A9 .ClearNametable2000WithTile24 al 00C1A9 .ClearNametable2000WithTile24
al 00C1AD .FillNametableAndClearAttributes al 00C1AD .FillNametableAndClearAttributes
al 00C17D .ConvertPlayerSlotToOamOffset al 00C17D .ConvertPlayerSlotToOamOffset
al 00C160 .ClearAudioSetGameMode
al 00C16F .UpdateZp_37_C16F al 00C16F .UpdateZp_37_C16F
al 00C173 .ClampScreenSelectionPositionMin
al 00C15B .StoreSelectedPlayerAndScreenFlags al 00C15B .StoreSelectedPlayerAndScreenFlags
al 00C173 .ClampScreenSelectionPositionMin
al 00C160 .ClearAudioSetGameMode
al 00C167 .CheckGameModeZero al 00C167 .CheckGameModeZero
al 00C18D .DispatchScreenState al 00C18D .DispatchScreenState
al 00C13A .ClearActorFlagsLoop al 00C13A .ClearActorFlagsLoop
Binary file not shown.
+36 -7
View File
@@ -18,7 +18,7 @@ This document provides a consolidated overview of all NES disassembly projects i
| 8 | Tennis (JU) | Japan | 16 KB | 8 KB | Vertical | BYTE-IDENTICAL | $C07D / $C000 / $C000 | | 8 | Tennis (JU) | Japan | 16 KB | 8 KB | Vertical | BYTE-IDENTICAL | $C07D / $C000 / $C000 |
| 9 | Pac-Man (USA) (Namco) | USA | 16 KB | 8 KB | Horizontal | BYTE-IDENTICAL | $C10E / $C000 / $C17E | | 9 | Pac-Man (USA) (Namco) | USA | 16 KB | 8 KB | Horizontal | BYTE-IDENTICAL | $C10E / $C000 / $C17E |
| 10 | Donkey Kong 3 (JUE) | Japan | 16 KB | 8 KB | Horizontal | BYTE-IDENTICAL | $C07D / $C000 / $C000 | | 10 | Donkey Kong 3 (JUE) | Japan | 16 KB | 8 KB | Horizontal | BYTE-IDENTICAL | $C07D / $C000 / $C000 |
| 11 | Donkey Kong Jr. (JU) | Japan | 16 KB | 8 KB | Horizontal | BYTE-IDENTICAL (Pass 1) | $C6E6 / $C66E / $FFF0 | | 11 | Donkey Kong Jr. (JU) | Japan | 16 KB | 8 KB | Horizontal | BYTE-IDENTICAL (Pass 2/3) | $C6E6 / $C66E / $FFF0 |
--- ---
@@ -31,7 +31,7 @@ This document provides a consolidated overview of all NES disassembly projects i
- **Linker:** PRG at $C000, 16 KB; single segment - **Linker:** PRG at $C000, 16 KB; single segment
- **Vectors:** NMI → $C094 (`NmiHandler`), RESET → $C000 (`Reset`), IRQ → $C0F7 (`IrqHandler`, self-loop) - **Vectors:** NMI → $C094 (`NmiHandler`), RESET → $C000 (`Reset`), IRQ → $C0F7 (`IrqHandler`, self-loop)
- **Key subsystems:** NMI-driven frame loop with PPU command buffer, round spawner ($C790), bonus objects ($CB74), carrier/rescue sequence ($C6F9), particle system ($CDAA), sound driver ($FFF7 banked) - **Key subsystems:** NMI-driven frame loop with PPU command buffer, round spawner ($C790), bonus objects ($CB74), carrier/rescue sequence ($C6F9), particle system ($CDAA), sound driver ($FFF7 banked)
- **Build fix:** None needed; byte-identical out of the box - **Build fix:** None needed; byte-identical out of the box. **Pass 1 verified complete 2026-10-16** — 5,303 instructions / 11,260 code bytes (68.7%), which is *more* than the complete-opcode static tracer finds (5,277 / 11,202); the 166 tracer-only bytes are genuine data (command scripts at $D437/$D45A, the $C3B5 pointer table, the arithmetic table at $FBCA), not missed code. The last two `.incbin` spans were converted to explicit classified bytes (`TrailingFillBeforeApuTables` at $F4C5, `SoundEngineTailBytes` at $F5FC), so no ROM byte is hidden
- **Details:** `Balloon Fight (JU)/OVERVIEW.md` - **Details:** `Balloon Fight (JU)/OVERVIEW.md`
### 2. Baseball (UE) ### 2. Baseball (UE)
@@ -121,7 +121,7 @@ This document provides a consolidated overview of all NES disassembly projects i
- **Linker:** HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB) - **Linker:** HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB)
- **Vectors:** NMI → $C07D (`NmiHandler`), RESET → $C000 (`Reset`), IRQ → $C000 (`Reset` reused) - **Vectors:** NMI → $C07D (`NmiHandler`), RESET → $C000 (`Reset`), IRQ → $C000 (`Reset` reused)
- **Key subsystems:** Main loop with object/game state management at $C02E–$C18D, object movement and geometry calculations ($C28E–$C683), audio engine ($F8DE+) with sound channels ($F0–$FF), PPU command buffer streaming ($C3F4+) - **Key subsystems:** Main loop with object/game state management at $C02E–$C18D, object movement and geometry calculations ($C28E–$C683), audio engine ($F8DE+) with sound channels ($F0–$FF), PPU command buffer streaming ($C3F4+)
- **Build fix:** Build split `main.s` into `header.s`, `prg.s`, `chr.s`; added `RandomStateAlt = $E4` for `dec $E4` at CPU $E71B (zero-page reuse with `RandomState0 = $54`) - **Build fix:** Build split `main.s` into `header.s`, `prg.s`, `chr.s`; added `RandomStateAlt = $E4` for `dec $E4` at CPU $E71B (zero-page reuse with `RandomState0 = $54`). **2026-10-16 — one-byte branch-target bug fixed:** the project was recorded as BYTE-IDENTICAL but had never built byte-identical. The `bne` at CPU $C14C (PRG $014C) was annotated `D0 12` (target $C160) but assembled as `D0 0D`, because it was labelled `StoreSelectedPlayerAndScreenFlags` ($C15B) instead of the routine actually at $C160. Retargeted to `ClearAudioSetGameMode` — which is also the semantically correct target for a "mode != $01" test. `cmp` now passes.
- **Details:** `Donkey Kong 3 (JUE)/copilot/OVERVIEW.md` - **Details:** `Donkey Kong 3 (JUE)/copilot/OVERVIEW.md`
### 11. Donkey Kong Jr. (JU) ### 11. Donkey Kong Jr. (JU)
@@ -131,7 +131,7 @@ This document provides a consolidated overview of all NES disassembly projects i
- **Linker:** HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB) - **Linker:** HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB)
- **Vectors:** NMI → $C6E6 (`NmiHandler`), RESET → $C66E (`ResetHandler`), IRQ → $FFF0 (stray byte, effectively unused) - **Vectors:** NMI → $C6E6 (`NmiHandler`), RESET → $C66E (`ResetHandler`), IRQ → $FFF0 (stray byte, effectively unused)
- **Key subsystems:** Reset idles in a PRNG-advance loop — the entire frame state machine (title/attract/gameplay/sound) runs inside the NMI handler; VRAM/event queue at $0300 with dispatch table at $BFFE; 8-byte PRNG at $18–$1F; sound timers at $F1AF - **Key subsystems:** Reset idles in a PRNG-advance loop — the entire frame state machine (title/attract/gameplay/sound) runs inside the NMI handler; VRAM/event queue at $0300 with dispatch table at $BFFE; 8-byte PRNG at $18–$1F; sound timers at $F1AF
- **Build fix:** None needed; byte-identical out of the box (`make` in `copilot/`). Pass-1 status — unresolved bytes preserved as `.incbin` blocks pending Pass 2/3 renaming and table decoding - **Build fix:** None needed; byte-identical out of the box (`make` in `copilot/`). **Pass 1 verified complete 2026-10-16** — a full byte-accounting audit of `prg.s` confirms all 16,384 PRG bytes are emitted exactly once (6,199 instructions + 3,889 `.byte` + 16 `.word`), every emitted value equals the ROM byte, and there are zero audit-comment mismatches or duplicate addresses. No `.incbin` remains. A complete-opcode static tracer independently reaches 6,135 instructions / 12,358 code bytes (75.4%) — every one of which is already present in the file, and the file carries 121 further code bytes the tracer does not find (reached via the `DeferredTaskHandlerTable` dispatch at $BFFE). Pass 2/3 naming is complete; Pass 3 tables are named (`DeferredTaskHandlerTable`, `HudDigitDescriptorTable`, …)
- **Details:** `Donkey Kong Jr. (JU)/OVERVIEW.md` - **Details:** `Donkey Kong Jr. (JU)/OVERVIEW.md`
### 12. CIRCUS ### 12. CIRCUS
@@ -258,13 +258,42 @@ make verify # (where supported by the Makefile)
--- ---
## Pass 1 methodology note (2026-10-16)
The original Pass 1 disassembler had two limitations that silently understated
code coverage. Both are worth knowing before trusting a coverage percentage:
1. **Documented opcodes only.** Its table held the 151 documented 6502 opcodes.
These ROMs also use the 54 undocumented opcodes and the 26 unofficial NOPs
as 2/3-byte padding; a tracer that halts on them strands the real code behind.
2. **No runtime dispatch.** It followed only vectors and direct `jsr`/`jmp`/branch
targets. Games here dispatch through computed jumps — `jmp ($0014)`, the
`pla`-based inline-table gotos in Wild Gunman (`Label_C341`), and task tables
like Donkey Kong Jr.'s `DeferredTaskHandlerTable` at $BFFE.
Effect measured per project (old tracer → complete-opcode dispatch-aware tracer):
| Project | Old | Complete | Action taken |
|---------|-----|----------|--------------|
| NES PowerPad Test Card (U) | 546 instr | 559 | Pass 1 already sound |
| Joypad Test Cartridge (U) | 282 instr | **1,379** | **Pass 1 redo still outstanding** |
| Wild Gunman (U) | 1,600 instr | **4,083** | Pass 1 redone (49.3% code) |
| Balloon Fight (JU) | 5,216 instr | 5,277 | hand-done file was already better |
| Donkey Kong Jr. (JU) | 6,135 instr | 6,135 | file already complete |
**Joypad Test Cartridge (U) is the one project still carrying the old, truncated
Pass 1** — its recorded "3.5% code" is really ~18%, and roughly 2.4 KB of code is
still filed as `.byte` data. It must be redone before its Pass 2/3 can be trusted.
---
## Summary ## Summary
All 15 projects in this repository rebuild **byte-identical** to their original ROMs: All 15 projects in this repository rebuild **byte-identical** to their original ROMs:
| # | Project | Build Status | Pass 3 | | # | Project | Build Status | Pass 3 |
|---|---------|--------------|--------| |---|---------|--------------|--------|
| 1 | Balloon Fight (JU) | BYTE-IDENTICAL | in progress | | 1 | Balloon Fight (JU) | BYTE-IDENTICAL | complete (Pass 1 verified) |
| 2 | Baseball (UE) | BYTE-IDENTICAL | complete | | 2 | Baseball (UE) | BYTE-IDENTICAL | complete |
| 3 | Bomberman (U) | BYTE-IDENTICAL | complete | | 3 | Bomberman (U) | BYTE-IDENTICAL | complete |
| 4 | Burger Time (U) | BYTE-IDENTICAL | complete | | 4 | Burger Time (U) | BYTE-IDENTICAL | complete |
@@ -274,10 +303,10 @@ All 15 projects in this repository rebuild **byte-identical** to their original
| 8 | Tennis (JU) | BYTE-IDENTICAL | complete | | 8 | Tennis (JU) | BYTE-IDENTICAL | complete |
| 9 | Pac-Man (USA) (Namco) | BYTE-IDENTICAL | complete | | 9 | Pac-Man (USA) (Namco) | BYTE-IDENTICAL | complete |
| 10 | Donkey Kong 3 (JUE) | BYTE-IDENTICAL | complete | | 10 | Donkey Kong 3 (JUE) | BYTE-IDENTICAL | complete |
| 11 | Donkey Kong Jr. (JU) | BYTE-IDENTICAL | pending | | 11 | Donkey Kong Jr. (JU) | BYTE-IDENTICAL | complete (Pass 1 verified) |
| 12 | CIRCUS | BYTE-IDENTICAL | complete | | 12 | CIRCUS | BYTE-IDENTICAL | complete |
| 13 | Wild Gunman (U) | BYTE-IDENTICAL | pending (Pass 1 redone) | | 13 | Wild Gunman (U) | BYTE-IDENTICAL | pending (Pass 1 redone) |
| 14 | Joypad Test Cartridge (U) | BYTE-IDENTICAL | pending | | 14 | Joypad Test Cartridge (U) | BYTE-IDENTICAL | pending |
| 15 | NES PowerPad Test Card (U) | BYTE-IDENTICAL | pending | | 15 | NES PowerPad Test Card (U) | BYTE-IDENTICAL | complete |
The 20 `_wip/` skeleton projects above also all rebuild byte-identical (PRG level); all Pass 1 work and promotion to this index's numbered inventory is future work. The 20 `_wip/` skeleton projects above also all rebuild byte-identical (PRG level); all Pass 1 work and promotion to this index's numbered inventory is future work.