# Golf (JU) — Pass 1 complete, Pass 2 complete, Pass 3 complete ROM: `_wip/NROM/Golf (JU)/Golf (JU).nes` (24,592 bytes, iNES). Status: **Pass 1 complete, Pass 2 complete, Pass 3 complete — ready for promotion.** The **whole-ROM** build is **byte-identical** (header + PRG + CHR, `cmp reconstructed.nes rom.nes`). **Pass 1 (code/data split) — complete.** Reachable code traced from RESET ($C000) and NMI ($C0A6); all 4,217 reachable instructions are explicit source in `prg.s` (13,541 lines). NMI callbacks ($C5A0=`GameplayInputNmiCallback`, $C6C8, $CFAA, $DAA2; continuations $CFC3/$D03E/$D08A) and controller/attract paths ($DAA2, $C0C7=`InitializeRuntimeState`) are in source. All remaining `.incbin` spans have zero code entry points and are confirmed data. **Pass 2 (label naming) — complete.** Startup/NMI/PPU/controller/course-decode routines named (batches 1–3 per `PASS2_PLAN.md`). All 112 subroutine-entry `Label_` labels renamed: 4 semantic (`WaitForFramePreservingY`, `SpriteOffsetForController`, `SetPpuScrollAndMask`, `MultiplyABy32`) and 108 address-derived `SubC` where behaviour is not yet established. All jsr/jmp call sites carry symbolic names — 0 raw `$XXXX` call operands. The remaining 349 internal branch-target `Label_` labels are resolved: 339 given role-based names (`_Loop`, `_OnZero`, `_OnNonzero`, `_OnCarryClear`, `_OnCarrySet`, `_OnNonnegative`) derived with `_tools/derive_branch_names.py` from `routinemap.tsv` and applied with `_tools/apply_pass2_map.py`; the other 10 (`Label_C198`, `Label_D424`, `Label_D9D8`, `Label_D9F9`, `Label_DF3A`, `Label_FA78`, `Label_FB24`, `Label_FB83`, `Label_FC6B`, `Label_FF00`) are fall-through-only labels that no branch or jump targets, so they intentionally keep their `Label_` definitions. **Pass 3 (semantic naming) — complete.** Conversions completed: - Interrupt vectors at $FFFA–$FFFF → `.word NmiHandler`/`Reset`/`IrqLoop` - Course-stream split pointer table at $E6C3/$E6D5 (18 entries) → `CourseLayoutPointerLo`/`CourseLayoutPointerHi` - Bit-mask table $D6F4–$D6FB → `BitMaskTable` ($80 40 20 10 08 04 02 01) - PPU write table $FD0A–$FD11 → `PpuTableFd0a` - Score adjust table $D988–$D9B0 → `D988ScoreAdjustTable` - PPU value table $FF85–$FF9F → `FF85PpuValueTable` - Sprite/object tables (`sprite_object_tables.inc`) and gameplay PPU palette ($E1B8–$E1D7) already emitted as structured source. - Audit comments converted from the pre-canonical spelling (`; C000: A9 00`) to the canonical `; CPU:$C000 PRG:0000 A9 00` form at column 35 via `_tools/repair_audit_comments.py` (ground truth: ca65 listing vs `prg.bin`) then `_tools/align_audit_comments.py` — 4,156 comments re-aligned; comment and whitespace only, no byte change. - All NES hardware-register operands (`$2000`–`$4017`) use `symbols.inc` names (already done in Pass 2). - 275 zero-page operands substituted with `locals.inc` aliases via `_tools/apply_symbols.py`; 0 multi-alias residue, no unexplained numeric operands remain. - Every branch and jump target is a symbolic label; 0 raw `$XXXX` operands remain in code. Remaining `.incbin` spans are largely Iwata's compressed level data ($E6E7–$F905, ~4.6 KB) and opaque lookup tables ($C786–$C84C, $CD85–$CE63, $D304–$D3B6, $D563–$D659, $D823–$D8EC, $DC59–$DD77, $E1D8–$E281, $E3DE–$E3FB, $E588–$E6AF, $FBBF–$FC23). Without runtime tracing / a code/data log to confirm consumers and structure, converting these risks guessy rewrites. Per repo convention, they remain as `.incbin` with address comments. **Pass 3 data tables — interpreted.** Every data region in the PRG is interpreted in the *Pass 3 data-table inventory* below: the structure of each block, the units of its records, and the instruction that reads it. Five sub-tables were separated out of the `.incbin` spans because an instruction in this dump names their address — `ClubSwingSpriteTable` ($C78B), read by `lda $C78B,y` at $C858; `CourseCellPropertyTable` ($D82E), read by `lda $D82E,y` at $D7F8; `HoleLayoutIndexTable` ($E59D) and `HoleLayoutParameterTable` ($E6A7), read by `lda $E59D,y` at $E6B2 and `lda $E6A7,y` at $E6BD; and `CourseLayoutStreamData` ($E6E7), the eighteen compressed hole maps the decoder at $DD78 walks through the pointer table at $E6C3/$E6D5. The spans that remain unnamed are recorded there as unreferenced data, with their ROM provenance and the value structure that is visible in them, rather than named after something they might be. Remaining `.incbin` spans ($C71E–$C720, $C786–$C84C, $CD85–$CE63, $D304–$D3B6, $D563–$D659, $D823–$D8EC, $DC59–$DD77, $E1D8–$E281, $E3DE–$E3FB, $E588–$E6AF, $E6E7–$F905, $FBBF–$FC23) remain as data per the no-guessy-rewrites convention; convert only when a consumer is identified. ## Power-on flow analysis Derived from the disassembly by `_tools/derive_poweron.py`; the call graph is walked from the reset entry. ## ROM Facts | Property | Value | |----------|-------| | Format | iNES | | Mapper | 0 — NROM-128 | | Board | NROM | | PRG | 16 KB (1 x 16 KB), mapped at CPU $C000-$FFFF | | CHR | 8 KB (1 x 8 KB) | | Mirroring | Vertical (header byte 6 bit 0 = 1) | | Four-screen VRAM | no | | Battery | no | | Trainer | none | | Footer | none | | NMI vector | `$C0A6` | | RESET vector | `$C000` | | IRQ vector | `$C0C4` | | ROM SHA-256 | `70986ed4fb441199a9ff50c7b5c6388d9f7a17c29990d1b5f7214572ae0a4bc6` | | PRG SHA-256 | `9f729074cd7c74340f325460173519427b1180f5101f8dc37da7b1c2b9b604d5` | ### Header notes - The header's mapper bits and size fields are read as-is; nothing is unusual about them. ## Interrupt Vectors (taken from PRG $3FFA–$3FFF) | Vector | Target | Note | |--------|--------|------| | NMI | `$C0A6` | per-frame heartbeat | | RESET | `$C000` | cold start at $C000 — the usual pattern in this repo | | IRQ | `$C0C4` | IRQ handler | ## Build This project keeps the **CPU-address linker layout** (one of the 38 `_wip/` projects that do): the PRG really is one contiguous window at `$C000-$FFFF`, so the memory area sits at that CPU address rather than at a `.nes` file offset. ``` $ cd "_wip/NROM/Golf (JU)" && make ca65 -g -o header.o header.s ca65 -g -o prg.o prg.s ca65 -g -o chr.o chr.s ld65 -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o reconstructed.nes header.o prg.o chr.o cmp reconstructed.nes rom.nes Byte-identical ROM verified. ``` The header and the CHR **are** linked and proven — the whole ROM is compared byte for byte, exactly as in the promoted tier. (This project predates the tier's move off the old PRG-only compare, so the older `Makefile.pass0`, `prg.s.pass0` and `prg_pass1.s` artifacts are still in the folder as history; the live build is `prg.s` + `linker.cfg` + `Makefile`.) ## Audit comments — canonical, column 35 Every instruction carries a canonical audit comment at column 35: ``` LDA #$00 ; CPU:$C000 PRG:0000 A9 00 STA PPUCTRL ; CPU:$C002 PRG:0002 8D 00 20 ``` Converted from the pre-canonical spelling (`; C000: A9 00`) with `_tools/repair_audit_comments.py` (ca65 listing vs `prg.bin` ground truth) then `_tools/align_audit_comments.py` — 4,156 comments re-aligned; comment and whitespace only, no byte change. `make` stays byte-identical throughout. ## Files | File | Purpose | |------|---------| | `Golf (JU).nes` | original ROM image (reference) | | `rom.nes` | space-free copy of the original ROM, the byte-identity target | | `prg.bin` / `header.bin` / `chr.bin` | split image, produced by `_tools/splitnes.py` | | `prg.s` | Pass-1 disassembly: classified code/data split with canonical audit comments (column 35); Pass 2 and Pass 3 complete — all routine entries and branch targets named, all zero-page and hardware operands symbolic. Data split into `course_streams.inc` and `sprite_object_tables.inc` | | `header.s` / `chr.s` | the header and CHR segments as ca65 includes | | `symbols.inc` | shared NES hardware constants (PPU/APU/controller) | | `locals.inc` | project-local zero-page aliases (32 semantic names; all 275 zero-page operands substituted, 0 multi-alias residue) | | `course_streams.inc` | Pass-1 compressed primary course-stream bounds | | `sprite_object_tables.inc` | Pass-3 sprite/object table structures | | `linker.cfg` | HEADER / PRG / CHR memory areas (PRG at $C000, 16 KB) | | `Makefile` | links `reconstructed.nes` and `cmp`s it against `rom.nes` | | `PASS1_STATUS.md`, `PASS2_PLAN.md`, `NMI_CALLBACK_AUDIT.md`, `OVERLAP_AUDIT.md`, `SPRITE_OBJECT_TABLE_AUDIT.md`, `COMPRESSED_PARSER_AUDIT.md` | working notes kept alongside the source | ## Pass 3 data-table inventory Every data region in the PRG is listed here — the tables the Pass 3 work already emitted as structured `.byte` rows, and the `.incbin` spans that were still opaque. A region an instruction reads is named and its structure recorded; a region nothing reads is recorded as unreferenced data with its ROM provenance; a region whose bytes decode as instructions is recorded as code the tracer could not reach. | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | $CC15 | 15 B | `SpriteLayoutGroupMap` | One byte per layout group: `00 00 00 01 01 02 02 02 03 03 03 03 03 04 04`. The caller indexes it with a selection value and gets one of five logical layout groups. | the sprite-layout parser at `$CBEA` | | $CC24 | 13 B | `SpriteLayoutPointerLo` | The low bytes of thirteen layout-list addresses: `74 89 9E B3 C8 DD C8 B3 F2 07 1C 31 46` — resolving to $CC74, $CC89, $CC9E, $CCB3, $CCC8, $CCDD, $CCC8, $CCB3, $CCF2, $CD07, $CD1C, $CD31, $CD46. The repeated entries are deliberate: they mirror the same animation state. | the sprite-layout parser at `$CBEA` | | $CC31 | 13 B | `SpriteLayoutPointerHi` | The matching high bytes, one for each entry of `$CC24`; `$CC` throughout, `$CD` where a list crosses a page. | as above | | $CC3E | 6 B | `AltSpriteLayoutPointerLo` | The low bytes of six alternate layout-list addresses: `5B 70 9E 70 5B 74` — $CD5B, $CD70, $CC9E, $CD70, $CD5B, $CC74. The alternate set shares the standard lists rather than duplicating them. | the sprite-layout parser at `$CBEA`, when the selector field is 4 | | $CC44 | 6 B | `AltSpriteLayoutPointerHi` | The matching high bytes, one for each entry of `$CC3E`. | as above | | $CC4A | 21 B | `SpriteLayoutXOffsets` | Twenty-one screen X offsets, one per sprite-list entry, mostly `00 08 10` — the three columns a sprite is built from. | the sprite-layout parser at `$CBEA`, with the list byte as the index | | $CC5F | 21 B | `SpriteLayoutYOffsets` | Twenty-one screen Y offsets, one per sprite-list entry, stepping by eight pixels from `$00` to `$30` so that the rows line up. | as above | | $CC74 | 273 B | `SpriteLayoutLists` | The layout lists themselves: byte strings, each byte both the OAM tile value and the index into the two coordinate tables above, each list closed by `$FF`. The parser also stops after 21 entries. | the sprite-layout parser at `$CBEA`, through the pointer tables | | $D124 | 6 B | `ForwardSequenceThresholds` | Six threshold bytes, `4E 46 3E 36 2E 26`, that gate a forward animation step. | the sequence interpreter in the $D1xx range | | $D12A | 3 B | `TerminalSequenceThresholds` | Three threshold bytes, `4E 46 36`, that end a sequence. | as above | | $D12D | 8 B | `ReturnSequenceThresholds` | Eight threshold bytes, `2E 26 4E 46 36 26 2E 36`, for the return step of a sequence. | as above | | $D135 | 3 B | `FinalSequenceThresholds` | Three threshold bytes, `3E 4D 54`, for the last step of a sequence. | as above | | $D138 | 6 B | `ForwardSequenceFrameLimits` | Six frame limits, `5E 70 36 4D 50 0A`, one per forward step. | as above | | $D6F4 | 8 B | `BitMaskTable` | The eight single-bit masks `80 40 20 10 08 04 02 01`, used to shift a value one bit at a time. | the bit-test code in the $D6xx range | | $D988 | 41 B | `D988ScoreAdjustTable` | The score table: a 6-byte header (`80 80 00 00 FF 00`) followed by the score values `00 FF 04 0A 10 17 1D 23 2A 30 37 3D 44 4B 52 59 60 67 6E 76 7D 85 8D 96 9E A7 B0 BA C3 CD D8 E3 EE FA FF` — the points awarded for each hole, and the `10 05 49 FF 18 69 01` tail. | the scoring code in the $D9xx range | | $DB0D | 3 B | `MenuCursorYPositions` | The three menu cursor Y positions, `78 88 98` (120, 136, 152) — one per menu row. | the menu code at `$DB0D` | | $D823 | 11 B | (unnamed) | Eleven bytes, `03 0C 30 C0 0F EF 00 FE 00 FF 00`, that follow the cell interpreter's code. No instruction in this dump addresses them. | unreferenced data | | $D82E | 191 B | `CourseCellPropertyTable` | The course cell's property bytes. The cell interpreter at `$D7F5` reads one byte with `ldy $D80D,x` and then one from this table with `lda $D82E,y`; the values `$10`, `$20`, `$40` and `$FF` select the collision and terrain responses, and the table continues with the terrain-cell codes (`0A 09 08 06 02 00 …`). | `lda $D82E,y` at `$D7F8` | | $D304 | 179 B | (unnamed) | The bytes decode as a run of 6502 instructions — `lda $49` / `bne` / `jsr $C894` / `lda $3B` / `sta $0208` / … — and end with `jmp $D08A`, so this is a routine the tracer could not reach, not data. | none | | $D563 | 247 B | (unnamed) | 247 bytes whose values climb steadily — `00 00 00 01 01 02 03 04 05 06 08 09 0B 0D 0F 11 13 16 19 1B 1E 21 24 28 2B 2F 32 36 3A 3E 42 47 …` — in the shape of a distance or power table. No instruction in this dump addresses it with an absolute operand, so the consumer that walks it is not established. | unreferenced data (ROM provenance: immediately after the routine at `$D562`) | | $DC59 | 287 B | (unnamed) | 287 bytes of which only two values occur, `$B8` and `$9D` — a two-state mask, in the shape of a per-cell occupancy bitmap. No instruction in this dump addresses it with an absolute operand. | unreferenced data (ROM provenance: between the routine at `$DC56` and the course decoder at `$DD78`) | | $E1B8 | 32 B | `GameplayPpuPalette` | The thirty-two PPU palette bytes uploaded to `$3F00`-`$3F1F` during gameplay. | the palette upload at `$E1AB` | | $E1D8 | 170 B | (unnamed) | 170 bytes of CHR tile indices — `20 20 21 21 41 4F 61 6F BC 83 CF 8F 0C 15 1E 0B 19 0A 1B 9D 18 1E 1D FF 12 17 FF 02 02 02 …` — in the shape of the per-hole background graphics. No instruction in this dump addresses them with an absolute operand. | unreferenced data (ROM provenance: immediately after the gameplay palette) | | $E282 | 1 B | `ObjectSpanStartZero` | The zero that starts the object span table; the span a selector selects begins at the previous entry and ends at its own. | the hole-object parser at `$DE5E`-`$DF52` | | $E283 | 31 B | `ObjectSpanEnds` | Thirty-one monotonically increasing span ends — `03 07 09 0B 0E 10 12 14 16 19 1B 1E 20 23 25 27 29 2B 2E 31 34 38 3A 3C 3E 40 43 45 47 49` — one per object selector, the exclusive end of that shape's slice. | as above | | $E2A2 | 73 B | `ObjectTileTable` | Seventy-three CHR tile indices, one per expanded object sprite (`$E0`-`$F2`). | as above | | $E2EB | 73 B | `ObjectXOffsetTable` | Seventy-three X offsets paired with `ObjectTileTable`, mostly `$00`, `$08` and `$10`; the parser adds each to the record's base X byte. | as above | | $E334 | 73 B | `ObjectYOffsetTable` | Seventy-three Y offsets paired with the other two; the parser adds each to the record's base Y byte. | as above | | $E37D | 7 B | `FixedObjectSpanBounds` | Seven prefix boundaries, `00 03 06 0B 10 17 1E`, for the fixed setup sprite groups. | the fixed setup parser at `$DE5E` | | $E384 | 30 B | `FixedObjectXOffsets` | Thirty X offsets, chiefly `$00`, `$04` and `$08`, for the fixed setup groups. | as above | | $E3A2 | 30 B | `FixedObjectTileTable` | Thirty CHR tile indices, `$F5`-`$FC` — a different CHR family from the variable object table. | as above | | $E3C0 | 30 B | `FixedObjectAttributeTable` | Thirty OAM attribute bytes, chiefly `$02` and `$42` with `$82` and `$C2` variants: the high bits flip the sprite, the low bits select palette 2. | as above | | $E3DE | 30 B | `ObjectFollowupTable` | Thirty bytes, `08 08 00 …`, that begin another sequence. It is indexed by neither of the two audited object-expansion loops, so its consumer is not established. | unreferenced data (ROM provenance: immediately after the fixed setup attribute table) | | $E588 | 21 B | (unnamed) | Twenty-one bytes of the hole-layout data, before the per-hole index table. No instruction in this dump addresses them. | unreferenced data | | $E59D | 18 B | `HoleLayoutIndexTable` | One byte per hole (18 holes). The value selects the hole's entry in the layout tables. | `lda $E59D,y` at `$E6B2`, with Y = `current_hole_index` | | $E5AF | 248 B | (unnamed) | 248 bytes of hole-layout data, between the per-hole index table and the per-hole parameter table. No instruction in this dump addresses them. | unreferenced data | | $E6A7 | 9 B | `HoleLayoutParameterTable` | Nine bytes, the parameters of the hole the index table selected. | `lda $E6A7,y` at `$E6BD` | | $E6C3 | 18 B | `CourseLayoutPointerLo` | The low bytes of the eighteen course-stream pointers: `E7 AF E8 BC 91 F3 C5 BC BA E3 EC 09 BA CF D3 AC C7 EA`. | the course decoder at `$DD78`-`$DE39` | | $E6D5 | 18 B | `CourseLayoutPointerHi` | The matching high bytes, one for each entry of `$E6C3`; the eighteen streams start at $E6E7, $EAAF, $F2E8, $F4BC, $E991, $EEF3, $F0C5, $F7BC, $F6BA, $EFE3, $EDEC, $ED09, $E7BA, $E8CF, $F3D3, $EBAC, $F5C7 and $F1EA (`course_streams.inc` holds their bounds). | as above | | $E6E7 | 4639 B | `CourseLayoutStreamData` | The eighteen compressed course streams. Each stream is a run-length-coded hole map: three header bytes, then commands — `$00`-`$0F` emit a run of header byte 0, `$10`-`$1F` of header byte 1, `$20`-`$2F` of header byte 2, `$A0`-`$AF` emit a cycling three-value run, `$30`-`$9F` and `$B0`-`$FE` are literal bytes, and `$FF` ends the stream. The decoder writes 16 bytes per unit into the 512-byte course buffer at `$0300`. | the course decoder at `$DD78`-`$DE39`, reached through `CourseLayoutPointerLo`/`Hi` | | $FBBF | 101 B | (unnamed) | 101 bytes, mostly CHR tile indices and attribute bytes — `C4 CC D4 DC E4 EC F4 FC 04 0C 14 1C 0C 08 04 00 00 0C 18 24 84 84 84 84 84 C1 C0 84 84 BF BE 84 …`. No instruction in this dump addresses them with an absolute operand. | unreferenced data (ROM provenance: immediately after the routine at `$FBBE`) | | $C71E | 3 B | (unnamed) | Three bytes, `51 7C 5C`, between the NMI epilogue jump and the routine at `$C721`. No instruction in this dump addresses them. | unreferenced data | | $C786 | 5 B | (unnamed) | Five bytes, `0D 0D 0D 0D 06`, immediately before the club table. No instruction in this dump addresses them. | unreferenced data | | $C78B | 194 B | `ClubSwingSpriteTable` | The club's swing animation: the table the club-selection index reads two bytes at a time. The first bytes are the sprite tiles the swing is drawn from, and the later ones are the per-frame offsets and attributes of the swing. | `lda $C78B,y` at `$C858`, with Y = twice `club_selection_index` | | $CD85 | 223 B | (unnamed) | 223 bytes whose values climb (`00 01 02 03 04 05 04 03 06 00 07 08 09 0A 00 04 08 0A 0D 10 12 14 16 19 1C 23 25 2B 33 …`) and then turn into CHR tile indices (`BB BC BE BF C5 C6 C7 …`). It follows the sprite-index lists of `$CC74` and is not reached by any of the audited pointer tables, so the consumer that walks it is not established. | unreferenced data (ROM provenance: immediately after the last sprite-index list at `$CD84`) | | $FD0A | 8 B | `PpuTableFd0a` | Eight PPU register values, `29 01 30 27 29 0F 16 27` — a burst of palette, scroll and mask writes. | the PPU setup code at `$FD0A` | | $FF85 | 117 B | `FF85PpuValueTable` | 117 bytes of PPU values: the first sixteen step by six (`00 06 0C 12 18 00 12 18 1E 24 30 46 36 47 46 4D`) and the rest are attribute and tile bytes for the course graphics. | the PPU upload code that walks it | **How the structures were derived.** The sprite and object tables come from the project's own `SPRITE_OBJECT_TABLE_AUDIT.md`, which traced the two parsers that consume them; the parallel arrays and their lengths follow from the offsets the parsers add. The course streams follow from `COMPRESSED_PARSER_AUDIT.md`, which decoded the command set of the run-length decoder at `$DD78`; the eighteen stream bounds and the pointer table that selects them are in `course_streams.inc` and at `$E6C3`/`$E6D5`. The three spans that were split further — `$C78B`, `$D82E` and `$E59D`/`$E6A7` — were split because an instruction in this dump names their address: `lda $C78B,y` at `$C858`, `lda $D82E,y` at `$D7F8`, `lda $E59D,y` at `$E6B2` and `lda $E6A7,y` at `$E6BD`. The remaining `.incbin` spans are recorded with their provenance and the value structure that is visible in them; none of them is addressed by an absolute operand in this dump, and the parsers that the audit documents identify reach their data through pointers, so their exact consumers are recorded as not established rather than guessed. **The eight short `.byte` runs that are not tables.** `$C199-$C19A`, `$D425-$D426`, `$D9D9-$D9DA`, `$D9FA-$D9FB`, `$DF3B-$DF3C`, `$FB25-$FB26`, `$FB84-$FB85` and `$FF01-$FF02` are separate `.byte` rows in `prg.s` only because the tracer's linear walk happened to end there; each of them decodes as one or two 6502 instructions (`lda #$00` / `sta $49`, `lda #$40` / `clc` / `adc $45`, `sec` / `php` / `jsr $D906`, `lda #$FF` / `sta $74` / `rts`, `lda #$02` / `sta $0200,x` / `inx`, `lda #$B8` / `sta $C0,x` / `dec $6F`, `lda #$FF` / `sta $0288`, `lda #$00` / `asl a` / `sta $400A`). They are code the tracer could not reach, not data, and they are not listed as tables above. --- ## Next Steps 1. **Pass 1** — done (byte-identical whole-ROM build, 4,217 instructions). 2. **Pass 2** — done: all 112 subroutine entries named (4 semantic + 108 `SubC`); all jsr/jmp call sites symbolic (0 raw `$XXXX` call operands); 339 of 349 internal branch targets given role-based names (`_Loop`, `_OnZero`, …); 10 fall-through-only labels keep their `Label_` definitions. 3. **Pass 3** — done: audit comments canonical at column 35 (4,156 re-aligned); all hardware-register operands use `symbols.inc` names; 275 zero-page operands substituted with `locals.inc` aliases (0 residue); every branch and jump target is symbolic; every data region is interpreted and recorded in the *Pass 3 data-table inventory* above, with the five sub-tables that an instruction names split out of their `.incbin` spans. 4. **Promotion** — `git mv "_wip/NROM/Golf (JU)" "NROM/Golf (JU)"` once the controlling agent approves, then add the row to the root `README.md` inventory and remove this entry here. The whole-ROM build is already in place, so promotion needs no build surgery.