# 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_prg.bin` (16,384 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 | Partial | Routine headers added for all labeled positions; key routines semantically named (`Reset`, `NmiHandler`, `MainStateDispatch`, `MainLoopBody`, etc.); zero-page aliases in `locals.inc` | | Pass 3 | Partial | Some data tables referenced by name in code; deeper table naming is future work | --- ## 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 ```bash cd "_wip/Wild Gunman (U)/copilot" make # → ca65 + ld65 + cmp reconstructed_prg.bin prg.bin → "BYTE-IDENTICAL" ``` --- ## 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** — move to a top-level `Wild Gunman (U)/` folder, add `header.s` / `chr.s` + full-ROM `make` target (as e.g. `Burger Time (U)`), then join the root `OVERVIEW.md` numbered inventory. - **Pass 2/3 continuation** — semantically rename remaining `Label_Cxxxx` routines, name data tables (sprite data at $C844, sound tables in $E000–$FFFF range), and refine zero-page aliases.