Pass 3 for NES PowerPad Test Card (U): name all data tables and VramWrite streams

- Decoded the VramWrite ($E1CC) stream format: <addr hi> <addr lo> <count> <bytes>, $00 terminator
- Named all 9 payload streams ($E4CC-$E7C2, no gaps/overlaps) plus VramWriteSourcePointerTable at $F000
- Split PadBitTableLives into Player1TileTable ($E4B4) and Player2TileTable ($E4C0), 12 entries each (Y=0..11)
- Decoded CHR font (digits $30-$39, letters $3A-$52, space $5F; 'W' missing from the A-Z run)
  and recovered on-screen text including 'PRESS A TO TEST SIDE-A' / 'SIDE-B'
- Documented that the 16 KiB PRG is two byte-identical 8 KiB halves; $C000 copy is dead
- Documented 7 unreached subroutines inside the live code region (kept as .byte per Pass-1 policy)
- Fixed a stray duplicate sentence and stale _wip paths in root OVERVIEW.md

Byte-identical ROM verified.
This commit is contained in:
tmerritt committed 2026-10-16 20:24:23 -04:00
1 parent 3ef16a6f99
commit 4c644d2a20
23 files changed
+17290 -507

No files matched your search

+48 -14
View File
@@ -40,7 +40,7 @@ This document describes the execution flow of the disassembled NES PowerPad Test
|------|--------|-------------|
| Pass 1 | Complete | Code/data split — entire PRG bank classified as traced code (~7.2%) or explicit `.byte` data (~92.8%) with `; CPU:$xxxx PRG:$xxxx` annotations |
| Pass 2 | Complete | Routine headers added for all labeled positions; key routines semantically named (`Reset`, `NmiHandler`, `VramWrite`, `ReadPad`, `ProcessPads`, etc.); zero-page aliases in `locals.inc` |
| Pass 3 | Complete | Data tables/structures identified and named (`PaletteData` at $E121, `PadBitTableLives` at $E4B4); remaining `Label_Exxxx` references are internal branch targets within routines |
| Pass 3 | Complete | All data tables named: `VramWriteSourcePointerTable` ($F000, 9 streams), `Player1TileTable`/`Player2TileTable` ($E4B4/$E4C0), `PaletteData` ($E121), and all nine VramWrite payload streams (`PowerPadSideAButtonGraphics`, `PanelFrameTiles`, `StatusMessageText`, `SideATestPromptText`, …). Screen text decoded from CHR. Remaining 46 `Label_Exxxx` are internal loop/branch targets. |
---
@@ -155,33 +155,67 @@ Full list in `copilot/locals.inc`.
## 8. Data Tables
| Table | Address | Description |
|-------|---------|-------------|
| `PaletteData` | $E121 | 32-byte palette data loaded into PPU palette RAM |
| `PadBitTableLives` | $E4B4 | 70-byte pad bit lookup table; `DrawScore` reads from offset $0C ($E4C0) |
| Pattern pointers | $F000–$F013 | Pointer pairs loaded by `LoadPatternTable1`–`5` routines |
### 8.1 Direct tables
| Table | Address | Size | Description |
|-------|---------|------|-------------|
| `PaletteData` | $E121 | 32 | Palette loaded into PPU $3F00-$3F1F by `LoadPalette` |
| `Player1TileTable` | $E4B4 | 12 | PPUADDR low bytes for player 1, indexed Y=0..11 |
| `Player2TileTable` | $E4C0 | 12 | Same for player 2; `$00` = "skip this button" |
| `VramWriteSourcePointerTable` | $F000 | 18 | 9 little-endian stream pointers, one per `LoadPatternTable*` / draw call site |
### 8.2 VramWrite payload streams
`VramWrite` ($E1CC) reads records of `<addr hi> <addr lo> <count> <count bytes>`, terminated by a `$00` at a record boundary. All nine streams tile `$E4CC-$E7C2` with no gaps and no overlaps:
| Slot | Ptr | Stream label | Address | Records | Writes to |
|------|-----|--------------|---------|---------|-----------|
| 0 | $F000 | `PowerPadSideAButtonGraphics` | $E54F | 2 | $2083, $20A3 — 52 tile graphics in pattern table 0 |
| 1 | $F002 | `PowerPadButtonLabelTiles` | $E58A | 4 | $2136, $2156, $2236, $2256 — button captions |
| 2 | $F004 | `PanelFrameTiles` | $E5B3 | 8 | $2124…$22C4 — panel borders for six indicator boxes |
| 3 | $F006 | `SideAPressedButtonRow` | $E64C | 6 | $2184…$22A4 — side-A button caps |
| 4 | $F008 | `SideAPanelButtonRow` | $E6BF | 6 | same rows, alternate layout |
| 5 | $F00A | `StatusMessageText` | $E732 | 3 | $2121, $2163, $2183 — status messages |
| 6 | $F00C | `Player1ButtonIconStream` | $E4CC | 2 | $2196, $21B6 |
| 7 | $F00E | `Player2ButtonIconStream` | $E4E3 | 2 | $2296, $22B6 |
| 8 | $F010 | `SideATestPromptText` | $E790 | 2 | $2304, $2344 — the prompt rows |
### 8.3 Screen text (decoded from CHR)
Font mapping read directly out of `chr.bin` pattern table 1: digits `$30-$39` = `'0'-'9'`, letters `$3A-$52` = `'A'-'Z'` (note **`W` is absent** from the `A-Z` run), `$57` = `-`, `$58` = `/`, `$59` = `\`, `$5F` = space.
| Address | Text |
|---------|------|
| $2121 | `CONNECT POWER PAD <$5D> CONTROLLER` |
| $2163 | `<$60>IF POWER PAD IS CONNECTED<$62>` |
| $2183 | `<$61> IC MAY BE BAD <$53> <$63>` |
| $2304 | `PRESS A TO TEST SIDE-A` |
| $2344 | `PRESS B TO TEST SIDE-B` |
Tiles `$53-$5E` are truncated glyph fragments in CHR ROM — including the `W` of `POWER`, which sits at `$5E` and renders incomplete. The original cartridge therefore displays partial marks at those positions; this is a ROM authoring artifact, reproduced faithfully.
---
## 9. Build
```bash
cd "_wip/NES PowerPad Test Card (U)/copilot"
make # → ca65 + ld65 + cmp reconstructed_prg.bin prg.bin → "BYTE-IDENTICAL"
cd "NES PowerPad Test Card (U)/copilot"
make # → ca65 + ld65 + cmp reconstructed.nes rom.nes → "Byte-identical ROM verified."
```
---
## 10. Notes
- The PRG is approximately 7% code / 93% data. Most of the PRG bank ($C000–$DFFF, $E54F–$FFFF) is $FF-filled test pattern data.
- The NMI handler drives per-frame updates; the main loop (`Reset` at $E058) spins waiting for vblank and frame counter changes.
- `PadBitTableLives` at $E4B4 is a shared lookup table — `DrawLives` reads from base $E4B4, `DrawScore` reads from offset $0C ($E4C0).
- IRQ vector points to $E000 (Reset) and is effectively unused.
- **The 16 KiB PRG is two byte-identical 8 KiB halves.** `prg[0x0000:0x2000] == prg[0x2000:0x4000]` is exactly true. Both halves hold the same program, pointer table, and vector set. The vectors point at `$E06E`/`$E000`/`$E000`, so the CPU always enters the `$E000` copy; nothing references `$C000-$DFFF`, so the first copy is dead and is kept as classified `.byte` data rather than disassembled.
- Unreached gaps inside the live code region decode as valid subroutines (PPUMASK show/hide background, PPUCTRL NMI-disable, frame-counter digit tiles, `ldx #$80 / txs`). Per Pass-1 policy they stay `.byte` data; each is decoded in the `prg.s` header comment block.
- The PRG is approximately 7.2% code / 92.8% data. Most data is `$FF` padding from `$E7C3-$EFFF` and `$F012-$FFF9`.
- The NMI handler drives per-frame updates; the main loop spins waiting for vblank and frame-counter changes.
- IRQ vector points to $E000 (`Reset`) and is effectively unused.
---
## 11. Next Steps
- **Promotion** — move to a top-level `NES PowerPad Test Card (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.
- **Optional deeper Pass 3 work** — extract the pattern pointer data at $F000+ into labeled `.byte` blocks; refine zero-page aliases with more specific roles.
- Optional: refine the `Zp05`–`Zp0F` aliases now that `ProcessPads`/`DrawScore`/`DrawLives` semantics are known (pad-state bytes vs. shift counters).
- Optional: `$E4CC-$E4F9` holds two near-identical icon streams differing only in two bytes; could be collapsed to a parameterized table in a future reconstruction, but the byte form is preserved as-is.