# Lunar Pool (U) — Skeleton Project ROM: `_wip/Lunar Pool (U)/Lunar Pool (U).nes` (24,592 bytes, iNES). Status: **PASS 1 COMPLETE** — 4,586 instructions / 9,475 code bytes (57.8%) / 6,909 data bytes, byte accounting exact, **BYTE-IDENTICAL** at the PRG level. Pass 2 and 3 outstanding. ## ROM Facts | Property | Value | |----------|-------| | Mapper | 0 (NROM) — from the iNES header | | PRG | 16 KB (header claims map at CPU $C000–$FFFF) | | CHR | 8 KB | | Mirroring | Horizontal | | Battery-backed flag | clear | | ROM SHA-256 | `6da37fd34e8f4eb3df34e9f446726a193db77d0e0bba32aa328ef225993fc8a1` | | PRG SHA-256 | `43a2f54cd48b38fc1e4a33051e019528e9789a774ace773fa7549c33dc9492c9` | ## Interrupt Vectors (taken from PRG $3FFA–$3FFF) | Vector | Target | Note | |--------|--------|------| | NMI | `$FF65` | per-frame heartbeat | | RESET | `$E80C` | non-$C000 entry at $E80C — trace from here in Pass 1 | | IRQ | `$FF64` | target $FF64 — verify during Pass 1 | ## Pass Status | Pass | Status | Description | |------|--------|-------------| | Pass 0 | Superseded | `prg.s` was a 12-line `.incbin` of the whole PRG — no disassembly at all | | Pass 1 | **Complete** | 4,586 instructions / **9,475 code bytes (57.8%)** / 6,909 data bytes. Byte accounting exact: 9,403 + 6,981 = 16,384. 598 labels, 598 routine headers | | Pass 2 | Outstanding | ~585 `Label_Cxxxx` to name | | Pass 3 | Outstanding | Data regions inside the 6,909 unreached bytes need naming | ### How Pass 1 got to 57.8% | Step | Instructions | Code | |------|--------------|------| | Vectors + direct `jsr`/`jmp`/branch, official opcodes | 1,259 | 2,561 B (15.6%) | | `--undocumented` (108 NMOS opcodes) | 4,572 | 9,436 B (57.6%) | | `--auto-seed` (48 table-referenced entries) + `--extra-seed 0xE80C` | **4,586** | **9,475 B (57.8%)** | ### The dispatch mechanisms **1. `jmp ($7961)` at three sites** (`$F788`, `$F7F6`, `$F828`). The target address is read from the RAM pointer at `$7961`/`$7962`, so it cannot be followed statically. These are game-over / hi-score screens: the data blocks that follow them contain the screen text (`"game over"`, `"hi`score__"`, `"shot"`). **2. Stack-based dispatch at `$CBDC`** — the same pattern as Mario Bros (JU)'s `$CD9E`: ``` $CBDC pla / sta ZpFA $CBDF pla / sta ZpFB $CBE7 lda (ZpFA),y ; read the entry, indexed by Y ``` The pointer arrives via `pla`, so it is only knowable in the caller's frame. ### Tracer fixes required to get there Three undocumented opcodes were missing from `nesdis.py` and were added: * `$72` — `RRA abs` (the `RRA` family had `$63`/`$67`/`$6F`/`$73`/`$77`/`$7B`/`$7F` but not `$72`). * `$42` — `NOP #imm` (the NOP-immediate family had `$80`/`$82`/`$89`/`$C2`/`$E2` but not `$42`). * `$FA` — `NOP abs,X` (the NOP-abs,X family had `$1C`/`$3C`/`$5C`/`$7C`/`$DC`/`$FC` but not `$FA`). One emitter fix: ca65 silently rewrites an operand whose high byte is `$00` in the shorter zero-page form (`sta $00FF,x` becomes `sta $FF,x`, emitting `$95` instead of `$9D`). `render()` now emits the `a:` prefix for ABS/ABSX/ABSY/IND operands with a high byte of `$00`, forcing the absolute form so the bytes match the ROM. `operand_value()` strips the prefix when parsing. ### Known remaining gaps * The tracer still reports two overlaps (`$C3BB` — resolved with `--data 0xC3BB:1` — and `$F0EF`) and three rejected mis-aligned stubs. * 6,909 bytes (42.2%) are still data, gated behind the `jmp ($7961)` and `$CBDC` dispatches. * `locals.inc` carries 114 generated zero-page aliases, none yet named semantically. ## `copilot/` Contents | File | Purpose | |------|---------| | `Lunar Pool (U).nes` | original ROM image (reference) | | `prg.bin` / `header.bin` / `chr.bin` | split image, produced by `_tools/splitnes.py` | | `prg.s` | Pass-1 skeleton: `.incbin "prg.bin", $0000, $4000` (whole PRG) | | `symbols.inc` | shared NES hardware constants (PPU/APU/controller, from the skeleton) | | `locals.inc` | project-local zero-page aliases — empty for now (filled in Pass 2) | | `linker.cfg` | PRG at $C000, 16 KB, `fill = yes` | | `Makefile` | links `reconstructed_prg.bin` and `cmp`s it against `prg.bin` | | `reconstructed_prg.bin` | build output from the last verified build | Header/CHR are reproduced verbatim from `header.bin`/`chr.bin` (not linked): `make` verifies **PRG bytes only** at this stage, which is exactly what a skeleton proves. When this project is promoted to a full disassembly, `header.s` / `chr.s` (as e.g. `Burger Time (U)`) and a full-ROM `make` target get added. ## Build ```bash cd "_wip/Lunar Pool (U)/copilot" make # → cmp reconstructed_prg.bin prg.bin → "BYTE-IDENTICAL" ``` ## Next Steps (per `PROMPTS.md`) 1. **Pass 1** — complete (57.8% code, byte accounting exact). 2. **Pass 2** — name the ~585 remaining `Label_Cxxxx`; fill `locals.inc` with semantic zero-page aliases. 3. **Pass 3** — name data tables/structures in the 6,909 unreached bytes; release provisional flags. 4. **Promotion** — move to a top-level `/copilot/` folder, add full-ROM build (header.s + chr.s + vectors), and write the power-on flow `OVERVIEW.md` in the established style; then it joins the root index's numbered inventory.