Files
disasm/Wild Gunman (U)/OVERVIEW.md
T
tmerritt c79a52e354 Promote Wild Gunman (U) from _wip to top-level project
Moves Wild Gunman (U) from _wip/ to Wild Gunman (U)/ with full ROM build:
- Adds header.s, chr.s, linker.cfg (HEADER+PRG+CHR segments)
- Updates Makefile to build and verify reconstructed.nes vs rom.nes
- Adds rom.nes copy + copilot/rom.nes symlink per repo convention
- Verified byte-identical build

Pass 1 complete: full code/data classification (~20% code / ~80% data)
Pass 2 partial: routine headers for all labels, semantic naming for key
routines (Reset, NmiHandler, MainStateDispatch, MainLoopBody, etc.),
zero-page aliases in locals.inc

Updates root OVERVIEW.md:
- Removes Wild Gunman from _wip skeleton table (23 -> 22 skeletons)
- Adds Wild Gunman as #13 in numbered inventory (BYTE-IDENTICAL, Pass 2)
- Updates summary count from 12 to 13 projects
2026-10-16 18:51:15 -04:00

159 lines
6.4 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_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.