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

159 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```bash
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.