# Zanac (U) ## Status **Pass 3 complete.** All address-derived `Label_XXXX` names have been replaced with semantic entry-point names (133 routines) plus address-based names for remaining branch targets (~212 `Sub_XXXX`/`Next_XXXX`/`Skip_XXXX`/`Exit_XXXX`). All raw `$XXXX` jsr/jmp operands replaced with label references. Zero-page aliases renamed to semantic names (14 named). Every data block in the image is now accounted for: 47 blocks are named for what they are, and the **Pass 3 data-table inventory** below gives the address range, the size, the structure of the bytes and the consuming instruction for all 104 blocks the tracer emitted - the rest are recorded as code the tracer could not reach or as unreferenced data with their ROM provenance. The project builds and verifies byte-identical to the original ROM. The 94 entry points that the bank-6 and bank-7 code still reached by address are now named as well. Bank 6 is the enemy bank, so its routines are named for what the enemies do (`MoveEnemyX`, `MoveEnemyY`, `StepEnemyTowardsTargetX`, `TestEnemyOverlap`, `SelectEnemyAiDirection`, `UpdateEnemyAiState`, `SpawnEnemy`, `SpawnEnemyWave`, `SelectEnemySpriteType`, `UpdateEnemyWaveTimer`, `SpawnEnemyProjectile`, ...), and the twelve routines that bank 6 exports to the engine in bank 7 carry an `Export` prefix (`ExportUpdateEnemyWave`, `ExportSelectEnemySlot`, ...). Bank 7's three remaining entries are named for what they do (`WriteVramBlock`, `SetPpuMaskAndNmiFlag`, `UpdatePpuAndMask`). No entry point is left with an address-derived name. `make` assembles `header.s`, `prg.s`, `prg.07.s`, links them with `ld65` and compares the result against `rom.nes`: ``` Byte-identical ROM verified. ``` ### Pass 1 results | Metric | Value | |--------|-------| | PRG size | 131072 bytes (128 KB) at file offset $0010 | | Code | 58,381 bytes (44.5% of the image) | | Instructions | 6,206 in bank 7, 2,103 in bank 6 | | Bank 7 routines | 391 labels traced | | Bank 6 routines | 466 labels traced | | Banks 0-3 | empty: every byte is `$00` | | Banks 4-5 | stage data: the CPU reads them and never fetches them | ### Pass 2 results | Metric | Value | |--------|-------| | Semantic entry-point names | 133 (Reset, NmiHandler, InputRead, ObjInitCall, ObjMoveCall, ObjDrawCall, ObjUpdateCall, SetOamPtrs, WaitVblank, GameModeDispatch, etc.) | | Internal branch labels | ~212 (`Sub_XXXX`/`Next_XXXX`/`Skip_XXXX`/`Exit_XXXX`) | | Raw `$XXXX` jsr/jmp operands | 0 (all replaced with label references) | | Raw `$XXXX` branch operands | 0 (all replaced with label references) | | Remaining `Label_XXXX` | 0 (all replaced) | ### Pass 3 results | Metric | Value | |--------|-------| | Semantic zero-page aliases | 14 (Reset, NmiHandler, InputRead, ObjInitCall, ObjMoveCall, ObjDrawCall, ObjUpdateCall, SetOamPtrs, WaitVblank, GameModeDispatch, etc.) | | Named data tables | 47 (CmdHandlerJumpTable, SpriteAttrTable, NametableRowAddrTable, PpuAttrInitScript, StaffCreditsText, ApuChannelMaskTable, etc. - see **Pass 3 data-table inventory**) | | Data blocks interpreted | 104 of 104 (47 named blocks across 32 rows; 55 blocks recorded as code the tracer could not reach; the rest recorded as data with its structure and its ROM provenance) | | Raw `$XXXX` jsr/jmp operands | 0 | | Raw `$XXXX` branch operands | 0 | | Remaining `Label_XXXX` | 0 | ### Pass 1 notes - Bank 7 (last 16 KB, fixed at CPU $C000-$FFFF) was traced from vectors: NMI=$CE8D, RESET=$E7BF, IRQ=$CEC4. Bank 7 code is assembled separately as `prg.07.s` with `.org $C000` and linked at file offset $1C010. - Bank 6 (the switchable bank at CPU $8000-$BFFF) is traced too, from a Mesen Code Data Logger capture of a real session: the capture's jump targets and code-run starts seed the tracer, and the capture's Data flag forces the stage data back to data. It is assembled as `prg.6.s` with `.org $8000` and linked at file offset $18010 - it needs a segment of its own, because a call inside it must assemble to the CPU address and not to the file offset. Its labels carry the `Bank6_` prefix, because it shares its CPU window with the other switchable banks. - Banks 0-3 are empty: every byte of the image in them is `$00`, so the linker fills them and the source says so. - Banks 4-5 hold the stage data: the CPU reads them and never fetches a byte from them as an opcode, which is what the Code Data Logger records. - The bank switch is an indirect store: `sta (Zp00,x)` at $C7B9 with the pointer built from the caller's X, so the bank number is the high byte of a zero-page pointer rather than an immediate. - Seven invalid opcode addresses found: $D506, $D876, $E15B, $E44B, $E520, $E909, $F0FE (likely data mis-decoded as opcodes or vice versa) - Every row's audit comment carries the offset of the byte in the whole PRG image, so a single lookup in `prg.bin` finds every byte of the project - including bank 7, whose rows used to be numbered from the start of the bank. ## Pass 3 data-table inventory Every data block the tracer emitted, with what its bytes are and the instruction that reads them. The image is banked: **bank 6** is the switchable bank at CPU `$8000-$BFFF` (`prg.6.s`), **bank 7** is the fixed bank at CPU `$C000-$FFFF` (`prg.07.s`), and **banks 0-5** live in `prg.s` (their "CPU" field in the audit comment is really the PRG offset). Every address below is therefore given with its bank. "Not referenced by any instruction" means no operand in this dump lands inside the block; such a block is either **unreferenced data** (recorded with its ROM provenance) or **code the tracer could not reach** (its bytes decode as a run of instructions and the Mesen CDL capture recorded them fetched as opcodes). The CDL column records what `zanac_gameplay.cdl` saw at those addresses: `code` = fetched as an opcode, `data` = read as data, `untouched` = never touched. ### Banks 0-5 (`prg.s`) | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$0000-$3FFF` (bank 0) | 16384 B | `-` | unreferenced data - the whole bank is `$00`; the CPU never fetches or reads it | not referenced by any instruction | | `$4000-$7FFF` (bank 1) | 16384 B | `-` | unreferenced data - the whole bank is `$00` | not referenced by any instruction | | `$8000-$BFFF` (bank 2) | 16384 B | `-` | unreferenced data - the whole bank is `$00` | not referenced by any instruction | | `$C000-$FFFF` (bank 3) | 16384 B | `-` | unreferenced data - the whole bank is `$00` | not referenced by any instruction | | `$10000-$13FFF` (bank 4) | 16384 B | `-` | the first stage's data bank: the first `$30` bytes are `$FF` padding, then at `$8030` a table of 16-bit pointers to the bank's own graphic blocks, then 16 KB of tile patterns - from `$12A40` the bytes are 1-bit-per-pixel runs (`14 28 50 A0 40 03 04 0C 18 30 60 80 80 40 A0 50`, a shrinking triangle), and from `$12B30` a table of 16-bit scale factors (`42 08 42 08 42 08 42 14 ...` reading as `$0842, $0842, $0842, $1442`, then `08 00 20 00 80 00 02 00` = 8, 32, 128, 2). The game has no CHR ROM, so the tile patterns must come from here; CDL: 9609 of 16384 bytes read as data | `lda $8030,y` / `lda $8031,y` at `$CC7E`/`$CC83` (bank 7) read the pointer table of whichever bank is switched in; CDL: 9609 bytes read as data, 6775 untouched | | `$14000-$17FFF` (bank 5) | 16384 B | `-` | the second stage's data bank, the same shape as bank 4: `$30` bytes of `$FF` padding, a 16-bit pointer table at `$8030`, and 16 KB of tile data; CDL: 2855 bytes read as data | `lda $8030,y` / `lda $8031,y` at `$CC7E`/`$CC83` (bank 7) read the pointer table of whichever bank is switched in; CDL: 2855 bytes read as data, 13529 untouched | ### Bank 6 - the switchable bank at `$8000-$BFFF` (`prg.6.s`) | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$8121-$818B` | 107 B | `EnemySizeThresholdTable, EnemySizeClassTable, EnemyMoveDeltaTable` | three tables and a code stub: a 3-entry ascending threshold table `$32,$6A,$AB` at `$8121`, a 4-entry class table at `$8124` (`$02,$03,$03,$04`) followed by 28 further one-byte values to `$8143`, and sixteen 4-byte records at `$8144`, each of them two 16-bit signed movement deltas - the X delta first, then the Y delta - which the code shifts left by three bits before adding them to the object's 16-bit position. The last eight bytes (`$8184-$818B`) are a copy of the routine at `$818C` | `cmp $8120,y` at `$8085` (thresholds), `lda $8124,y` at `$8091` (class), `lda $8144,y`/`$8145,y`/`$8146,y`/`$8147,y` at `$809E`/`$80A3`/`$80A8`/`$80AD` (deltas) | | `$81EA-$81EC` | 3 B | `-` | code the tracer could not reach - `BCS $820C` / `RTS`; CDL: 3/3 bytes fetched as code | not referenced by any instruction | | `$81F3-$81F5` | 3 B | `-` | code the tracer could not reach - `BCS $820C` / `RTS`; CDL: 3/3 bytes fetched as code | not referenced by any instruction | | `$81FC-$8202` | 7 B | `-` | code the tracer could not reach - `BCS $820C` / `JMP $8250` / `BCS $820C`; CDL: 7/7 bytes fetched as code | not referenced by any instruction | | `$85A8-$85C0` | 25 B | `-` | code the tracer could not reach - a 13-instruction sequence that shifts the enemy's position and calls `$85D8`; CDL: 17/25 bytes fetched as code | not referenced by any instruction | | `$85CE-$85D7` | 10 B | `-` | code the tracer could not reach - `LDA #$84 / STA $18 / LDA #$00 / STA $19 / LDY #$00`; CDL: untouched | not referenced by any instruction | | `$8607-$860B` | 5 B | `-` | code the tracer could not reach - `INC $14 / BNE $8602 / RTS`; CDL: 5/5 bytes fetched as code | not referenced by any instruction | | `$8619-$872A` | 274 B | `-` | a 274-byte pixel-data block: from `$8679` the bytes are a rising ramp of one-byte values ($96-$C3) that step by one every few entries, from `$86C9` a field of `$23` with a handful of outliers ($A9, $50, $37, $3C, $28, $48, $53, $5B), and from `$8619` a run of high values that is read as the byte plane of a 2-bit-per-pixel image. CDL: 208 bytes read as data | `lda $86CF,y` at `$8216` and `$8229` (the pixel values), `eor $8619,x` at `$8731` and `adc $8619,y` at `$8752` (the plane XORed and added into the object's position) | | `$87AD-$87C6` | 26 B | `-` | a 26-byte index permutation: `$00,$01,$02,$03,$04` then `$19` down to `$05` in steps of one - a reversed slot order used to walk the object list back to front; CDL: 26/26 bytes read as data | not referenced by any instruction - the permutation is indexed by a computed value, so no operand lands inside the block | | `$87D3-$8821` | 79 B | `-` | code the tracer could not reach - 40 instructions that pick the larger of two positions and advance a sprite; CDL: 68/79 bytes fetched as code | not referenced by any instruction | | `$88F8-$890B` | 20 B | `-` | code the tracer could not reach - `SEC / BCS / RTS` followed by more code; CDL: 4/20 bytes fetched as code | not referenced by any instruction | | `$894D-$8C67` | 795 B | `-` | a mixed block: `$894D-$895F` is code, `$8960-$89AD` is a 16-byte record table of 16-bit values that step by two ($F8, $FA, $FC, $FE) against a second value that steps by nine ($60, $69, $72, $7B) - a table of (position, value) pairs - and the rest is a 3-byte-record table of tile indices and attribute nibbles. CDL: 466 bytes read as data, 329 untouched | `lda $89B1,y` at `$885D`, `lda $89B2,y` at `$8862` and `lda $89B4,y` at `$886F` pick one of the three 16-byte records at `$89B1`; the record's 16-bit field is the address of a pixel stream | | `$8E64-$8EA0` | 61 B | `EnemySpriteTypeTable` | `$8E64-$8E73` is code; `$8E74-$8E85` is an 18-entry table of the enemy's sprite type (`$FF,$FF,$FF,0,0,0,1,1,1,6,8,$0A,4,$0C,$0C,2,0,$0E`) and `$8E86-$8E9B` is a 22-entry table of the same field's non-zero values ($30-$34 = the five enemy types). CDL: 35 bytes read as data, 16 fetched as code | `lda $8E74,y` at `$8CB0`, `lda $8E86,y` at `$8CE0` (the type byte that goes to `$0576`), `lda $8E7D,y` at `$8CEC` and `$913E`, `lda $8E91,y` at `$8D67`, `lda $8E92,y` at `$A90B` | | `$8EB8-$8EC9` | 18 B | `EnemyMoveVectorTable` | six 3-byte records - a movement vector of two signed bytes plus a third byte (`FB 02 01`, `F9 03 01`, `F8 02 02`, `F8 03 02`, `F7 02 03`, `F7 03 03`); the first two bytes of each record are the X and Y delta and the third the speed class | `lda $8EB8,y` at `$8EA8`, `lda $8EB9,y` at `$8EAD` and `lda $8EBA,y` at `$8EB2` (the index is `Zp33 * 3`) | | `$8F65-$8F76` | 18 B | `-` | code the tracer could not reach - `LDA #$16 / STA $85 / LDA $055C,x / STA $87 / LDY #$00 / JSR $85D8 / INC $14 / BNE $8F65`; CDL: untouched | not referenced by any instruction | | `$8F99-$8FF5` | 93 B | `-` | code the tracer could not reach - 44 instructions with no invalid opcode; CDL: 93/93 bytes fetched as code | not referenced by any instruction | | `$9021-$9038` | 24 B | `EnemySpriteTypeTable, EnemySpriteParamTable` | two 12-entry tables: the sprite type of each of the twelve enemy shapes (`$05,$06,$06,$07,$07,$08,$08,$07,$07,$06,$06,$05`) and a second parameter per shape (`$00,$01,$02,$03` repeated three times); CDL: 24/24 bytes read as data | `lda $9021,y` at `$9000` (the type, stored to `$0576`) and `lda $902D,y` at `$9006` (the parameter, stored to `$0590`) | | `$90CD-$90DC` | 16 B | `-` | eight 16-bit code addresses ($9139, $91A0, $91E5, $925B, $92F0, $935B, $940F, $914C); the CPU read the first, third, fourth, fifth, sixth and eighth as data (CDL: 8/16 bytes read as data) | not referenced by any instruction - the reader computes the address | | `$90E2-$9139` | 88 B | `-` | a table of sixteen 16-bit code addresses ($919A, $9171, $91EA, $9277, $932E, $9371, $932E, $9469) followed by 64 bytes of length-prefixed ASCII ("68:", "68@", "=AB", "68:", ">?@", "68:", "666") interleaved with control bytes ($83, $C1, $C3, $C2, $85, $FE, $FF); CDL: 28 bytes read as data | not referenced by any instruction | | `$916A-$9171` | 8 B | `-` | code the tracer could not reach - `JSR $906E / BNE $9498 / JMP $948B`; CDL: untouched | not referenced by any instruction | | `$91E4-$91E5` | 2 B | `-` | unreferenced data - two bytes, `$0D $0D`; CDL: untouched | not referenced by any instruction | | `$9252-$925B` | 10 B | `EnemyWaveParamTable` | ten one-byte values in pairs (`$3C,$3C,$3D,$3D,$3E,$3E,$41,$41,$42,$42`) - a per-wave parameter (the enemy's vertical speed or health step); CDL: 6/10 bytes read as data | `lda $9252,y` at `$9246` (the index is the enemy's type divided by sixteen, plus one when the object is in its second half) | | `$92F1-$9434` | 324 B | `-` | code the tracer could not reach - 141 instructions that move an object along a path, decrement a counter at `$0716` and branch through `$93FE`; CDL: untouched, so the capture never ran it | not referenced by any instruction | | `$943F-$9463` | 37 B | `-` | eight 16-bit code addresses ($82FD, $9171, $9463, $9277, $944E, $9371, $946E, $9169) followed by code; CDL: 8 bytes read as data | not referenced by any instruction | | `$946F-$948A` | 28 B | `-` | code the tracer could not reach - `LDA $60 / CMP #$05 / BCC / LDA #$3E / JSR $B2EF / JSR $948B / JMP $9483 / ...`; CDL: untouched | not referenced by any instruction | | `$9578-$958D` | 22 B | `-` | three small tables: three bytes at `$9578` (`$00,$00,$01`), six bytes at `$9580` (`$32,$50,$64,$78,$8C,$50` - a rising ramp) and eight bytes at `$9586` (`$02,$03,$01,$01,$03,$03,$03,$03`); CDL: 8 bytes read as data | `lda $9586,y` at `$9494`, `lda $9578,y` at `$94E4`, `ora $9578,y` at `$94ED` and `lda $9580,y` at `$955A` | | `$95D9-$9614` | 60 B | `-` | two 30-entry tables of one-byte values: `$95D9` holds `$04-$06` per enemy type and `$95F7` holds `$01,$21,$41,$11` - a per-type movement/appearance code; CDL: 9 bytes read as data | `lda $95D9,y` at `$95AD` and `lda $95F7,y` at `$95C1` | | `$96DC-$96E7` | 12 B | `-` | a 12-byte table of small values ($2F, $80, $2F, $02, $2F, $01, $2F, $02, $2E, $01, $2F, $02) - a per-enemy movement pattern; CDL: 12/12 bytes read as data | not referenced by any instruction - the reader computes the address | | `$97BA-$97CE` | 21 B | `-` | thirteen one-byte values (`$01,$06,$03,$07,$02,$05,$04,$19,$19,$1A,$02,$00,$03`) followed by code; CDL: 9 bytes read as data | not referenced by any instruction | | `$97E7-$97F2` | 12 B | `-` | code the tracer could not reach - `LDY $15 / LDA #$83 / STA $14 / JSR $97F3 / JMP $97B4`; CDL: untouched | not referenced by any instruction | | `$97FD-$9828` | 44 B | `-` | code the tracer could not reach - 18 instructions that move a shot and update the score; CDL: untouched | not referenced by any instruction | | `$98C6-$9917` | 82 B | `-` | `$98C6-$98E3` is code; `$98E4-$98F6` is code; `$98F7-$9917` is a 51-byte table of one-byte values ($08,$08,$41,$06,$24,$1E,$1E,$0A,$1E,$42,$02,$78,$3A,...) - a per-enemy behaviour table; CDL: 15 bytes read as data | `lda $98F7,x` at `$9927`, `lda $98F8,x` at `$992D` and `lda $98F9,x` at `$9933` | | `$99D0-$9E98` | 1225 B | `-` | 48 bytes of data at the head (`$00,$13,$90,$FF,$00,$68,$68,$27,$26,$25,$24,$23,$22,$21,$40,$2F,$2E,$2D,$2C,$2B,$AA,$00,$E8,$01,$76,$35,$34,$33,$42,$33,$44,$36,$38,$7B,$2A,$28,$26` - a descending ladder of one-byte values) followed by 1177 bytes of code the tracer could not reach, including the string "HIDEJ" at `$9D13`; CDL: 38 bytes read as data | not referenced by any instruction | | `$9F49-$9F6B` | 35 B | `-` | code the tracer could not reach - 14 instructions that set up a new object; CDL: untouched | not referenced by any instruction | | `$9FBC-$9FD5` | 26 B | `-` | a 26-byte command stream: `20 E0 02 02 01 07`, `20 E0 03 03 01 07`, `60 A0 80 80 FF 00 E0 FF 20 01 C8 FD 38 03` - opcode `$20` takes a pointer and two bytes, `$60` a count and a block of data; CDL: 13 bytes read as data | `lda $9FBC,y` at `$9EC6` and `$9EE7`, `lda $9FBE,y` at `$9ECC`, `lda $9FC0,y` at `$9EDF` and `$9EF5`, `lda $9FCE,y` at `$9FF9` and `lda $9FCF,y` at `$9FFF` | | `$A054-$A05D` | 10 B | `-` | two small tables: five bytes at `$A054` (`$1F,$20,$21,$20,$1F` - a sprite's horizontal animation range) and four bytes at `$A059` (`$00,$00,$00,$80`); CDL: 10/10 bytes read as data | `lda $A054,y` at `$A038` and `ora $A059,y` at `$A043` | | `$A06D-$A076` | 10 B | `-` | code the tracer could not reach - four instructions; CDL: 10/10 bytes fetched as code | not referenced by any instruction | | `$A08A-$A0BA` | 49 B | `-` | code the tracer could not reach - 20 instructions that spawn a shot; CDL: untouched | not referenced by any instruction | | `$A165-$A263` | 255 B | `-` | seven bytes of data at the head (`$11,$12,$13,$14,$02,$22` and a spare) followed by 248 bytes of code the tracer could not reach; CDL: 6 bytes read as data | not referenced by any instruction | | `$A3EC-$A508` | 285 B | `-` | code the tracer could not reach - 108 instructions that walk the enemy list and update each enemy's position; CDL: untouched | not referenced by any instruction | | `$A601-$A608` | 8 B | `EnemyInitParamTable` | four pairs of one-byte values: `$02,$80`, `$01,$08`, `$03,$00`, `$08,$04` - the four parameters (palette, flags, timer and variant) a newly spawned enemy is given; CDL: 4/8 bytes read as data | `lda $A601,y` at `$A519`, `lda $A602,y` at `$A527`, `lda $A603,y` at `$A52D` and `lda $A604,y` at `$A533` | | `$A6A5-$A6C0` | 28 B | `-` | code the tracer could not reach - 11 instructions that move a shot; CDL: untouched | not referenced by any instruction | | `$A74A-$A74B` | 2 B | `EnemyAltSpriteTypeTable` | two bytes, `$1D` and `$1E` - the alternative sprite type an enemy switches to on its second animation frame; CDL: 2/2 bytes read as data | `lda $A74A,y` at `$A73A` (the index is bit 2 of the frame counter) | | `$A767-$A775` | 15 B | `-` | code the tracer could not reach - six instructions; CDL: 15/15 bytes fetched as code | not referenced by any instruction | | `$A806-$A820` | 27 B | `-` | code the tracer could not reach - 12 instructions; CDL: 18/27 bytes fetched as code | not referenced by any instruction | | `$A8E5-$A8E8` | 4 B | `-` | code the tracer could not reach - two instructions; CDL: 4/4 bytes fetched as code | not referenced by any instruction | | `$A97E-$AA61` | 228 B | `-` | seven bytes of data at the head (`$06,$07,$08,$08,$07,$06,$05` - a rising and falling ramp) followed by 221 bytes of code the tracer could not reach; CDL: 7 bytes read as data | `lda $A97E,y` at `$A971` | | `$AAA8-$AC47` | 416 B | `-` | code the tracer could not reach - 90 instructions that move the player's shot; CDL: untouched | not referenced by any instruction | | `$AC83-$AE98` | 534 B | `-` | 32 bytes of data at the head - a 16-entry ramp (`$09,$0A,$0B,$0C,$0D,$0C,$0B,$0A` twice) and sixteen tile indices in four groups of four (`$00,$01,$02,$03`, `$00,$41,$42,$43`, `$00,$C1,$C2,$C3`, `$00,$81,$82,$83`) - followed by 502 bytes of code the tracer could not reach; CDL: 32 bytes read as data | `lda $AC83,y` at `$AC71` and `lda $AC93,y` at `$AC77` | | `$AEB8-$AEC0` | 9 B | `-` | code the tracer could not reach - four instructions; CDL: 9/9 bytes fetched as code | not referenced by any instruction | | `$AF9A-$B099` | 256 B | `-` | a 256-byte block of code the tracer could not reach with a 24-byte table of one-byte values at the head (`$06,$06,$00,$00,...,$04,$00,$04,$04,$04,$08,$03,$03,$08,$08,$08,$00`); CDL: 8 bytes read as data | not referenced by any instruction | | `$B1CD-$B1E1` | 21 B | `-` | code the tracer could not reach - 12 instructions that add a random value to the score; CDL: untouched | not referenced by any instruction | | `$B260-$B630` | 977 B | `-` | code the tracer could not reach - 432 instructions with no invalid opcode, the largest single block of unreachable code in the bank; the head is eight bytes of the same `$FC,$F3,$CF,$3F,$03,$0C,$30,$C0` bit-mask pattern as `$D7DB`; CDL: 2 bytes read as data | not referenced by any instruction | | `$B646-$B6B5` | 112 B | `-` | 30 bytes of data at the head (`$07,$28,$7F,$07,$1E,$06,$06,$0A,$08,$07,$14,$06,$07,$14,$06,$07,$28,$06,$50,$63,$12,$00,$01,$01,$02,$01,$01,$00,$01,$02,$03,$02,$01` - a per-enemy spawn table) followed by 82 bytes of code the tracer could not reach; CDL: untouched | not referenced by any instruction | | `$B6F8-$B71F` | 40 B | `-` | code the tracer could not reach - 18 instructions; CDL: 29/40 bytes fetched as code | not referenced by any instruction | | `$B729-$B87E` | 342 B | `-` | code the tracer could not reach - 64 instructions from `JMP $82FE`; CDL: 3 bytes fetched as code | not referenced by any instruction | | `$B8AD-$B8E4` | 56 B | `-` | sixteen 3-byte records (`$04,$30,$03`, `$04,$30,$02`, `$04,$20,$04`, `$04,$20,$03`, `$04,$1C,$03`, `$04,$30,$04`, `$04,$28,$05`, `$04,$38,$02`, `$05,$40,$08`, `$0A,$30,$06`, `$04,$10,$09`, `$03,$20,$05`, `$08,$20,$08`, `$04,$28,$06`, `$03,$20,$08`, `$06,$50,$06` - a tile, a speed and a timer per record) followed by eight bytes ($06,$16,$17,$26,$27,$26,$17,$16); CDL: 8 bytes read as data | `lda $B8DD,y` at `$B901` (the eight-byte table at the end of the block) | | `$B951-$B95E` | 14 B | `-` | seven 16-bit values - $0040, $0080, $00C0, $0100, $0180, $0200, $0280 - a ladder of nametable row offsets in steps of $40; CDL: untouched | not referenced by any instruction | | `$BA17-$BE07` | 1009 B | `-` | code the tracer could not reach - 130 instructions that update the scroll position and the background; CDL: untouched | not referenced by any instruction | | `$BE41-$BFFF` | 447 B | `-` | unreferenced data - 447 bytes of `$FF` padding at the end of the bank; CDL: untouched | not referenced by any instruction | ### Bank 7 - the fixed bank at `$C000-$FFFF` (`prg.07.s`) | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$C000-$C6AA` | 1707 B | `CmdHandlerJumpTable, PpuAttrSlotTable, SpriteAttrTable, NotePitchTable` | five blocks: (1) `$C000-$C08F` - 48 three-byte `JMP abs` trampolines, entry *n* at `$C000+3n`; the Mesen CDL records the CPU fetching 25 of the 48 opcodes, so the table is entered by a computed jump whose index arithmetic lives in code the tracer could not reach, and the targets are the game's engine routines (the reset entry `$E7BF`, the PPU attribute writer `$C815`, the object handlers `$C6AB`/`$C768`/`$C8BA`/`$C8DF`, the sound driver `$EF71`/`$EF79`/`$EF90`); (2) `$C090-$C0EF` - 96 bytes of `$FF` padding; (3) `$C0F0-$C0FF` - eight `$FF` bytes then eight counters `$00-$07`; (4) `$C100-$C19F` - 160 one-byte sprite attributes, of which `$C12F-$C133` are the half-widths of the five enemy types ($30-$34) and `$C182-$C186` their half-heights; (5) `$C1A0-$C23F` - a 160-byte pitch ladder whose values step up by 5, 9, 13, 17, 25 and 33 and whose last 64 bytes count `$C2` to `$C6` in runs | (1) the table is entered by a computed jump, not by a direct operand; (2) none; (3) `lda $C0F8,y` at `$C81B` and `sta $C0F8,y` at `$C81E` (and again at `$C8AF`/`$C8B2` in the unreached routine at `$C87F`); (4) `lda $C0FF,y` at `$81AF` in bank 6 (the half-width) and `lda $C152,y` at `$81CB` (the half-height); (5) none | | `$C6CA-$C6D2` | 9 B | `-` | code the tracer could not reach - `PLA / STA $16 / PLA / STA $17 / JSR $C6EA`; CDL: untouched | not referenced by any instruction | | `$C725-$C767` | 67 B | `-` | code the tracer could not reach - a routine that pushes the object pointer, calls the object driver at `$C8EB` and `$C7F8` and returns through `$C6D3`; CDL: untouched | not referenced by any instruction | | `$C824-$C864` | 65 B | `-` | code the tracer could not reach - five near-identical five-instruction sequences, each of which reads the counter at `$C0F8+n` and writes it back unchanged; CDL: untouched | not referenced by any instruction | | `$C87F-$C8B9` | 59 B | `-` | code the tracer could not reach - 35 instructions that copy a three-byte record out of the object and update the counter at `$C0F8`; CDL: untouched | not referenced by any instruction | | `$C928-$C937` | 16 B | `TextRowBaseTable` | two parallel eight-entry tables: the low bytes of the screen row base addresses (`$30` seven times then `$00`) and the high bytes (`$80` seven times then `$C1`); the code forms the VRAM address `$C930[y]*256 + $C928[y] + A*2`, so rows 0-6 write into the switchable bank at `$8030` and row 7 into the fixed bank at `$C100` | `adc $C928,y` at `$C8ED` and `lda $C930,y` at `$C8F1` (inside `VramWrite`) | | `$C95C-$CA9C` | 321 B | `-` | code the tracer could not reach - 166 instructions that walk the enemy list, compute an object's screen position from the scroll registers and store it; CDL: untouched | not referenced by any instruction | | `$CC16-$CC35` | 32 B | `StageInitValueTable, StageInitCountTable` | two parallel 16-entry tables: a value (`$07,$00,$01,$02,$03,$04,$05,$06,$07,$00,$01,$02,$03,$04,$06,$05`) and a count (`$06,$02,$01,$04,$01,$02,$03,$01,$03,$01,$03,$02,$06,$03,$00,$0C`); the code calls the routine at `$9493` with the value in A, the count times | `lda $CC16,y` at `$CC00` and `lda $CC26,y` at `$CC05` | | `$CCAD-$CCE2` | 54 B | `TitleObjParamTable, TitleObjXIndexTable, TitleObjYIndexTable` | three parallel 13-entry tables - the screen layout each of the thirteen title-screen objects uses (`$06,$04,$04,$04,$04,$05,$05,$05,$05,$06,$06,$06,$06`), its X index (`$1E,$1A,$1A,$1A,$1A,$1C,$1C,$1C,$1C,$1E,$1E,$1E,$1E`) and its Y index (`$24,$20,$20,$20,$20,$22,$22,$22,$22,$24,$24,$24,$20`) - followed by 21 bytes of code the tracer could not reach (`ORA ($CB),Y / RTS / LDY #$00 / STA $0104,Y` and a 128-byte fill loop) | `lda $CCAD,y` at `$CC6C`, `ldx $CCBA,y` at `$CC8A` and `ldx $CCC7,y` at `$CC97` (the index is the object slot in `Zp91`) | | `$CCF6-$CD6A` | 117 B | `-` | code the tracer could not reach - 40 instructions at the head (the PPU scroll and mask update) followed by a 36-byte table of one-byte values that is a jump/command table (`$04,$07,$01,$04,$03,$06,$00,...`); CDL: 66 bytes fetched as code, 41 read as data | not referenced by any instruction | | `$D108-$D2C7` | 448 B | `-` | code the tracer could not reach from `$D118` to `$D1DB`, then a 76-record table of three-byte values at `$D1DC` ($00-$14) and nineteen 8-byte bit-mask records at `$D240` (one non-zero byte each: $01, $06, $01, $02, $03, $05, $08, $01, $02, $04, $05, $08, $01, $03, $04, $05, $07, $01, $02), then two four-byte code stubs at `$D2C0`; CDL: 114 bytes fetched as code, 42 read as data | not referenced by any instruction | | `$D2FD-$D346` | 74 B | `-` | code the tracer could not reach - 36 instructions that spawn an object at a position taken from the enemy list; CDL: untouched | not referenced by any instruction | | `$D506-$D56A` | 101 B | `-` | code the tracer could not reach - 51 instructions, of which the first 24 bytes are a table of four one-byte values read as data; CDL: 24 bytes fetched as code | not referenced by any instruction | | `$D61F-$D69E` | 128 B | `EnemyOverlapTable, EnemyOverlapBandTable, EnemyOverlapSizeTable` | three parallel 16-entry tables of one-byte values: `$D61F` ($10,$14,$20,$12,$28,$10,$08,$18,$30,$28,$24,$04,$08,$20,$10,$28) is indexed by (value-16)/4, `$D63B` ($01,$02,$01,$01,$02,$02,$01,$02,$01,$02,$03,$02,$03,$02,$04,$05) and `$D64B` ($30,$38,$3C,$34,$38,$3C,$30,$34,$40,$3C,$30,$38,$33,$2E,$2C,$1C) by value/16; the routine that reads them keeps the largest result at `$50`. The last four bytes of the block (`$B9,$5B,$D6,$60`) are code the tracer could not reach | `lda $D61F,y` at `$D5F0`, `lda $D63B,y` at `$D609` and `lda $D64B,y` at `$D617` | | `$D74B-$D752` | 8 B | `-` | unreferenced data - four bytes `$00,$55,$AA,$FF` (the standard spread of a two-bit value into a byte) followed by `$AA,$FF,$AA,$FF`; the spread table is the shape the bit-plane expansion at `$DB58` needs, but no operand in this dump lands inside the block, so the reader is not established; CDL: 8/8 bytes read as data | not referenced by any instruction | | `$D7BD-$D7E2` | 38 B | `NametableRowAddrTable, SpritePixelMaskTable` | fifteen 16-bit nametable row addresses ($2000, $2040, $2080, $20C0, $2100, $2140, $2180, $21C0, $2200, $2240, $2280, $22C0, $2300, $2340, $2380 - every second row of the 30-row nametable) followed by eight mask bytes ($FC, $F3, $CF, $3F, $03, $0C, $30, $C0 - four masks and their complements, used to fold a position into a 4- or 16-pixel cell) | `lda $D7BD,y` at `$D7B2` and `lda $D7BE,y` at `$D7B7` (the row address), `and $D7DB,y` at `$DB62` and `lda $D7DF,y` at `$DB67` (the masks) | | `$D876-$DA35` | 448 B | `ObjFieldCopyPtrTable` | eight 16-bit code addresses at the head ($D893, $D8A0, $D8BE, $D8DB, $D91F, $D937, $D9DF, $DA15) pointing at the eight field-copy handlers that follow - each starts `LDY $16 / LDA ($B5),Y` and copies a different group of bytes out of the object record into the zero page, and each ends in a jump to the shared tail at `$DA28`; CDL: 322 bytes fetched as code, 10 read as data | the eight handlers, reached through the pointer table; the table itself is read by code the tracer could not reach | | `$DBC5-$DCC4` | 256 B | `ObjPatternTable` | a 256-entry table of one-byte values, sparsely filled: 23 live entries at `$DBC5` ($00,$00,$01,$01,$02,$02,$02,$02,$03,$03,$00,$00,$0F,$04,$05,$05,$04,$03,$08,$0B,$0E,$02,$02,$03,$03,$03), 70 zeros, then 70 live entries at `$DC45` ($0C,$0D,$0D,$0C,...) and the rest zeros; the two bytes the code reads are packed into a twelve-bit value | `lda $DBC5,x` at `$DB88` and `$DB95` (the index is the table at `$0189`) | | `$DCDE-$DD25` | 72 B | `CharCodeTable, ObjXOffsetTable, ObjYOffsetTable` | three blocks: 22 ASCII character codes at `$DCDE` ($49,$4A,$4B,$4C,$4D,$4E,$54,$54,$47,$46,$36,$57,$57,$57,$57,$54,$56,$55,$4B,$58,$35,$59 - "IJKLMNTTGF6WWWWTVUKX5Y", the alphabet the name-entry screen offers); a 24-entry table of signed X offsets at `$DCF4` ($54, $52, then $08 eighteen times, then $0C, $0C, $08, $08) and a 26-entry table of signed Y offsets at `$DD0C` ($08 eighteen times, then $04, $08, $04, $08, $08, $08, $08, $08) | `adc $DCF4,y` at `$DD74` (the X offset, added to the object's X position) and `adc $DD0C,y` at `$DD85` (the Y offset); the character table is read by code the tracer could not reach | | `$DE14-$DF67` | 340 B | `-` | code the tracer could not reach - 173 instructions; CDL: untouched | not referenced by any instruction | | `$DF9F-$DFBE` | 32 B | `GameStateSeedTable` | seven bytes at the head (`$00,$00,$00,$01,$00,$00,$00`) that are copied into the state block at `$0182`, followed by 25 bytes (`$0F,$37,$27,$18,$35,$25,$15,$30,$10,$00,$31,$21,$11,$16,$30,$12,$30,$37,$16,$30,$22,$0F,$30,$28,$0F`) that no instruction in this dump reads | `lda $DF9F,x` at `$DF7C` (seven bytes are copied to `$0182`-$$0188`); the 25 bytes after them are read by code the tracer could not reach | | `$E15B-$E228` | 206 B | `TitleMenuStrings` | the two title-menu strings, each introduced by a two-byte marker: `$22 $2B` then "GAME START " and `$22 $8B` then "CONTINUE"; the code that follows (from `$E175`) sets the menu cursor at `$0200`/`$0201`/`$0202`/`$0203` and the sound effect at `$019A`; CDL: 62 bytes read as data, 58 fetched as code | the menu routine inside this block; no operand in this dump lands inside the block | | `$E254-$E265` | 18 B | `PowerupMaskTable` | two parallel tables: nine bytes at `$E254` ($07,$03,$06,$05,$06,$05,$06,$02,$08 - a per-power-up code) and ten bytes at `$E25D` ($08,$02,$20,$02,$10,$40,$02,$04,$40,$02 - a bit mask); the code tests the player's held button against the mask and counts the power-ups in `$6F` | `ldx $E254,y` at `$E235` and `and $E25D,y` at `$E23A` | | `$E290-$E398` | 265 B | `TitleScreenTextScript` | the title screen's drawing script: records of `[command][length][length bytes]`, with `$FF` as the separator and `$00` as the end of the stream. Command `$03` writes ASCII text ("score top", "area", "game designed by COMPILE", "produced by aii", "presented by pony inc.", "copyright 1986 pony inc.", "licensed by", "ninendo OF americ a inc.", "see you again\ingur di"), commands `$08` and `$09` write sixteen- and fourteen-byte runs of tile indices ($90-$A0 and $94-$9C); CDL: 265/265 bytes read as data | the script is read by the routine at `$E399` and the text routines around `$E2F7`; no operand in this dump lands inside the block, so the reader is not established | | `$E3B8-$E3F3` | 60 B | `PpuAttrInitScript` | a PPU write script: nine records of `[address high][address low][payload...][$80 terminator]` followed by a `$00` end-of-stream byte. The addresses are $2300 and $23C2-$23EB - the tail of the nametable and the whole $23C0 attribute table - and the payloads are attribute bytes ($FF for all four palettes, then $A5, $A0, $5A, $52, $50, $50, $0A); CDL: 25 bytes read as data | `lda $E3B8,y` at `$E399`, `$E3A3` and `$E3AA` (the routine writes the addresses with `STA PPUADDR` and the payloads with `STA PPUDATA`) | | `$E44B-$E4CE` | 132 B | `SoundTestText` | the sound-test screen's label, "OUND TEST" - the leading `S` was split off by the tracer and lives in the last byte of the code block at `$E398`-`$E44A` - followed by 125 bytes of code the tracer could not reach; CDL: untouched | the string is read by the code at `$E44B`'s own routine; no operand in this dump lands inside the block | | `$E520-$E60E` | 239 B | `HudScoreText` | the score display's label, "SCORE" (six ASCII bytes) followed by 233 bytes of code the tracer could not reach that moves the digits into the nametable; CDL: untouched | the string is read by the routine at `$E520`; no operand in this dump lands inside the block | | `$E671-$E766` | 246 B | `StaffCreditsText` | the staff-roll script: `$00`-terminated strings, each preceded by a one-byte code that is the screen row the text is drawn on - row 8 "CONGRATULATIONS!", row 2 "YOU ARE THE MESSIAH OF EARTH.", row 13 "STAFF", row 2 "game design JEMINI,JANUS,MOO", row 5 "JEMINI", row 2 "program JEMINI", row 7 "MIYAMOTO", row 2 "graphics JANUS", row 4 "test play MCNUI", row 14 "WAO ISEE", row 14 "YORIKI", row 5 "produced by aii", row 6 "created by COMPILE", row 15 "see you again\ingur di"; CDL: untouched | the script is read by the credits routine; no operand in this dump lands inside the block | | `$E909-$E916` | 14 B | `EnemyTypeVariantTable` | the tail of a 16-entry table that starts at `$E907` - the two bytes `$E907`/`$E908` were split off by the tracer as the instruction `ORA $06`, but the code's `LDA $E907,Y` reads them as table entries 0 and 1. The table maps an enemy's low nibble to its canonical type: $06,$07,$08,$09,$0A,$0B,$0C,$0F,$01,$02,$03,$04,$0F,$0F,$00 (entries 2-15) | `lda $E907,y` at `$E8E0` (the result replaces the low nibble of the enemy's type byte at `$0145`) | | `$E9D0-$EA0A` | 59 B | `EnemyMoveIntervalTable, ObjAttrTypeTable` | three blocks: a 16-entry table at `$E9D0` ($0C,$0C,$01,$02,$0C,$04,$07,$08,$09,$0C,$09,$0A,$0F,$0F,$0F,$0F - the interval at which each enemy type moves); 30 bytes of code the tracer could not reach at `$E9E0`; and a 13-entry table at `$E9FE` ($02,$01,$1D,$01,$1D,$20,$1F,$20,$19,$1F,$19,$20,$02) indexed by the same object slot as the tables at `$CCAD`, `$CCBA` and `$CCC7`; CDL: 1 byte read as data | `lda $E9D0,x` at `$E9BD` (the enemy's move interval) and `lda $E9FE,y` at `$E86A` (the value is loaded but the code then branches on the object index, not on it) | | `$EA3D-$EA51` | 21 B | `SoundCmdJumpTable` | seven three-byte `JMP abs` trampolines - the sound driver's command table: `$EF71` (sound init), `$EF79` (sound init 2), `$EF90` (sound update), `$EA52`, `$F0F4` (stop a channel), `$F0FA` (start a channel) and `$F146` (set the channel's parameters); CDL: untouched | the table is entered by a computed jump; no operand in this dump lands inside the block | | `$EB58-$EB79` | 34 B | `-` | code the tracer could not reach - 16 instructions; CDL: untouched | not referenced by any instruction | | `$EBAF-$ED4F` | 417 B | `ItemHandlerPtrTable` | 32 16-bit code addresses at the head ($EBEA, $EBF5, $EC0A, $EC16, $EC1E, $EC0F, $EC04, $EC73, $EC1A, $EC34, $EC4E, $EF8F, $ED43, $EC3E, $ECF4, $ECF4, $EC4A, $EC77, $ECB1, $EC7E, $ED14, $EDA3, $EDD6, $ED3B, ...) - the per-item-type handler table - followed by 385 bytes of code the tracer could not reach; CDL: 219 bytes fetched as code, 28 read as data | `lda $EBAF,x` at `$EBA0` and `lda $EBB0,x` at `$EBA4` (the index is doubled) | | `$EE43-$EE70` | 46 B | `-` | code the tracer could not reach - 22 instructions; CDL: untouched | not referenced by any instruction | | `$EF42-$EF49` | 8 B | `ApuChannelMaskTable` | eight APU channel masks: $01, $02, $04, $08 (set one channel) and $FE, $FD, $FB, $F7 (clear it); the sound engine ORs one into the channel-enable byte at `$0307`; CDL: 8/8 bytes read as data | `lda $EF42,y` at `$EECD`, `$EED5` and `$EF30` | | `$F01C-$F0F3` | 216 B | `NoteFrequencyTable` | 108 16-bit little-endian frequencies in an equal-tempered scale - each entry is 2^(-1/12) of the one before (ratio 0.9437), running from $07FE down to $0004, i.e. nine octaves from 2046 Hz to 4 Hz; CDL: 88 bytes read as data | not referenced by any instruction - the sound engine that reads it is in code the tracer could not reach | | `$F0FE-$F145` | 72 B | `SoundScriptPtrTable` | 36 16-bit pointers into the script data at `$F16B` ($F180, $F209, $F2FF, $F372, $F458, $F4D8, $F52B, $F544, $F5AB, $F5D8, $F60D, $F6EF, $F628, $F765, $F773, $F783, $F797, $F7AA, $F7BD, $F7D8, $F7ED, $F7FD, $F80E, $F821, $F837, $F84C, $F942, $F9D1, $FA2D, $FA50, $FB7A, $FBDB, $FC42, $FD75, $FE5C, $FE71) - one per sound effect | `lda $F0FE,x` at `$EFAB` and `lda $F0FF,x` at `$EFB0` (the routine pushes the pointer on the stack and returns to it) | | `$F16B-$FFFF` | 3733 B | `NoteDurationTable, SoundScriptData` | the sound driver's data: a 16-entry note-duration table at `$F16B` ($01,$02,$03,$04,$06,$08,$0C,$10,$18,$20,$30,$40,$60,$80,$C0 frames, then $00) and 3717 bytes of command streams after it - each stream is a list of 16-bit code addresses, note numbers and $80 terminators, with the text of the sound effects mixed in; the three vectors sit in the last six bytes; CDL: 946 bytes read as data | `lda $F190,y` at `$EE0A` and `lda $F191,y` at `$EE0F` (a 16-bit pointer inside the stream) | ## Power-on flow analysis Derived from the disassembly by `_tools/derive_poweron.py`; the call graph is walked from the reset entry. **RESET** is at `$E7BF` (`$E7BF`). The CPU comes here out of reset with the PPU and APU in their power-on state and the zero page and RAM uninitialised. The reset path calls: | Address | Routine | |---------|---------| **NMI** is at `$CE8D` (`$CE8D`). It is the per-frame handler; what it calls: | Address | Routine | |---------|---------| **IRQ** is at `$CEC4` (`$CEC4`) - a different address from RESET. ## ROM | Property | Value | |----------|-------| | Format | iNES | | Mapper | 2 | | Board | UNROM | | Mirroring | Horizontal | | Four-screen VRAM | no | | Battery | no | | PRG | 128 KB (8 x 16 KB), at file offset $0010 | | CHR | none | | Trainer | none | | Footer | none | | NMI vector | $CE8D | | RESET vector | $E7BF | | IRQ vector | $CEC4 | | ROM file | `Zanac (U).nes` | | SHA-256 | `7e75a19b9e11715c0eb19a2017a322df02f5cf4f7b30acef0fa7a0615236bda4` | ### Header notes - none ## Open work - **Pass 1** - complete. Banks 6 and 7 traced (44.5% of the image is code); banks 0-3 are empty and banks 4-5 are stage data. Build verifies byte-identical. - **Pass 2** - complete. All 133 entry points have semantic names; ~212 internal branch labels remain as address-based names; All raw `$XXXX` jsr/jmp/branch operands replaced with label references. - **Pass 3** - data tables done. Semantic zero-page aliases applied (14 named); 47 data blocks named for what they are and every one of the 104 data blocks the tracer emitted is recorded in the **Pass 3 data-table inventory** with its structure and its consuming instruction. What is still open is recorded rather than guessed at: - The 48-entry `JMP` trampoline table at `$C000` and the 7-entry one at `$EA3D` are both entered by a computed jump. The Mesen CDL shows the CPU fetching 25 of the 48 opcodes, so the table is real, but the index arithmetic that selects an entry is in code this dump's tracer could not reach, so the meaning of an entry number is not established. - `$C1A0-$C23F` is a 160-byte pitch ladder whose values step up by 5, 9, 13, 17, 25 and 33; no instruction in this dump reads it, so its record layout is recorded as not established. - The banks 4-5 stage data holds 16 KB of 1-bit-per-pixel tile patterns and a table of 16-bit pointers at `$8030`; how the tile data is addressed inside the bank is not established. - 55 of the blocks are code the tracer could not reach. They are CDL silent or recorded as code, they decode cleanly as instructions, and they are left exactly as the tracer split them. - **Promotion** to `NROM//` once ready, as `git mv`. ## Files | File | Purpose | |------|---------| | `prg.s` | Pass 3 disassembly for banks 0-5: banks 0-3 are empty, banks 4-5 hold the stage data | | `prg.6.s` | Pass 3 disassembly for bank 6, the switchable bank at CPU $8000-$BFFF | | `prg.07.s` | Pass 3 disassembly for bank 7, the fixed bank at CPU $C000-$FFFF | | `header.s` | iNES header include | | `prg.bin` | 131072 bytes (128 KB) PRG image | | `rom.nes` | byte-identical copy of the original ROM | | `linker.cfg` | HEADER / PRG / PRG6 / PRG7 memory areas | | `symbols.inc` | NES hardware register names | | `locals.inc` | zero-page aliases (14 semantic names applied) | | `Makefile` | build and byte-identity check |