- 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.
6.5 KiB
Wild Gunman (U) — Game Flow from Power-On
This document describes the execution flow of the disassembled Wild Gunman (U) PRG bank (16 KB, mapped at CPU $C000–$FFFF), starting from power-on. It is based entirely on copilot/prg.s in the repository. The rebuild is verified byte-identical to the original prg.bin.
1. ROM and Memory Layout
| Property | Value |
|---|---|
| Format | iNES |
| PRG | 1 × 16 KB (mapped at CPU $C000–$FFFF) |
| CHR | 1 × 8 KB |
| Mirroring | Horizontal |
| PRG bank start | CPU $C000 |
| Source file | copilot/prg.s (~3,000 lines with headers) |
| Output | reconstructed.nes (24,592 bytes) |
Interrupt vectors (CPU $FFFA–$FFFF)
| Vector | Address | Handler |
|---|---|---|
| NMI | $C086 | NmiHandler |
| RESET | $C000 | Reset |
| IRQ / BRK | $C000 | Reuses Reset |
2. Build Status
make in copilot/ assembles prg.s with ca65, links with ld65, and verifies against prg.bin.
Verification result: BYTE-IDENTICAL — reconstructed_prg.bin matches prg.bin exactly (all 16,384 bytes), and prg.bin matches the PRG bank of Wild Gunman (U) [!].nes.
3. Pass Status
| Pass | Status | Description |
|---|---|---|
| Pass 1 | Complete | Code/data split — entire PRG bank classified as traced code (~20%) or explicit .byte data (~80%) with ; CPU:$xxxx PRG:$xxxx annotations |
| Pass 2 | Complete | Routine headers added for all labeled positions; key routines semantically named (Reset, NmiHandler, MainStateDispatch, MainLoopBody, etc.); zero-page aliases in locals.inc |
| Pass 3 | Complete | Data tables/structures identified and named (SpriteOamTable at $C844, VramWriteParameterTable at $C09E); remaining Label_Cxxxx references are internal branch targets within routines |
4. Reset — CPU $FFFC → $C000
Reset ($C000)
On power-on or reset:
CLD— clear decimal mode;SEI— disable maskable interrupts.- Wait for PPU warm-up vblank — poll
$2002bit 7 until clear (bplloop at $C002–$C005). STX $2000 / STX $2001— disable PPU rendering and NMI with X=0.DEX / TXS— initialize stack pointer to $FF.LDX $05EC— read OAM base value;LDY #$06 / STY $01— set up a 6-page counter.- Clear RAM — zero pages $0000–$05FF via indirect store loop (
STA (Zp00),Ywith page increment). LDX #$5F / STX $05EC— set OAM base to $5F.JSR ResetInitSub1($C20B) andJSR ResetInitSub2($C21B) — initialization subroutines.- Set scroll to 0 (
STA $2005twice with Y=0). LDY #$01 / STY $30— setGameMode= 1.LDA #$0F / STA $4015— enable all APU channels.JSR $FFF7— jump via vector to sound init at $F40F.LDA #$90 / STA $2000— set PPUCTRL = $90 (NMI enabled, $2000 base).STA Zp08($08 = $90);LDA #$02 / STA Zp09($09 = $02).- Copy 12 bytes from $C844 to $05F0 (sprite/OAM data table).
- Main loop (
Label_C07Cat $C07C): checkNmiPhase($20); if nonzero, jump to $C061; otherwise callMainLoopBody($C573) and loop forever.
Key point: Like several other games in this repo, the main loop is essentially driven by NMI. The Reset code sets up hardware and enters a loop that waits for NMI phase changes.
5. NMI / Frame Heartbeat — NmiHandler ($C086)
The NMI fires once per frame:
- Save registers (
PHP / PHA / TXA / PHA / TYA / PHA). - Set OAMADDR to 0 (
STA $2003), trigger OAM DMA (STA $4014with value 2 → $0200–$02FF). - Check
NmiFlag($22) — if nonzero, skip to $C0A6 (restore registers, RTI). - Game logic — call
NmiGameLogic3($C29F),NmiGameLogic1($C233),NmiGameLogic5($C3AC),NmiGameLogic2($C265). LDY #$01 / STY NmiFlagandSTY NmiPhase— set both flags to 1.- Restore registers (
PLA / TAY / PLA / TAX / PLA / PLP / RTI).
6. Main State Dispatcher — MainStateDispatch ($C0B3)
Checks GameMode ($30):
- If nonzero: branch to $C130 (other game mode handler).
- If zero: check
Ctrl1($16) bit 4 (AND #$10); if zero, go to $C10A. - Otherwise, read
OAMBase($05FC) into Y; if nonzero, go to $C10F. - Continue with startup sequence.
7. Key Subroutines
| Label | Address | Description |
|---|---|---|
Reset |
$C000 | Cold-start initialization and main loop entry |
NmiHandler |
$C086 | Per-frame NMI: OAM DMA, game logic, register restore |
MainStateDispatch |
$C0B3 | Game mode dispatch based on $30 |
ResetInitSub1 |
$C20B | Reset initialization subroutine |
ResetInitSub2 |
$C21B | Reset initialization subroutine |
NmiGameLogic1 |
$C233 | NMI game logic (called from NMI) |
NmiGameLogic2 |
$C265 | NMI game logic (called from NMI) |
NmiGameLogic3 |
$C29F | NMI game logic (called from NMI) |
NmiGameLogic4 |
$C32B | NMI/gameplay logic |
NmiGameLogic5 |
$C3AC | NMI/gameplay logic |
MainLoopBody |
$C573 | Main loop body, called each frame |
Sub_C5D1 |
$C5D1 | Subroutine called from 5 call sites |
8. Zero-Page Aliases (locals.inc)
| Alias | Address | Role |
|---|---|---|
GameMode |
$30 | Current game mode/state |
NmiPhase |
$20 | NMI phase flag |
NmiFlag |
$22 | NMI flag |
Timer2F |
$2F | Frame timer |
Ctrl1 |
$16 | Controller 1 state |
Mode38 |
$38 | Mode byte |
Zp00 |
$00 | Indirect pointer lo (RAM clear) |
Zp01 |
$01 | Indirect pointer hi (RAM clear) |
Zp08 |
$08 | PPUCTRL value ($90) |
Zp09 |
$09 | Value $02 |
Full list in copilot/locals.inc.
9. Build
cd "Wild Gunman (U)/copilot"
make # → ca65 + ld65 + cmp reconstructed.nes rom.nes → "Byte-identical ROM verified."
10. Notes
- The PRG is approximately 20% code / 80% data. The large data portions include sprite/OAM tables, sound data, and game state tables.
- The NMI handler drives the per-frame game loop; the main loop (
Resetat $C07C) spins waiting for NMI phase changes. GameMode($30) selects between different game states/modes.- The vectors at $FFF4 and $FFF7 are indirect jump targets to sound routines at $F372 and $F40F respectively.
- IRQ vector points to $C000 (Reset) and is effectively unused.
11. Next Steps
- Promotion complete — moved to top-level
Wild Gunman (U)/folder with full ROM build (header.s,chr.s,linker.cfg,Makefile). - Optional deeper Pass 3 work — extract sprite table at $C844 into its own labeled
.byteblock, name sound data tables in $E000–$FFFF range, refine remaining zero-page aliases.