Files
disasm/_wip/Pac-Man (Tengen) (U)/OVERVIEW.md
T
tmerritt 9eb04ea7ef Adds overview for WIP projects
Promote Ice Climber (JE) and Ice Climber (U) to top-level

- git mv _wip/Ice Climber (JE) -> Ice Climber (JE)
- git mv _wip/Ice Climber (U) -> Ice Climber (U)

- Added header.s / chr.s (one-line .incbin each)
- Added HEADER/PRG/CHR linker.cfg with proper memory layout
- Added copilot/rom.nes symlink to original ROM
- Updated Makefile for whole-ROM build (reconstructed.nes vs rom.nes)
- Updated _tools paths from ../../../_tools/ to ../../_tools/

Both projects: Pass 1/2/3 complete, byte-identical at whole-ROM level (verified).

Root OVERVIEW.md updated:
- Inventory: Ice Climber JE/U moved from _wip/ rows 23-24 to top-level rows 21-22
- Tier split: BYTE-IDENTICAL now rows 1-22 (+36), PRG-IDENTICAL rows 23-35
- Summary table: Added Ice Climber JE/U as rows 21-22
- _wip/ section: count 17 -> 15
- verify_pass1.py scope: 19 -> 22 top-level projects

All 22 top-level projects verified byte-identical.

Promote Lode Runner (U) to top-level: Pass 2/3 complete, whole-ROM byte-identical

- git mv _wip/Lode Runner (U) -> Lode Runner (U)

- Added header.s / chr.s (one-line .incbin each)
- Added HEADER/PRG/CHR linker.cfg with proper memory layout
- Added copilot/rom.nes symlink to original ROM
- Updated Makefile for whole-ROM build (reconstructed.nes vs rom.nes)

Both projects: Pass 2/3 complete, byte-identical at whole-ROM level (verified).

Root OVERVIEW.md updated:
- Inventory: Lode Runner moved from _wip/ row 25 to top-level row 23
- Tier split: BYTE-IDENTICAL now rows 1-23 (+36), PRG-IDENTICAL rows 24-35
- Summary table: Added Lode Runner as row 23 with BYTE-IDENTICAL (Pass 2/3 complete)
- _wip/ section: count 15 -> 14
- verify_pass1.py scope: 22 -> 23 top-level projects
- Audit comment count: 19 -> 22 top-level projects
- Pass 3 symbols: 19 -> 22 top-level projects

All 23 top-level projects verified byte-identical.

TODO.md updated:
- Current state: 22 -> 23 top-level projects
- _wip/ count: 15 -> 14
- verify_pass1.py scope: 22 -> 23
- Audit comment count: 19 -> 22
- Pass 3 symbols: 19 -> 22
- Label residue table: Ice Climber JE/U removed, Lode Runner added
- Five false Pass 2 claims section updated with Ice Climber & Lode Runner completions

Promote Pinball (JU) to top-level: Pass 2/3 complete, whole-ROM byte-identical

- git mv _wip/Pinball (JU) -> Pinball (JU)

- Added header.s / chr.s (one-line .incbin each)
- Added HEADER/PRG/CHR linker.cfg with proper memory layout
- Added copilot/rom.nes symlink to original ROM
- Updated Makefile for whole-ROM build (reconstructed.nes vs rom.nes)

Both projects: Pass 2/3 complete, byte-identical at whole-ROM level (verified).

Root OVERVIEW.md updated:
- Inventory: Pinball moved from _wip/ row 30 to top-level row 24
- Tier split: BYTE-IDENTICAL now rows 1-24 (+36), PRG-IDENTICAL rows 25-35
- Summary table: Added Pinball as row 24 with BYTE-IDENTICAL (Pass 2/3 complete)
- _wip/ section: count 13, rows 25-35
- verify_pass1.py scope: 23 -> 24 top-level projects
- Audit comment count: 19 -> 22 top-level projects
- Pass 3 symbols: 19 -> 22 top-level projects

All 24 top-level projects verified byte-identical.

TODO.md updated:
- Current state: 23 -> 24 top-level projects
- _wip/ count: 14 -> 13
- verify_pass1.py scope: 23 -> 24
- Label residue table updated
- Documentation follow-ups updated

Promote Millipede (U) and Popeye (JU) (PRG 0) to top-level

Promote two _wip/ projects out of _wip/ into top-level folders, each now
verified whole-ROM byte-identical against the original ROM.

- Millipede (U): moves all copilot files up, adds header.s, chr.s,
  linker.cfg with PRG+HEADER+CHR areas, rom.nes copy, and switches the
  Makefile from PRG-only cmp to whole-ROM cmp. Makefile _tools depth
  adjusted (../../../../_tools/ -> ../../_tools/).
- Popeye (JU) (PRG 0): same promotion treatment.
- AGENTS.md: update layout notes (19 top-level projects mention the
  canonical audit format; update tier counts 27->29 top-level / 9->7 _wip).
- OVERVIEW.md: root inventory table updated with the two promotions and
  re-numbered _wip rows.
- TODO.md: handoff updates.
- Remaining _wip projects: assorted pass 2/3 edits (prg.s, locals.inc,
  reconstructed.lbl, reconstructed_prg.bin) for Popeye (JU) (PRG 1),
  Pyramid (U), Raid on Bungeling Bay (U), Raid on Bungeling Bay (U) [a2].
2026-10-19 19:58:41 -04:00

222 lines
8.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.
# Pac-Man (Tengen) (U) — Game Flow from Power-On
This document describes the execution flow of the disassembled Pac-Man (Tengen) (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 (U)` is an unrelated dump (different PRG). Both use horizontal mirroring.
---
## 1. Reset Vector — CPU $FFFC → $C033
**`Reset`** ($C033–$C0A0)
On power-on or reset, the 6502 jumps to **$C033** (non-$C000 entry). The reset routine performs cold-start hardware initialization:
| Step | Code | Purpose |
|------|------|---------|
| 1–2 | `sei` / `cld` | Disable interrupts, clear decimal mode |
| 3–4 | `lda #$00` / `sta $2000` / `sta $2001` | PPUCTRL=0, PPUMASK=0 (disable NMI, rendering) |
| 5–8 | `Label_C03D`: poll `$2002` via `bpl *-3` | Wait for VBlank |
| 9–10 | `ldx #$FF` / `txs` | Initialize stack pointer to $01FF |
| 11–12 | `lda #$00` / `tay` | A=0, Y=0 |
| 13–17 | `Label_C048`: `sta $0000,y` / `iny` / `cpy #$3E` / `bne` | Clear $0000–$003D (62 bytes) |
| 18–25 | `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 |
| 26–27 | `lda #$06` / `sta $2001` | PPUMASK = $06 (show BG, hide sprites) |
| 28–31 | `lda #$00` / `sta $2005` ×2 | PPUSCROLL = 0,0 |
| 32–33 | `sta Zp41` / `sta Zp42` | Zp41=Zp42=0 (scroll shadows) |
| 34–35 | `sta $2000` / `sta $2001` | PPUCTRL=0, PPUMASK=0 |
| 36 | `tay` | Y=0 |
| 37–45 | `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 |
| 46–49 | `Label_C086`: `lda #$00` / `tay` / `Label_C089`: `sta $0000,y` / `iny` / `bne` / `ldy #$00` | If mismatch: clear $0000–$00FF, Y=0 |
| 50+ | `Label_C0A0`... | Continue initialization (title screen, game setup) |
**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.
---
## 2. Interrupt Vectors — CPU $FFFA–$FFFF
Located at PRG offset $3FFA (end of 16 KB bank):
| Vector | Address | Handler |
|--------|---------|---------|
| NMI | $C0FA | `NmiHandler` |
| RESET | $C033 | `Reset` |
| IRQ | $C16A | `IrqHandler` (single `rti` — unused placeholder) |
---
## 3. NMI / Frame Heartbeat — `NmiHandler` ($C0FA)
The NMI fires once per frame and handles PPU updates, controller input, and audio:
```
NmiHandler ($C0FA):
pha / txa / pha / tya / pha ; preserve A,X,Y
lda #$1E / sta $2001 ; PPUMASK = $1E (BG+sprites on, left 8px)
lda #$00 / sta $2003 (OAMADDR)
sta Zp40 ($40) ; clear Zp40
lda Zp45 ($45) / sta $4014 (OAMDMA) ; sprite DMA from Zp45 page
jsr $DE3D ; sprite/OAM update
jsr $E261 ; game logic update
jsr $C16B ; palette/PPU update
lda Zp41 / sta $2005 ; PPUSCROLL X
lda Zp42 / sta $2005 ; PPUSCROLL Y
lda Zp47 / and Zp46 / and #$01 ; check game state flags
asl a / ora Zp43 ; combine flags
sta $2000 ; PPUCTRL update
lda #$01 / sta $4016 ; controller strobe on
lda #$00 / sta $4016 ; controller strobe off
ldx #$08
Label_C13B:
lda $4016 / and #$01 / cmp #$01 / ror Zp4D ; read player 1 buttons → Zp4D
lda $4017 / and #$01 / cmp #$01 / ror Zp4E ; read player 2 buttons → Zp4E
dex / bne Label_C13B ; 8 bits each
ldx Zp4D
lda Zp46 / and Zp47
beq Label_C15A
ldx Zp4E ; if Zp46&Zp47: use player 2 input
Label_C15A:
stx Zp4F ; store combined input
inc Zp4B ; frame counter
lda Zp48
bne Label_C165
jsr $EE8F ; attract mode logic
Label_C165:
pla / tay / pla / tax / pla / rti ; restore, return (IRQ handler follows)
```
**Key ZP Flags:**
- `Zp40` ($40): Cleared each NMI
- `Zp41`/$42 ($41/$42): Scroll X/Y shadows
- `Zp43` ($43): PPUCTRL flag (bit 0)
- `Zp45` ($45): OAM DMA source page
- `Zp46`/$47 ($46/$47): Game state flags
- `Zp4D`/$4E ($4D/$4E): Player 1/2 button state (shifted in)
- `Zp4F` ($4F): Active player input
- `Zp4B` ($4B): Frame counter
---
## 4. Key Subroutines
### 4.1 `$DE3D` — Sprite/OAM Update
Builds OAM at $0200 from object state.
### 4.2 `$E261` — Main Game Logic
Pac-Man movement, ghost AI, collision, dots, power pellets.
### 4.3 `$C16B` — Palette/PPU Update
```
Label_C16B:
lda $0299 / cmp #$0F / bne Label_C18C
lda $2002 / lda #$3F / sta $2006 / lda #$00 / sta $2006
ldy #$00
Label_C181:
lda $0299,y / sta $2007 / iny / cpy #$10 / bne Label_C181
```
When $0299=$0F, uploads 16 bytes from $0299 to PPU palette ($3F00).
### 4.4 `$EE8F` — Attract Mode
Called when Zp48=0 (title screen).
### 4.5 Controller Reading
8-bit shift register read from $4016 (player 1) and $4017 (player 2), results in Zp4D/Zp4E, combined to Zp4F based on Zp46/Zp47.
---
## 5. Indirect Dispatch
Multiple `jmp ($0010)` at $C250, $D90F, $D9B6, $D3CA — target in ZP $0010/$0011 set by caller.
One `jmp ($00FE)` at $EFDA — target in ZP $00FE/$00FF.
Not followed statically; requires runtime tracing.
---
## 6. Memory Map (Key Zero Page / RAM)
| Address | Name | Use |
|---------|------|-----|
| $10 | `Zp10` | General purpose |
| $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 |
| Ghost AI / level data | $C000+ | Referenced by indirect dispatch |
---
## 8. Summary of Execution Flow
```
Power On / Reset
│
▼
Reset ($C033) ────► sei/cld, PPU off, wait VBlank, 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)
├─ 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,482 lines, 192 `Label_` definitions, canonical audit comments, 19.5% code. 16 seeded routine entries from table references.
**Pass 2 outstanding** — No semantic names applied; all 192 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
**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).