# Pac-Man (U) — Game Flow from Power-On This document describes the execution flow of the disassembled Pac-Man (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` at the PRG level. **Variant note:** `Pac-Man (Tengen) (U)` is an unrelated dump (different PRG). Both use horizontal mirroring. **Unusual RESET vector:** The RESET vector points to **$FF00** (inside the vector page padding), not into the PRG code at $C000+. The PRG at $FF00 is all `$FF` bytes (invalid opcode). The actual initialization code begins at **$C042** (`Label_C042`), reached via an unknown mechanism — possibly the dump has a corrupted vector, or the game uses a hardware reset quirk. The disassembly traces from $C042 as the effective reset entry. --- ## 1. Effective Reset Entry — $C042 (`Label_C042`) **`Label_C042`** ($C042–$C0A0) The code at $C042 performs cold-start hardware initialization: | Step | Code | Purpose | |------|------|---------| | 1–2 | `ldx #$FF` / `txs` | Initialize stack pointer to $01FF | | 3–4 | `lda #$00` / `tay` | A=0, Y=0 | | 5–9 | `Label_C048`: `sta $0000,y` / `iny` / `cpy #$3E` / `bne` | Clear $0000–$003D (62 bytes) | | 10–17 | `ldx #$08` / `ldy #$87` / `Label_C054`: `sta (Zp00),y` / `iny` / `bne` / `inc Zp01` / `cpx Zp01` / `bne` | Clear 9 pages from $8700 ($8700–$8F00) via Zp00/Zp01 | | 18–19 | `lda #$06` / `sta $2001` | PPUMASK = $06 (show BG, hide sprites) | | 20–23 | `lda #$00` / `sta $2005` ×2 | PPUSCROLL = 0,0 | | 24–25 | `sta Zp41` / `sta Zp42` | Zp41=Zp42=0 (scroll shadows) | | 26–27 | `sta $2000` / `sta $2001` | PPUCTRL=0, PPUMASK=0 | | 28 | `tay` | Y=0 | | 29–37 | `Label_C077`: `lda $C0EB,y` / `cmp $0052,y` / `bne Label_C086` / `iny` / `cpy #$0F` / `bne` / `beq Label_C0A0` | Compare 15-byte copyright string at $C0EB with RAM at $0052; if match, jump to $C0A0 | | 38–41 | `Label_C086`: `lda #$00` / `tay` / `Label_C089`: `sta $0000,y` / `iny` / `bne` / `ldy #$00` | If mismatch: clear $0000–$00FF, Y=0 | **Copyright check:** 15-byte string "COPYRIGHT 1984 NAMCO LTD. ALL RIGHTS RESERVED" at $C0EB compared against RAM at $0052. On cold start, RAM is garbage so check fails, clearing $0000–$00FF. On warm reset, if signature persists, skips extra clear. **Note:** The code from $C042 onward is **identical** to `Pac-Man (Tengen) (U)` from its `Label_C03D` ($C03D) onward. The only difference is the entry point ($C033 vs $C042) and the initial `sei`/`cld`/`PPU off` which are missing here (possibly executed before jumping to $C042). --- ## 2. Interrupt Vectors — CPU $FFFA–$FFFF Located at PRG offset $3FFA (end of 16 KB bank): | Vector | Address | Handler | |--------|---------|---------| | NMI | $C0FA | `NmiHandler` | | RESET | $FF00 | **Points into vector padding ($FF bytes)** — likely corrupted dump | | IRQ | $C16A | `IrqHandler` (single `rti` — unused placeholder) | **Actual reset entry:** $C042 (`Label_C042`), traced as effective cold start. --- ## 3. NMI / Frame Heartbeat — `NmiHandler` ($C0FA) Identical to `Pac-Man (Tengen) (U)` — see that document for full details. Summary: ``` NmiHandler ($C0FA): pha / txa / pha / tya / pha lda #$1E / sta $2001 lda #$00 / sta $2003 / sta Zp40 lda Zp45 / sta $4014 (OAMDMA) jsr $DE3D (sprites) / jsr $E261 (game logic) / jsr $C16B (palette) lda Zp41/Zp42 → $2005 (scroll) lda Zp47/Zp46/Zp43 → $2000 (PPUCTRL) Controller strobe + 8-bit read ($4016/$4017) → Zp4D/Zp4E Select active input → Zp4F inc Zp4B (frame counter) Zp48=0 → jsr $EE8F (attract) pla / tay / pla / tax / pla / rti ``` --- ## 4. Key Subroutines Identical to `Pac-Man (Tengen) (U)`: - `$DE3D` — Sprite/OAM update - `$E261` — Main game logic - `$C16B` — Palette/PPU update (uploads 16 bytes from $0299 when $0299=$0F) - `$EE8F` — Attract mode logic - Controller reading: 8-bit shift from $4016/$4017 → Zp4D/Zp4E → Zp4F --- ## 5. Indirect Dispatch Same as Tengen variant: - `jmp ($0010)` at $C250, $D90F, $D9B6, $D3CA - `jmp ($00FE)` at $EFDA Not followed statically. --- ## 6. Memory Map (Key Zero Page / RAM) | Address | Name | Use | |---------|------|-----| | $40 | `Zp40` | Cleared each NMI | | $41–$42 | `Zp41`/`Zp42` | Scroll X/Y shadows | | $43 | `Zp43` | PPUCTRL flag | | $45 | `Zp45` | OAM DMA source page | | $46–$47 | `Zp46`/`Zp47` | Game state flags | | $48 | `Zp48` | Attract mode flag | | $4B | `Zp4B` | Frame counter | | $4D–$4E | `Zp4D`/`Zp4E` | Player 1/2 button shift registers | | $4F | `Zp4F` | Active input | | $0200 | OAM buffer | Sprite DMA source | | $0299 | Palette buffer | 16 bytes for $3F00 upload | | $0052 | Copyright check | 15-byte RAM signature | --- ## 7. Data Tables | Table | Address | Description | |-------|---------|-------------| | Copyright string | $C0EB | 15 bytes "COPYRIGHT 1984 NAMCO LTD. ALL RIGHTS RESERVED" | | Palette upload | $0299 | 16 bytes, uploaded when $0299=$0F | --- ## 8. Summary of Execution Flow ``` Power On / Reset │ ▼ (Vector points to $FF00 — $FF padding) │ ▼ (Unknown mechanism reaches $C042) Label_C042 ($C042) ────► stack=$FF │ ├─ Clear $0000–$003D (62 bytes) ├─ Clear $8700–$8F00 (9 pages via Zp00/Zp01) ├─ PPUMASK=$06, PPUSCROLL=0,0 ├─ Zp41=Zp42=0 ├─ PPUCTRL=0, PPUMASK=0 ├─ Copyright check: $C0EB vs $0052 (15 bytes) │ ├─ Match: jump to Label_C0A0 │ └─ Mismatch: clear $0000–$00FF │ ▼ Main Initialization (Label_C0A0+) │ ▼ Main Loop (VBlank synchronized) │ ▼ (NMI fires once per frame) NMI ($C0FA) [identical to Tengen variant] ├─ Preserve A,X,Y ├─ PPUMASK=$1E, OAMADDR=0, Zp40=0 ├─ OAM DMA from Zp45 page ├─ jsr $DE3D (sprites), $E261 (game logic), $C16B (palette) ├─ Scroll from Zp41/Zp42 ├─ PPUCTRL from Zp43/Zp46/Zp47 ├─ Controller strobe + 8-bit read ($4016/$4017) → Zp4D/Zp4E ├─ Select active input → Zp4F ├─ inc Zp4B (frame counter) ├─ Zp48=0 → jsr $EE8F (attract) └─ Restore, rti ``` --- ## 9. Naming and Residue **Pass 1 complete** — 4,506 lines, 194 `Label_` definitions, canonical audit comments, 19.6% code. 16 seeded routine entries from table references (same as Tengen variant). **Pass 2 outstanding** — No semantic names applied; all 194 labels remain address-derived `Label_XXXX`. **Pass 3 outstanding** — No semantic zero-page aliases; `locals.inc` holds neutral `ZpXX` names. **Indirect dispatch** (not followed statically): - `jmp ($0010)` at $C250, $D90F, $D9B6, $D3CA - `jmp ($00FE)` at $EFDA **Unusual RESET vector** — Points to $FF00 (vector page padding). The disassembly traces from $C042 as the effective entry. This may indicate a bad dump or a hardware quirk. **Promotion outstanding** — Add `header.s`/`chr.s` + HEADER/PRG/CHR `linker.cfg`, switch `make` to whole-ROM `cmp`, move `_tools` paths from `../../../_tools/` to `../../_tools/`. Build verification: `make` in `copilot/` produces `reconstructed_prg.bin`, compared against `prg.bin` (PRG byte-identical).