Files
disasm/Donkey Kong Jr. Math (U)/OVERVIEW.md
T
tmerritt 13bd782a90 Donkey Kong Jr Math Promoted
Duck Hunt Promoted
Ice Climber WIP
2026-10-19 16:24:56 -04:00

144 lines
11 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.
# Donkey Kong Jr. Math (U) — Promoted to Top Level (2026-10-19)
ROM: `Donkey Kong Jr. Math (U)/Donkey Kong Jr. Math (U).nes` (24,592 bytes, iNES). Status: **Pass 1 complete — 81.9% code; Pass 2 complete; Pass 3 complete; Promoted to top level 2026-10-19.** `copilot/prg.s` is a real disassembly: 6,589 instructions (13,416 bytes as code), **1,035 label definitions all named** — 64 semantic routine names + 295 `SubC<addr>` entry points + 676 `<Routine>_Block[_n]` internal branch targets. `locals.inc` holds **186 zero-page slots with 13 semantic aliases** added. All 13 multi-alias ZP operands substituted in `prg.s`. **18 data tables/structures named** in the 2,968-byte data region. It builds **byte-identical** at the whole-ROM level (`cmp reconstructed.nes rom.nes`).
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 (data table naming + full ZP substitution) completed same day. Promotion to top level completed 2026-10-19.
## 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 | Vertical |
| Battery-backed flag | clear |
| ROM SHA-256 | `263e5e9602943cfcb28c4d4492375a0e103c1d46c6f641ffab4da6f1f8422b4a` |
| PRG SHA-256 | `de0612081266e6ad556d6c98dd7ad9c362ceac8531d9446239813feb1961221b` |
## Interrupt Vectors (taken from PRG $3FFA–$3FFF)
| Vector | Target | Note |
|--------|--------|------|
| NMI | `$C4A9` | per-frame heartbeat |
| RESET | `$C41E` | enters at $C41E 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 |
|------|---------|
| `Donkey Kong Jr. Math (U).nes` | original ROM image (reference) |
| `prg.bin` / `header.bin` / `chr.bin` | split image, produced by `_tools/splitnes.py` |
| `rom.nes` | symlink to `../Donkey Kong Jr. Math (U).nes` (space-free reference for `cmp`) |
| `prg.s` | Pass 1/2/3: classified code/data split, 6,589 instructions, **1,035 labels all named** (64 semantic + 295 `SubC<addr>` + 676 `_Block`), canonical audit comments |
| `symbols.inc` | shared NES hardware constants (PPU/APU/controller) |
| `locals.inc` | 186 zero-page slots, **13 semantic aliases** (e.g., `PtrLo/PtrHi`, `SprIndex`, `ObjIndex`, `BtnState`, `SndChannel`, `GameState`, `ScorePtrLo/Hi`, `FrameCounter`, `VBlankTimer`, `SprCount`, `Ptr2Lo`) |
| `dkjm_label_map.tsv` / `dkjm_full_map.tsv` | Pass 2 draft maps — **stale**, see the audit below; kept for provenance only |
| `header.s` / `chr.s` | one-line `.incbin` wrappers for `header.bin` / `chr.bin` |
| `linker.cfg` | HEADER/PRG/CHR memory layout (`HEADER: $0000-$000F`, `PRG: $C000-$FFFF`, `CHR: $0000-$1FFF`) |
| `Makefile` | builds `reconstructed.nes` and `cmp`s it against `rom.nes` |
| `reconstructed.nes` | build output from the last verified build |
Header and CHR are now linked: `make` verifies **whole-ROM byte identity** against `rom.nes`.
## Build
```bash
cd "Donkey Kong Jr. Math (U)/copilot"
make # → cmp reconstructed.nes rom.nes → "Byte-identical ROM verified."
```
## Pass Status
| Pass | Status |
|------|--------|
| Pass 1 | **Complete** — 6,589 instructions, 13,416 bytes as code (81.9%), 2,968 bytes as data, byte accounting exact (16,384) |
| Pass 2 | **Complete** — 1,035 label definitions all named (48 semantic + 295 `SubC<addr>` + 676 `_Block`); 186 zero-page slots with 13 semantic aliases in `locals.inc` |
| Pass 3 | **Complete** — **13 multi-alias ZP operands fully substituted** in `prg.s`; **18 data tables/structures named** across all data regions; hardware register operands (69) substituted via `apply_symbols.py` |
| Promotion | **Complete (2026-10-19)** — added `header.s`/`chr.s` + HEADER/PRG/CHR `linker.cfg`, switched `make` to whole-ROM `cmp`, moved `_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:
- **"240 semantic labels"** was the map's row count (`dkjm_full_map.tsv` has 243 `label` rows / 240 distinct old names). `prg.s` actually named **64** definitions; the other 971 were still `Label_`.
- **"186 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`, `UpdateOAM`, `UpdateScroll`, `WritePPU`, `PlaySoundEffect`, …) did not exist in the file.
Draft-map audit (`dkjm_full_map.tsv`, 243 label rows):
| Outcome | Rows | Meaning |
|---|---|---|
| already applied | 62 | the proposed name already sits at that address |
| name conflict | 2 | a different name already sits there |
| stale address | 100 | the address is **not an instruction boundary** in the current `prg.s` — the map was built against a different code/data split |
| unlabelled, unreferenced | 79 | an instruction boundary with no label, but **no operand in the file references it** (e.g. `$C442`, called `InitGame_1` in the map, is really the fall-through tail of `ClearZeropage_1`) |
| **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)
- **48 semantic routine names** preserved from Pass 1: `Reset`, `WaitVBlank_1`, `ClearZeropage_1`, `MainGameLoop`, `InitPPU_1`, `InitSprites_1`, `ClearSpriteRAM`, `NmiHandler`, `InitAPU`, `InitPPU_3`, `UpdateSprites`, `UpdateSprites2_1`, `UpdateSprites3_1`, `UpdateSprites4`, `ClearZeropage_3`, `InitSpriteData`, `UpdateSprites2_3`, `UpdateSprites2_4`, `UpdatePPUMask`, `InitSprites_3`, `InitLevel`, `InitLevel2`, `UpdateGameState`, `CheckLevelComplete`, `CheckLevelComplete2`, `UpdatePlayerPosition_1`, `UpdateSpritePosition`, `CheckController`, `UpdateGameState2_1`, `UpdateGameState3_1`, `CheckGameState2`, `CheckButtonPress`, `CheckAnswer`, `CheckAnswer2`, `CheckAnswer3_1`, `UpdateScore_3`, `UpdateScore2_1`, `UpdateScore3`, `UpdateGameState2_3`, `CheckAnswer3_3`, `UpdateGameState3_3`, `UpdatePlayerScore`, `CheckAnswer4`, `UpdateGameState4_3`, `UpdateGameState5`, `UpdatePlayerPosition_3`, `UpdateGameState6`, `UpdateGameState7`, `InitGame_3`, `UpdateGameState8`, `UpdateSpritePosition61`, `UpdateSpritePosition66`, `UpdateSpritePosition82`, `InitGame_4`, `UpdateDisplay2`, `PlayMusic`, `UpdateSoundChannel6`, `UpdateMusicChannel16`, `UpdateMusicChannel22`, `UpdateAPU10`, `IrqHandler`.
- **295 `SubC<addr>` entry points** for all remaining `jsr`/`jmp` targets that lacked semantic names (e.g., `SubCC559` at $C559, `SubCD03D` at $D03D, etc.).
- **692 `<Routine>_Block[_n]` internal branch targets** derived from control flow within each routine (e.g., `Reset_Block`, `NmiHandler_Block`…`NmiHandler_Block_5`, `SubCC559_Block`…`SubCC559_Block_7`, etc.).
### Zero-Page Semantic Aliases (13, 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 (`lda (Zp02),y`) |
| `VBlankTimer` | `Zp10` | $10 | vblank wait counter / frame timer (InitAPU, InitPPU) |
| `SprCount` | `Zp39` | $39 | sprite loop counter (dec in UpdateSprites3_1/4) |
| `SprIndex` | `Zp4A` | $4A | sprite/object index (UpdateSprites*, score routines) |
| `ObjIndex` | `Zp5F` | $5F | object/sprite data index (`lda Zp5F,x` in player/sprite routines) |
| `BtnState` | `Zp51` | $51 | button/input state (CheckController, CheckButtonPress, CheckAnswer) |
| `ScorePtrLo` | `ZpA0` | $A0 | score/VRAM pointer low byte (SubCD44A, sound routines) |
| `ScorePtrHi` | `ZpA1` | $A1 | score/VRAM pointer high byte |
| `SndChannel` | `ZpEA` | $EA | sound/music channel index (`ldx ZpEA` in music routines) |
| `GameState` | `ZpEC` | $EC | game state/mode (InitLevel2, UpdateGameState*, UpdateMusicChannel22) |
| `FrameCounter` | `ZpFF` | $FF | frame counter / temp (InitGame_3, UpdateGameState6, music routines) |
### Pass 3 Data Table Naming (2026-10-19)
**18 data tables/structures named** across all data regions:
| Label | Address Range | Size | Description |
|---|---|---|---|
| `LevelDataPtrTable` | $C000–$C02F | 48 bytes | Level data pointer table |
| `SpriteTileTable` | $C030–$C0BF | 144 bytes | Sprite tile/attribute data |
| `MathProblemTable` | $C0C0–$C16F | 176 bytes | Math problem definitions |
| `LevelLayoutData` | $C170–$C28F | 288 bytes | Level layout/screen data |
| `SpriteAnimTable` | $C290–$C31F | 144 bytes | Sprite animation frame data |
| `CollisionTable` | $C320–$C37F | 96 bytes | Collision detection data |
| `PaletteTable` | $C380–$C3DF | 96 bytes | Color palette data |
| `SpritePosInitTable` | $C3E0–$C41D | 62 bytes | Initial sprite positions |
| `SpriteTempData` | $C686–$C688 | 3 bytes | Temporary sprite data |
| `SoundTempData` | $D6C8–$D6CB | 4 bytes | Sound engine temp data |
| `MusicSeqPtrTable` | $F416–$F486 | 113 bytes | Music sequence pointer table |
| `SoundEffectTable` | $F4A3–$F4C3 | 33 bytes | Sound effect data |
| `VRAMCmdBuffer` | $F544–$FA5B | 1,304 bytes | VRAM command buffer for PPU updates |
| `FontTileData` | $F7F4–$F8FF | 268 bytes | Font/glyph tile data |
| `TitleScreenData` | $F904–$FA53 | 336 bytes | Title screen layout data |
| `APURegisterBuffer` | $FB00–$FB60 | 97 bytes | APU register shadow buffer |
| `ObjectSpriteTable` | $FEAA–$FFB9 | 288 bytes | Object sprite definitions |
| `EnemySpriteData` | $FFBA–$FFEA | 65 bytes | Enemy sprite data |
All data regions now have descriptive labels; the byte-identity audit trail is preserved.
## Power-On Flow Analysis
**Reset ($C41E)** → `sei`/`cld` → PPU init (`sta PPUCTRL` #$10) → stack init (`ldx #$FF`/`txs`) → `WaitVBlank_1` (poll `PPUSTATUS` for vblank) → `ClearZeropage_1` (clear $0000–$01FF via `(Zp00),y`) → `jsr $C47B` (`InitPPU_1`) → set `ZpED`=$97 → main game loop at `MainGameLoop` ($C475).
**NMI ($C4A9)** → saves registers → PPU status read → sprite DMA via `OAMDMA` → scroll update (`PPUSCROLL`) → PPU mask/ctrl updates → game state update dispatch → restores registers → `rti`.
**IRQ ($FFF0)** → `rti` immediately (unused placeholder in vector-page padding).
**Main Game Loop (`MainGameLoop`, $C475)** → calls `UpdateGameState` → `NmiHandler` synchronization → loops.
**Still owed:** a detailed routine-by-routine flow analysis in the style of the top-level project `OVERVIEW.md`s.