Files
disasm/OVERVIEW.md
T
tmerritt ae3d71cef2 Complete Pass 3 on Wild Gunman (U) and Joypad Test Cartridge (U)
- Wild Gunman (U): Updated OVERVIEW.md to Pass 2/3, added SpriteOamTable comment at , fixed build path references
- Joypad Test Cartridge (U): Updated OVERVIEW.md to Pass 2/3, fixed build path references
- Donkey Kong Jr. (JU): Fixed root OVERVIEW.md summary table (was Pass 1, actually Pass 2/3 per project OVERVIEW.md)
- Root OVERVIEW.md: Updated summary table (Wild Gunman & Joypad Test Cartridge → Pass 2/3), updated observation notes

All projects verified byte-identical.
2026-10-16 19:11:04 -04:00

21 KiB
Raw Blame History

Disassembly Project Index — /home/tmerritt/Projects/disassembly/

This document provides a consolidated overview of all NES disassembly projects in this directory. Each project has its own folder containing a copilot/ subdirectory with the full source, build tooling, and an individual OVERVIEW.md (at the project folder root) with a detailed power-on execution flow analysis.


Project Inventory

# Project Region/Version PRG Size CHR Size Mirroring Build Status Vectors (NMI / RESET / IRQ)
1 Balloon Fight (JU) Japan 16 KB 8 KB Horizontal BYTE-IDENTICAL $C094 / $C000 / $C0F7
2 Baseball (UE) USA/Europe 16 KB 8 KB Horizontal BYTE-IDENTICAL $C046 / $C40B / $C43E
3 Bomberman (U) USA 16 KB 8 KB Vertical BYTE-IDENTICAL $C01A / $C000 / $FFF9
4 Burger Time (U) USA 16 KB 8 KB Horizontal BYTE-IDENTICAL $C046 / $C40B / $C43E
5 Clu Clu Land (JU) Japan 16 KB 8 KB Horizontal BYTE-IDENTICAL $C1F1 / $C000 / $FFF9
6 Defender 2 (U) USA 16 KB 8 KB Vertical BYTE-IDENTICAL $CAA1 / $C983 / $CA9E
7 Excitebike (JU) Japan 16 KB 8 KB Vertical BYTE-IDENTICAL $C1F1 / $FD24 / $FD24
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
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

Per-Project Details

1. Balloon Fight (JU)

  • Folder: Balloon Fight (JU)/
  • ROM: balloon.nes (24,592 bytes)
  • Source: copilot/prg.s (8,538 lines)
  • Linker: PRG at $C000, 16 KB; single segment
  • 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)
  • Build fix: None needed; byte-identical out of the box
  • Details: Balloon Fight (JU)/OVERVIEW.md

2. Baseball (UE)

  • Folder: Baseball (UE)/
  • ROM: baseball.nes (24,592 bytes)
  • Source: copilot/prg.s (21,624 lines — largest source)
  • Linker: PRG at $C000, 16 KB; single segment
  • Vectors: NMI → $C046 (NMI), RESET → $C40B (Reset), IRQ → $C43E (IRQ, rti)
  • Key subsystems: Queued event system ($EB66–$EB9C) as the main frame driver, round/state machine ($C5FE), batting/pitching ($C6044–$C6414), object sorting ($C339), sound driver ($F9D0–$FE2x)
  • Build fix: None needed; byte-identical out of the box
  • Details: Baseball (UE)/OVERVIEW.md

3. Bomberman (U)

  • Folder: Bomberman (U)/
  • ROM: bomberman.nes (24,592 bytes)
  • Source: copilot/main.s (8,070 lines)
  • Linker: HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB)
  • Vectors: NMI → $C01A (NmiHandler), RESET → $C000 (Reset), IRQ → $FFF9 (IrqHandler, rti)
  • Key subsystems: NMI-driven PPU command queue and VRAM transfers, main loop at $C368 with state dispatch table ($C75D), bomb/object object slots (80 slots at $03E6), sound driver ($E642–$FE2x) with dispatch table ($E6AD)
  • Build fix: One-byte mismatch at CPU $E70C (dec $E4 vs dec $54) — resolved by adding RandomStateAlt = $E4 to locals.inc alongside RandomState0 = $54 (zero-page reuse); also renamed ControllerState → ControllerState1, Main_HandleControl_C368 → MainGameLoop, Gameplay_HandleControl_C79D → GameplayUpdate, Sound_HandleControl_E65F → SoundUpdate
  • Details: Bomberman (U)/OVERVIEW.md

4. Burger Time (U)

  • Folder: Burger Time (U)/
  • ROM: Burger Time (U) [!].nes (24,592 bytes)
  • Source: copilot/prg.s (9,323 lines), copilot/header.s, copilot/chr.s
  • Linker: HEADER, PRG at $C000 (0x3FFA), VECTORS at $FFFA, CHR
  • Vectors: NMI → $C046 (NmiHandler), RESET → $C40B (ResetHandler), IRQ → $C43E (IrqHandler)
  • Key subsystems: Event-driven main loop via CommonEngineExit ($EB6A) and queued events ($EB9D), title screen / round state machine ($C5FE), object arrays ($03E6 state, $0436/$0486 position), audio engine ($F9D0–$FE2x) with music sequencer ($FD34)
  • Build fix: None needed; byte-identical. Requires symlink burger_time.nes → "../Burger Time (U) [!].nes" due to spaces in ROM filename
  • Details: Burger Time (U)/OVERVIEW.md

5. Clu Clu Land (JU)

  • Folder: Clu Clu Land (JU)/
  • ROM: rom.nes (24,592 bytes)
  • Source: copilot/main.s (9,150 lines)
  • Linker: PRG at $C000, 16 KB; single segment (no separate HEADER/CHR segments in cfg, but CHR data is embedded inline at end of prg.s)
  • Vectors: NMI → $C1F1 (NMIHandler), RESET → $C000 (ResetEntry), IRQ → $C196 (IrqHandler, reuses Reset)
  • Key subsystems: State-machine main loop via dispatch table at $C000–$C00D, NMI-driven PPU command queue + VRAM transfers, object pools ($03E6 state, $0436/$0486 position), sound driver ($E642–$F7C0) with dispatch table ($E6AD)
  • Build fix: One bpl branch to undefined label at CPU $FAB4 — replaced with bne *-$1B to produce original d0 e3 bytes (disassembler mislabeled bne as bpl)
  • Details: Clu Clu Land (JU)/OVERVIEW.md

6. Defender 2 (U)

  • Folder: Defender 2 (U)/
  • ROM: Defender 2 (U).nes (24,592 bytes)
  • Source: copilot/prg.s (11,905 lines)
  • Linker: HEADER at $0000 (16 bytes), PRG at $C000 (0x3FFA), VECTORS at $FFFA, CHR at $0000
  • Vectors: NMI → $CAA1 (NmiHandler), RESET → $C983 (ResetHandler), IRQ → $CA9E (IrqHandler, reuses Reset)
  • Key subsystems: Event-driven main loop (UpdateSideScrollingGameplay_CA6A at $CA6A) waiting on NMI flag, side-scrolling gameplay ($C073–$C324 family), PPU streaming ($F20B–$F7C0), audio engine ($E642–$F7C0) with dispatch table ($E6AD)
  • Build fix: Same bpl → bne *-$1B fix at CPU $E811 as Clu Clu Land (disassembler mislabeled bne as bpl)
  • Details: Defender 2 (U)/OVERVIEW.md

7. Excitebike (JU)

  • Folder: Excitebike (JU)/
  • ROM: rom.nes (24,592 bytes)
  • Source: copilot/prg.s (14,111 lines)
  • Linker: PRG at $C000, 16 KB; single segment
  • Vectors: NMI → $C23B (NMI), RESET → $C184 (Reset), IRQ → $FFF0 (points into audio data region — likely placeholder/unused)
  • Key subsystems: NMI-driven state machine with dispatch table at $C000–$C00D (14 states), title/menu system ($C346–$C454), gameplay update chain ($C99A), race update ($C8D6), audio engine ($F8DE) with 4 channels + SFX, serial I/O over controller port $4016
  • Build fix: Same bpl → bne *-$1B fix at CPU $FAB4 (same pattern as Clu Clu Land/Defender 2)
  • Details: Excitebike (JU)/OVERVIEW.md

8. Tennis (JU)

  • Folder: Tennis (JU)/
  • ROM: Tennis (JU).nes (24,592 bytes)
  • Source: copilot/prg.s (9,268 lines)
  • Linker: PRG at $C000, 16 KB; single segment
  • Vectors: NMI → $C07D (NmiHandler), RESET → $C000 (Reset), IRQ → $C000 (Reset reused)
  • Key subsystems: NMI-synchronized main loop (MainLoop at $C04F), game state machine via MainStateHandlerTable ($C101, 12 states), game mode state machine via GameModeStateTable ($CAD7, 8 states), object/sprite system with fixed-point physics, audio engine ($F8DE) with serial I/O over controller port
  • Build fix: None needed; byte-identical out of the box
  • Details: Tennis (JU)/OVERVIEW.md

9. Pac-Man (USA) (Namco)

  • Folder: Pac-Man (USA) (Namco)/
  • ROM: rom.nes (24,592 bytes)
  • Source: copilot/prg.s (9,268 lines)
  • Linker: PRG at $C000, 16 KB; single segment
  • Vectors: NMI → $C10E (NmiHandler), RESET → $C000 (Reset), IRQ → $C17E (IrqHandler)
  • Key subsystems: NMI-synchronized main loop (MainLoop at $C04F), game state machine via MainStateHandlerTable ($C101, 12 states), game mode state machine via GameModeStateTable ($CAD7, 8 states), object/sprite system with fixed-point physics, audio engine ($F8DE) with serial I/O over controller port
  • Build fix: None needed; byte-identical out of the box
  • Details: Pac-Man (USA) (Namco)/OVERVIEW.md

10. Donkey Kong 3 (JUE)

  • Folder: Donkey Kong 3 (JUE)/
  • ROM: Donkey Kong 3 (JUE).nes (24,592 bytes)
  • Source: copilot/prg.s (9,952 lines)
  • 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)
  • 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)
  • Details: Donkey Kong 3 (JUE)/copilot/OVERVIEW.md

11. Donkey Kong Jr. (JU)

  • Folder: Donkey Kong Jr. (JU)/
  • ROM: Donkey Kong Jr. (JU).nes (24,592 bytes)
  • Source: copilot/prg.s (12,807 lines; 6,135 instructions, 266 routines, 68 data labels, 0 conflicts)
  • 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)
  • 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
  • Details: Donkey Kong Jr. (JU)/OVERVIEW.md

12. CIRCUS

  • Folder: CIRCUS/
  • ROM: rom.nes (24,592 bytes)
  • Source: copilot/prg.s (~12,755 lines)
  • Linker: HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB)
  • Vectors: NMI → $C05A (NmiHandler), RESET → $C000 (Reset), IRQ → $4CB7 (outside mapped space — effectively unused)
  • Key subsystems: NMI-driven frame state machine — main loop is a spin loop at $C057, all per-frame work runs in NMI; main state dispatcher at $C0D0 with 16-state pointer table; controller input with edge flags at $09; menu system with text table at $C763 ("CONTINUE", "STAGE", "SELECT", "PLAY"); palette/screen transfer via descriptor streams; VRAM queue processing; variant object state machine at $DC3F–$FE7B; audio engine with sequence data at $FABB–$FFFB
  • Build fix: None needed; byte-identical out of the box (make in copilot/). Pass 1/2/3 complete — full code/data classification, semantic routine naming, and data table naming all done
  • Details: CIRCUS/OVERVIEW.md

13. Wild Gunman (U)

  • Folder: Wild Gunman (U)/
  • ROM: rom.nes (24,592 bytes)
  • Source: copilot/prg.s (~3,000 lines with routine headers)
  • Linker: HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB)
  • Vectors: NMI → $C086 (NmiHandler), RESET → $C000 (Reset), IRQ → $C000 (reuses Reset)
  • Key subsystems: NMI-driven frame loop — main loop is a spin loop waiting on NMI phase flag ($20); NMI handler does OAM DMA ($4014), PPU updates, and calls game logic subroutines ($C29F, $C233, $C3AC, $C265); main state dispatcher at $C0B3 based on GameMode ($30); reset copies sprite/OAM data from $C844 to $05F0; APU enabled via $4015 = $0F
  • Build fix: None needed; byte-identical out of the box (make in copilot/). Pass 1 complete — full code/data classification (~20% code / ~80% data); Pass 2 partial — routine headers and semantic naming for key routines (Reset, NmiHandler, MainStateDispatch, MainLoopBody, etc.); zero-page aliases in locals.inc (GameMode=$30, NmiPhase=$20, NmiFlag=$22, Timer2F=$2F, etc.)
  • Details: Wild Gunman (U)/OVERVIEW.md

14. Joypad Test Cartridge (U)

  • Folder: Joypad Test Cartridge (U)/
  • ROM: rom.nes (24,592 bytes)
  • Source: copilot/prg.s (~1,360 lines with routine headers)
  • Linker: HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB)
  • Vectors: NMI → $CCF7 (NmiHandler), RESET → $C000 (Reset), IRQ → $C000 (reuses Reset)
  • Key subsystems: NMI-driven frame loop — main loop spins waiting on NMI phase; NMI handler does OAM DMA ($4014), PPU scroll/control latch, and burst VRAM writes from table at $0302; VramPtrWrite ($CD3F) uses a return-address modification trick at RetAddrUpdate ($CDC8) for runtime-computed dispatch; VramWrite ($CDEE) sets PPU address and writes data to PPUDATA; reset clears RAM, initializes PPU ($2000=$10, $2001=$00), triggers OAM DMA, and enters main loop
  • Build fix: None needed; byte-identical out of the box (make in copilot/). Pass 1 complete — full code/data classification (~3.5% code / ~96.5% data); Pass 2 complete — routine headers for all labels, semantic naming for key routines, zero-page aliases in locals.inc (VramPtrLo=$00, PpuCtrlShadow=$EF, FrameCounterLo=$53, etc.)
  • Details: Joypad Test Cartridge (U)/OVERVIEW.md

Skeleton Projects (under _wip/)

21 additional ROMs awaiting full disassembly. Each is set up as a Pass-0 skeleton (default_project.tar.gz): copilot/ contains the split prg.bin/header.bin/chr.bin, a symbolic skeleton prg.s, and builds BYTE-IDENTICAL at the PRG level. Mapper 0 / 16 KB PRG / 8 KB CHR for all.

Project Vectors (NMI/RESET/IRQ) Mir. Status
Donkey Kong (JU) $C85F / $C79E / $FFF0 V BYTE-IDENTICAL
Donkey Kong Jr. Math (U) $C4A9 / $C41E / $FFF0 H BYTE-IDENTICAL
Duck Hunt (JUE) $C086 / $C000 / $C000 H BYTE-IDENTICAL
Golf (JU) $C0A6 / $C000 / $C0C4 H BYTE-IDENTICAL
Hogan's Alley (JU) $C088 / $C000 / $C000 H BYTE-IDENTICAL
Ice Climber (JE) $C076 / $C014 / $FFF0 V BYTE-IDENTICAL
Ice Climber (U) $C076 / $C014 / $FFF0 V BYTE-IDENTICAL
Lode Runner (U) $C02C / $C000 / $FFF9 H BYTE-IDENTICAL
Lunar Pool (U) $FF65 / $E80C / $FF64 H BYTE-IDENTICAL
MUSCLE (U) $86CB / $8000 / $8764 H BYTE-IDENTICAL
Mario Bros (JU) $C07D / $C000 / $C000 H BYTE-IDENTICAL
Millipede (U) $C7F1 / $C735 / $FFF0 H BYTE-IDENTICAL
NES PowerPad Test Card (U) $E06E / $E000 / $E000 H BYTE-IDENTICAL
Pac-Man (Tengen) (U) $C0FA / $C033 / $C16A V BYTE-IDENTICAL
Pac-Man (U) $C0FA / $FF00 / $C16A V BYTE-IDENTICAL
Pinball (JU) $C1ED / $C000 / $C1ED H BYTE-IDENTICAL
Popeye (JU) (PRG 0) $C701 / $C649 / $FFF0 H BYTE-IDENTICAL
Popeye (JU) (PRG 1) $C701 / $C649 / $FFF0 V BYTE-IDENTICAL
Pyramid (U) $D72E / $FF00 / $FF00 H Pass 1 done — classified code/data, byte-identical; see _wip/Pyramid (U)/OVERVIEW.md
Raid on Bungeling Bay (U) $C04C / $C000 / $C2C0 H BYTE-IDENTICAL
Raid on Bungeling Bay (U) [a2] $C04C / $C000 / $C2C0 V BYTE-IDENTICAL
Urban Champion (JU) $CA91 / $CA00 / $CA00 H BYTE-IDENTICAL

Notable vector/header observations for Pass 1 work:

  • Pac-Man (U) RESET at $FF00 (vector-page tail), while the Tengen variant RESETs at $C033 — different builds confirmed (PRG bytes differ).
  • MUSCLE (U) vectors sit in the $8000-EFFF window — inconsistent with the prg.s skeleton's expected $C000-$FFFF mapping; verify the header/image match before Pass 1.
  • Lunar Pool (U) NMI $FF65 / IRQ $FF64 are adjacent in the vector page; RESET $E80C.
  • NES PowerPad Test Card (U) RESET/IRQ both at $E000, far from the usual $C000-page entries.
  • Ice Climber (JE) vs (U): byte-identical PRG and header — only the CHR differs (~regional title graphic).
  • Popeye (JU) PRG 0 vs PRG 1: different PRG binaries (and mirroring); same vectors.
  • Raid on Bungeling Bay (U) vs [a2]: [a2] differs in mirroring only per header, but PRGs differ — separate builds.
  • Pyramid (U) — already at Pass 1: interesting unlicensed Sachen boot ($4100 mapper pokes, "AV" stack-byte check, two queued-event dispatchers). See its OVERVIEW.md.
  • CIRCUS — at Pass 2/3: full recursive disassembly complete. NMI-driven frame state machine (main loop is a spin loop at $C057). Menu text at $C763 ("CONTINUE", "STAGE", "SELECT", "PLAY"). Object state machine at $DC3F–$FE7B. Audio sequence data at $FABB–$FFFB. See CIRCUS/OVERVIEW.md.
  • Joypad Test Cartridge (U) — at Pass 2/3: minimal code (~3.5%), data-heavy test cartridge. NMI handler drives per-frame PPU/OAM updates. Uses return-address modification trick at RetAddrUpdate ($CDC8) with VramPtrWrite ($CD3F) for runtime-computed dispatch. See Joypad Test Cartridge (U)/OVERVIEW.md.
  • Wild Gunman (U) — at Pass 2/3: NMI-driven frame loop with sprite/OAM table at $C844. See Wild Gunman (U)/OVERVIEW.md.

Common Patterns Across All Projects

Memory Map

Every project uses a single 16 KB PRG bank mapped at CPU $C000–$FFFF, with 8 KB CHR. All builds are NROM (no bank switching).

Interrupt Structure

All projects follow the same NES interrupt pattern:

  • NMI fires once per frame and is the real-time heartbeat — handles OAM DMA, PPU updates, controller reading, and audio.
  • RESET performs cold-start hardware init, RAM clearing, and enters the main loop.
  • IRQ is almost always unused (either rti or a self-loop).

Two exceptions worth noting from branches of this family:

  • Excitebike uses a state-dispatch table at $C000 rather than a conventional inline main loop, and its IRQ vector points into a data region (unused).
  • Donkey Kong Jr. is the extreme case: its reset path idles forever in a PRNG-advance loop, and the entire frame state machine (title, attract, gameplay, sound) executes inside the NMI handler itself. Its reset entry is also at $C66E rather than $C000.

Build Toolchain

All projects use the same toolchain:

  • ca65 (6502 assembler)
  • ld65 (linker with .cfg memory map)
  • All rebuilds are verified byte-identical to the original prg.bin / ROM PRG bank.

Disassembly Style

All sources are "Pass-1" style disassemblies:

  • Every instruction annotated with PRG offset, CPU address, and emitted bytes
  • Inline .byte/.word/.res data blocks interleaved with code
  • Address-based labels (Label_Cxxxx, Sub_Cxxxx, EngineRoutine_Cxxxx) where semantics are not yet resolved
  • Zero-page symbol files (locals.inc) and hardware symbol files (symbols.inc)

Navigation

Each project folder contains:

  • copilot/ — disassembly source, build config, and build artifacts
  • OVERVIEW.md (at the project folder root) — detailed per-project power-on execution flow analysis
  • <rom>.nes — original NES ROM image (for reference)

To rebuild any project:

cd /home/tmerritt/Projects/disassembly/<Project>/copilot
make

To verify byte-identity:

make verify   # (where supported by the Makefile)

Summary

All 14 projects in this repository rebuild byte-identical to their original ROMs:

# Project Build Status
1 Balloon Fight (JU) BYTE-IDENTICAL
2 Baseball (UE) BYTE-IDENTICAL
3 Bomberman (U) BYTE-IDENTICAL
4 Burger Time (U) BYTE-IDENTICAL
5 Clu Clu Land (JU) BYTE-IDENTICAL
6 Defender 2 (U) BYTE-IDENTICAL
7 Excitebike (JU) BYTE-IDENTICAL
8 Tennis (JU) BYTE-IDENTICAL
9 Pac-Man (USA) (Namco) BYTE-IDENTICAL
10 Donkey Kong 3 (JUE) BYTE-IDENTICAL
11 Donkey Kong Jr. (JU) BYTE-IDENTICAL (Pass 2/3)
12 CIRCUS BYTE-IDENTICAL (Pass 2/3)
13 Wild Gunman (U) BYTE-IDENTICAL (Pass 2/3)
14 Joypad Test Cartridge (U) BYTE-IDENTICAL (Pass 2/3)

Every project builds successfully with make in the copilot/ folder, and every reconstructed ROM matches the original byte-for-byte. Donkey Kong Jr. shares the same byte-identical build guarantee; its remaining Pass 2/3 work is semantic renaming and table decoding, not build-correctness.

The 21 _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.