Files
disasm/Wild Gunman (U)/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

6.5 KiB
Raw Blame History

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:

  1. CLD — clear decimal mode; SEI — disable maskable interrupts.
  2. Wait for PPU warm-up vblank — poll $2002 bit 7 until clear (bpl loop at $C002–$C005).
  3. STX $2000 / STX $2001 — disable PPU rendering and NMI with X=0.
  4. DEX / TXS — initialize stack pointer to $FF.
  5. LDX $05EC — read OAM base value; LDY #$06 / STY $01 — set up a 6-page counter.
  6. Clear RAM — zero pages $0000–$05FF via indirect store loop (STA (Zp00),Y with page increment).
  7. LDX #$5F / STX $05EC — set OAM base to $5F.
  8. JSR ResetInitSub1 ($C20B) and JSR ResetInitSub2 ($C21B) — initialization subroutines.
  9. Set scroll to 0 (STA $2005 twice with Y=0).
  10. LDY #$01 / STY $30 — set GameMode = 1.
  11. LDA #$0F / STA $4015 — enable all APU channels.
  12. JSR $FFF7 — jump via vector to sound init at $F40F.
  13. LDA #$90 / STA $2000 — set PPUCTRL = $90 (NMI enabled, $2000 base).
  14. STA Zp08 ($08 = $90); LDA #$02 / STA Zp09 ($09 = $02).
  15. Copy 12 bytes from $C844 to $05F0 (sprite/OAM data table).
  16. Main loop (Label_C07C at $C07C): check NmiPhase ($20); if nonzero, jump to $C061; otherwise call MainLoopBody ($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:

  1. Save registers (PHP / PHA / TXA / PHA / TYA / PHA).
  2. Set OAMADDR to 0 (STA $2003), trigger OAM DMA (STA $4014 with value 2 → $0200–$02FF).
  3. Check NmiFlag ($22) — if nonzero, skip to $C0A6 (restore registers, RTI).
  4. Game logic — call NmiGameLogic3 ($C29F), NmiGameLogic1 ($C233), NmiGameLogic5 ($C3AC), NmiGameLogic2 ($C265).
  5. LDY #$01 / STY NmiFlag and STY NmiPhase — set both flags to 1.
  6. 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 (Reset at $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 .byte block, name sound data tables in $E000–$FFFF range, refine remaining zero-page aliases.