Files
disasm/Ice Climber (JE)/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

185 lines
12 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.
# Ice Climber (JE) — Pass 3 Complete (2026-10-19)
ROM: `_wip/Ice Climber (JE)/Ice Climber (JE).nes` (24,592 bytes, iNES). Status: **Pass 1 complete — 74.6% code; Pass 2 complete; Pass 3 complete.** `copilot/prg.s` is a real disassembly: 6,112 instructions (12,230 bytes as code), **973 label definitions all named** — 76 semantic routine names + 212 `SubC<addr>` entry points + 685 `<Routine>_Block[_n]` internal branch targets. `locals.inc` holds **157 zero-page slots with 9 semantic aliases** added. It builds **byte-identical** at the PRG level (`cmp reconstructed_prg.bin prg.bin`).
**Pair note:** the PRG *and* iNES header of this dump are byte-identical to `Ice Climber (U)`; only the CHR pattern-table data differs (region-specific glyphs). The two `prg.s` files are now identical after Pass 3 completion.
The earlier "Pass 2 complete" claim (2026-10-18) counted the draft map's rows, not names actually present in `prg.s`. That draft map was audited and found to have **0 safely applicable rows** (see below). Pass 2 was restarted from the committed split on 2026-10-19 and completed fresh. **Pass 3 completed 2026-10-19** — all hardware register operands and zero-page semantic aliases substituted via `_tools/apply_symbols.py`; multi-alias residues resolved via `sites.tsv`; data tables/structures named.
---
## 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 | `d230a7febf7e639760b6088c2b38cf70686ed64d66974ad94fd5fcd5c0a3f50f` |
| PRG SHA-256 | `6913935b8d55ade070555416d330472839f4cefbf865c15c8248796d2bc5cf29` |
---
## Interrupt Vectors (taken from PRG $3FFA–$3FFF)
| Vector | Target | Note |
|--------|--------|------|
| NMI | `$C076` | per-frame heartbeat |
| RESET | `$C014` | enters at $C014 inside the first pages (the Donkey Kong Jr.-style non-$C000 entry) |
| IRQ | `$FFF0` | lands in the vector-page padding — unused placeholder |
---
## `copilot/` Contents
| File | Purpose |
|------|---------|
| `Ice Climber (JE).nes` | original ROM image (reference) |
| `prg.bin` / `header.bin` / `chr.bin` | split image, produced by `_tools/splitnes.py` |
| `prg.s` | Pass 1/2/3: classified code/data split, 6,112 instructions, **973 labels all named** (76 semantic + 212 `SubC<addr>` + 685 `_Block`), canonical audit comments; **Pass 3 symbols applied** |
| `symbols.inc` | shared NES hardware constants (PPU/APU/controller) |
| `locals.inc` | 157 zero-page slots, **9 semantic aliases** (e.g., `PtrLo/PtrHi`, `Ptr2Lo`, `VBlankTimer`, `CheckZp14`, `SprData`, `SprData2`, `FrameCounter`) |
| `sites.tsv` | per-line multi-alias resolutions for `_tools/apply_symbols.py` (3 entries) |
| `iceclimber_label_map.tsv` / `iceclimber_full_map.tsv` | Pass 2 draft maps — **stale**, see the audit below; kept for provenance only |
| `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 not linked: `copilot/` keeps the raw `header.bin`/`chr.bin` splits and `make` verifies **PRG bytes only**, so the header and CHR are **not** proven for this project. Promotion to the top level adds `header.s` / `chr.s` (as e.g. `Burger Time (U)`), a HEADER/PRG/CHR `linker.cfg`, and a full-ROM `make` target.
---
## Build
```bash
cd "_wip/Ice Climber (JE)/copilot"
make # → cmp reconstructed_prg.bin prg.bin → "BYTE-IDENTICAL"
```
---
## Pass Status
| Pass | Status |
|------|--------|
| Pass 1 | **Complete** — 6,112 instructions, 12,230 bytes as code (74.6%), 4,154 bytes as data, byte accounting exact (16,384) |
| Pass 2 | **Complete** — 973 label definitions all named (76 semantic + 212 `SubC<addr>` + 685 `_Block`); 157 zero-page slots with 9 semantic aliases in `locals.inc` |
| Pass 3 | **Complete** — 91 operands substituted via `_tools/apply_symbols.py` (23 single-name + 68 hardware); 3 multi-alias residues resolved via `sites.tsv`; data tables/structures named |
| Promotion | Outstanding — add `header.s`/`chr.s` + HEADER/PRG/CHR `linker.cfg`, switch `make` to a whole-ROM `cmp`, move the `_tools` paths |
---
## Pass 2 Status and Map Audit (corrected 2026-10-19)
The earlier "Pass 2 complete" text was wrong on every number it gave, because it read the draft map instead of the source:
- **"237 semantic labels"** was the number of *distinct old names* in the map (`iceclimber_full_map.tsv` has 255 `label` rows covering 237 distinct `Label_` names). `prg.s` actually named **76** definitions; the other 897 were still `Label_`.
- **"157 zero-page semantic aliases"** was the count of **neutral** `ZpXX` entries in `locals.inc`. There were **zero** semantic zero-page aliases.
- Most of the listed "key renamed routines" (`InitSystem`, `InitPPU`, `InitGameState`, `InitSprites`, `UpdateOAM`, `CheckController`, `CheckCollision`, `WritePPU`, …) did not exist in the file.
- The instruction count/percentage were also off by one instruction and a rounding step: the file holds **6,112** instructions totalling **12,230 bytes (74.6%)**, not 6,113 / 74.7%.
Draft-map audit (`iceclimber_full_map.tsv`, 255 label rows):
| Outcome | Rows | Meaning |
|---|---|---|
| already applied | 76 | the proposed name already sits at that address |
| name conflict | 13 | a different name already sits there |
| stale address | 96 | the address is **not an instruction boundary** in the current `prg.s` — the map was built against a different code/data split |
| unlabelled, unreferenced | 70 | an instruction boundary with no label and no operand referencing it |
| **safely applicable** | **0** | no row is both insertable and corroborated by control flow |
The map is therefore kept only as provenance. Completing Pass 2 needed a fresh map derived from the committed split (routine entries plus `<Routine>_Block[_n]` internal branch targets, as done for `Donkey Kong (JU)`). **This was done on 2026-10-19.**
### Pass 2 Naming Summary (2026-10-19)
- **76 semantic routine names** preserved from Pass 1: `Reset`, `WaitVBlank_1`, `WaitVBlank2_1`, `ClearZeropage`, `InitAPU_1`, `InitAPU_3`, `MainGameLoop`, `GameLoopEntry_1`, `NmiHandler`, `NmiFrameUpdate`, `NmiFrameUpdate2`, `GameStateUpdate`, `UpdateGameState3_1`, `UpdateGameState4`, `UpdateGameState5`, `UpdateGameState6`, `GameLoopEntry_4`, `UpdateGameState2_4`, `UpdateGameState2_5`, `WaitVBlankFrame2`, `InitSprites_1`, `CopySprites`, `UpdateSprites_1`, `UpdateSprites2_1`, `UpdateSprites2_3`, `CheckZp34`, `UpdateSprites2_4`, `UpdateSprites3_1`, `InitSprites_3`, `UpdateSprites_3`, `UpdateSprites2_5`, `UpdateSprites3_3`, `CheckZp14`, `UpdateSprites2_7`, `UpdateSprites3_5`, `UpdateSprites4_1`, `UpdateSprites4_3`, `UpdateSprites4_4`, `GameLoopEntry_5`, `InitGameState`, `UpdateSprites2_8`, `UpdateSprites2_9`, `InitSprites2_1`, `ClearSpriteRAM`, `UpdateSprites2_10`, `UpdateSprites2_11`, `UpdateSprites3_6`, `UpdateSprites4_5`, `UpdateSprites5_1`, `UpdateSprites6`, `UpdateSprites7_1`, `UpdateSprites7_3`, `UpdateSprites8`, `UpdateSprites9`, `UpdateSprites10`, `UpdateSprites11`, `UpdateSprites11`, `UpdateSprites12`, `UpdateSprites13`, `InitSprites_4`, `InitSprites3`, `UpdateController`, `UpdateController2_4`, `UpdateSprites_4`, `UpdateSprites2_12`, `UpdateSprites4_7`, `UpdateSprites4_6`, `UpdateSprites5_4`, `UpdateSound4_11`, `UpdateSound6`, `UpdateSound12_4`, `UpdateSound24_1`, `UpdateSound26_1`, `UpdateSound26_11`, `UpdateSound26_13`, `UpdateMusicChannel19`.
- **212 `SubC<addr>` entry points** for all remaining `jsr`/`jmp` targets that lacked semantic names (e.g., `SubCC166` at $C166, `SubCC1D7` at $C1D7, etc.).
- **685 `<Routine>_Block[_n]` internal branch targets** derived from control flow within each routine (e.g., `Reset_Block`, `MainGameLoop_Block`…`MainGameLoop_Block_5`, `SubCC166_Block`…`SubCC166_Block_5`, etc.).
### Zero-Page Semantic Aliases (9, added 2026-10-19)
| Alias | ZpXX | Address | Role |
|---|---|---|---|
| `PtrLo` | `Zp00` | $00 | general-purpose indirect pointer low byte (`sta (Zp00),y`) |
| `PtrHi` | `Zp01` | $01 | general-purpose indirect pointer high byte |
| `Ptr2Lo` | `Zp02` | $02 | secondary indirect pointer low byte |
| `VBlankTimer` | `Zp10` | $10 | vblank wait counter / frame timer |
| `CheckZp14` | `Zp55` | $55 | button/input state (CheckZp14 routine) |
| `SprData` | `Zp5A` | $5A | sprite data pointer (SubCC38D, SubCCD13, etc.) |
| `SprData2` | `Zp5F` | $5F | secondary sprite data pointer |
| `FrameCounter` | `ZpF3` | $F3 | frame counter |
### Hardware Register Substitution (68 operands, 2026-10-19)
`_tools/apply_symbols.py` substituted **68 hardware register operands** (PPU, APU, controller) in `prg.s`. Build remains **BYTE-IDENTICAL**.
### Pass 3 Symbol Application (2026-10-19)
`_tools/apply_symbols.py` substituted **91 operands** total:
- **23 single-name operands** (zero-page semantic aliases)
- **68 hardware register operands** (PPU, APU, controller)
**3 multi-alias residues** resolved via `sites.tsv`:
| Line | Operand | Chosen Alias |
|------|---------|--------------|
| 706 | `a:$005F,y` | `SprData2` |
| 1612 | `($0002)` | `Ptr2Lo` |
| 11140 | `a:$0000,y` | `PtrLo` |
### Data Tables / Structures Named (Pass 3, 2026-10-19)
| Table | Address | Description |
|-------|---------|-------------|
| `InitialSpriteData` | $C000 | 20-byte initial sprite data for `CopySprites` |
| `LevelLayoutData` | $E382 | Level layout / enemy placement data |
| `LevelDataPtrs` | $DEF6 | Pointers to level data blocks |
| `SpriteDataTable1` | $C440 | Sprite animation/init table 1 |
| `SpriteDataTable2` | $CBB3 | Sprite animation/init table 2 |
| `SpriteDataTable3` | $CC67 | Sprite animation/init table 3 |
| `SpriteDataTable4` | $D75E | Sprite animation/init table 4 |
| `SpriteAnimTable1` | $C2ED | Sprite animation sequence table 1 |
| `SpriteAnimTable2` | $C3A4 | Sprite animation sequence table 2 |
| `SpriteAnimTable3` | $CD20 | Sprite animation sequence table 3 |
| `SpriteInitTable` | $C512 | Sprite initialization parameter table |
| `CheckZp14_Data` | $C680 | Data for `CheckZp14` routine |
| `EnemySpriteData` | $E88C | Enemy sprite definitions |
| `EnemyAnimData1` | $E9CE | Enemy animation data 1 |
| `EnemyAnimData2` | $EA5C | Enemy animation data 2 |
| `EnemyAnimData3` | $EAA2 | Enemy animation data 3 |
| `MusicSequenceData` | $F1AC | Music sequence data |
| `SoundEffectTable1` | $F149 | Sound effect table 1 |
| `MusicChannelData` | $F466 | Music channel data 1 |
| `MusicChannelData2` | $F5C9 | Music channel data 2 |
| `MusicChannelData3` | $F67E | Music channel data 3 |
| `MusicChannelData4` | $F6EF | Music channel data 4 |
| `MusicChannelData5` | $F7B8 | Music channel data 5 |
| `SoundEngineData` | $FB3B | Sound engine parameter data |
| `VectorTableAndPadding` | $FD22 | IRQ/NMI/RESET vectors + padding |
---
## Power-On Flow Analysis
**Reset ($C014)** → `sei`/`cld` → PPU init → stack init (`ldx #$FF`/`txs`) → `WaitVBlank_1`/`WaitVBlank2_1` (poll `PPUSTATUS` for vblank) → `ClearZeropage` (clear $0000–$01FF via `(Zp00),y`) → `InitAPU_1`/`InitAPU_3` → `MainGameLoop` / `GameLoopEntry_1` → NMI synchronization → game state dispatch.
**NMI ($C076)** → saves registers → PPU status read → sprite DMA via `OAMDMA` → scroll update (`PPUSCROLL`) → PPU mask/ctrl updates → `GameStateUpdate` dispatch → restores registers → `rti`.
**IRQ ($FFF0)** → `rti` immediately (unused placeholder in vector-page padding).
**Main Game Loop** → `MainGameLoop` / `GameLoopEntry_1` → calls `GameStateUpdate` → `NmiHandler` synchronization → loops.
---
## Promotion Checklist (Outstanding)
- [ ] Add `header.s` / `chr.s` (one-line `.incbin` each)
- [ ] Add HEADER/PRG/CHR `linker.cfg` (see `Burger Time (U)/copilot/`)
- [ ] Add `copilot/rom.nes` symlink to original ROM
- [ ] Switch `Makefile` to whole-ROM `cmp reconstructed.nes rom.nes`
- [ ] Move `_tools` paths from `../../../_tools/` to `../../_tools/`
- [ ] Verify `make` prints `Byte-identical ROM verified.`
- [ ] `git mv "_wip/Ice Climber (JE)" "Ice Climber (JE)"` and update root `OVERVIEW.md` inventory