# Soccer (JU) ## Status **Pass 3 complete; promoted.** `prg.s` is a traced disassembly with all address-derived labels replaced, all `jsr`/`jmp` operands symbolic, hardware registers named via `symbols.inc`, and `apply_symbols.py` reporting 0 residue. Code coverage: 18,110 / 32,768 bytes (55.3%). `make` assembles and verifies byte-identical to `rom.nes`. Data tables: all 34 data blocks in the PRG are interpreted in the [Pass 3 data-table inventory](#pass-3-data-table-inventory) below. 27 of them carry a name (30 labels in all); the 7 that carry none are recorded as unreferenced data or as 6502 code the tracer could not reach. The 9 `.byte` rows whose audit comment carried a PRG offset computed with the wrong base were repaired to the canonical `; CPU:$XXXX PRG:XXXX AA BB` form; the bytes themselves are untouched. ## Entry-point naming (2026-10-21) **311** of the **354** address-derived entry points (`Sub_xxxx` / `SubCxxxx`) in this project now carry a semantic name. The names come from what the ROM shows each routine does, read out of the routine's own header, its callers and the RAM fields it touches: | Area | Names | |------|-------| | reset / NMI | `Reset`, `NmiHandler`, `StartNewMatch`, `InitMatchState`, `InitNewMatchState`, `InitGameState`, `ResetMatchState`, `UpdateGameFrame` | | input | `ReadControllers`, `CallLevelHandler`, `CallPlayerHandler` | | VRAM / PPU | `ApplyVramUpdateStream`, `ApplyVramStream`, `WriteVramBlockFromTable`, `WriteVramRecord`, `ClearScreenAndPalettes`, `ClearNameTableRows`, `ClearOamBuffer`, `HideOamSprites`, `SetScrollPosition`, `SetScrollX` | | players | `InitPlayerState`, `UpdatePlayerSprite`, `UpdatePlayerPosition`, `MovePlayer`, `UpdatePlayerSpeed`, `UpdatePlayerFrame`, `GetPlayerPosition`, `SetPlayerPosition`, `SetPlayerDirection`, `SetPlayerState`, `ResetPlayerRecord`, `InitPlayerRecord` | | ball / field | `ResetBall`, `ResetBallPosition`, `SetBallPosition`, `ReadFieldState`, `GetFieldTile`, `ReadFieldTile`, `LoadLevelTileRow`, `ResetFieldObjects` | | score / HUD | `UpdateScore`, `SetScoreValue`, `SetScoreDisplay`, `UpdateScoreDisplay`, `SetScoreDigits`, `WriteScoreValue` | | sound | `UpdateSoundEngine`, `StartSoundEngine`, `AdvanceMusicSequence`, `AdvanceSoundEffectSequence`, `SetPulse1ControlAndNote`, `SetPulse1Timer`, `SetPulse2Timer`, `SetTriangleTimer`, `LookUpNotePeriod`, `ReadSoundTableByte` | ### Residue — 44 entry points still address-derived The 44 labels that keep their `Sub_xxxx` / `SubCxxxx` spelling are listed here with the reason each one could not be given a better name: * **Data blocks the tracer split** — `SubCBA48`, `SubCBE16`, `SubCC373`, `SubCC3C6`, `SubCC3FB`, `SubCC502`, `SubCC503`, `SubCC517`, `SubCC53A`, `SubCC60B`, `SubCC80E`, `SubCC82C`, `SubCC83A`, `SubCC89A`, `SubCCA99`, `SubCCAF9`, `SubCCE34`, `SubCDA4C`, `SubCDA4D`, `SubCDA52`, `SubCDA55`, `SubCDA63`, `SubCDFF0`, `SubCE59E`, `SubCE5B0`, `SubCE633`, `SubCE63F`, `SubCE663`, `SubCFB06`. These are not entry points at all: they head data regions whose bytes happen to decode as instructions, and the dump does not establish a separate role for each piece. The blocks they belong to are interpreted in the data-table inventory below. * **Player-record and field-state helpers** — `Sub_BA47`, `Sub_BBE5`, `Sub_C1E3`, `Sub_C8E1`, `Sub_C9D5`, `Sub_CB7E`, `Sub_CBE8`, `Sub_CF7A`, `SubCD9AD`, `SubCDB57`, `SubCCD01`, `SubCCD3D`, `SubCC6DB`, `SubCC891`, `SubCCA07`. Each moves or tests one field of the per-player record at `$0300`/`$06xx`, but the dump does not say which game quantity (position, speed, facing, stamina, …) the field holds, so a name would be a guess. * **The remaining `SubCC…` handlers** — `SubCC3FB`, `SubCC503`, `SubCC517`, `SubCC53A`, `SubCC6DB`, `SubCC891`, `SubCCA07`, `SubCCA99`, `SubCCAF9` and `SubCCE34` — are the per-script handlers the level pointer table (`LevelHandlerPointerTable`, `$8251`) and the field tables reach; the pointer table records *which* handler runs but not what it does. They sit on data blocks the tracer split, so the block they head is named after the block rather than after a routine. --- ## 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 `$8000` (`Reset`). 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 | |---------|---------| | `$800A` | `Reset_OnNonnegative` (falls through) | | `$8013` | `Reset_OnNonnegative2` (falls through) | | `$8021` | `Reset_OnNonzero` (falls through) | | `$8044` | `Reset_OnNonnegative3` (falls through) | | `$8021` | `Reset_OnNonzero` | **NMI** is at `$809F` (`NmiHandler`). It is the per-frame handler; what it calls: | Address | Routine | |---------|---------| **IRQ** is at `$8000` (`Reset`) - the same address as RESET, so the IRQ vector reaches the reset path. ## ROM | Property | Value | |----------|-------| | Format | iNES | | Mapper | 0 (NROM) | | PRG | 32 KB | | CHR | 8 KB | | Mirroring | Vertical | | NMI vector | $809F | | RESET vector | $8000 | | IRQ vector | $8000 | ## Power-on flow RESET enters at `$8000`: cld ; CPU:$8000 PRG:0000 D8 sei ; CPU:$8001 PRG:0001 78 ldx #$00 ; CPU:$8002 PRG:0002 A2 00 stx PPUCTRL ; CPU:$8004 PRG:0004 8E 00 20 stx PPUMASK ; CPU:$8007 PRG:0007 8E 01 20 lda PPUSTATUS ; CPU:$800A PRG:000A AD 02 20 bpl Reset_OnNonnegative ; CPU:$800D PRG:000D 10 FB dex ; CPU:$800F PRG:000F CA txs ; CPU:$8010 PRG:0010 9A ldx #$1F ; CPU:$8011 PRG:0011 A2 1F lda Zp50,x ; CPU:$8013 PRG:0013 B5 50 sta $0350,x ; CPU:$8015 PRG:0015 9D 50 03 NMI handler at `$809F`: php ; CPU:$809F PRG:009F 08 pha ; CPU:$80A0 PRG:00A0 48 txa ; CPU:$80A1 PRG:00A1 8A pha ; CPU:$80A2 PRG:00A2 48 tya ; CPU:$80A3 PRG:00A3 98 pha ; CPU:$80A4 PRG:00A4 48 ldx #$03 ; CPU:$80A5 PRG:00A5 A2 03 lda $80EE,x ; CPU:$80A7 PRG:00A7 BD EE 80 ## Pass 3 data-table inventory Every data block in the PRG is listed below. The block boundaries are the tracer's (`; ---- data $XXXX-$YYYY` markers in `prg.s`); where a block holds more than one structure the sub-labels in `prg.s` mark the split. Soccer (JU) is Nintendo's 1985 five-a-side football game: ten outfield players plus a goalkeeper on each side, a scrolling pitch and a kick-off / throw-in / goal-kick state machine. Most of the data below is therefore per-player records (position, sprite, kit colour, speed) and per-team definitions. | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$8251-$8262` | 18 | `LevelHandlerPointerTable` | 9 little-endian 16-bit code addresses: `$9FD2`, `$A04B`, `$A0D0`, `$A0D6`, `$A109`, and `$86BE` four times - one entry per game state | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$0251` | | `$82EF-$8306` | 24 | `PlayerSpriteRecordTable` | 8 records of 3 bytes (Y position, tile index, sprite attribute): `($1E,$1C,$0A)`, `($10,$0B,$1B)`, `($0F,$1A,$0F)`, `($1B,$10,$0B)`, `($1A,$0A,$13)`, `($19,$17,$0E)`, `($1C,$19,$0C)`, `($19,$31,...)` - the on-field sprite of each of the ten players plus the two keepers | `lda $82EF,x` at `$82CB` copies the whole table into the sprite buffer at `$0701`; `lda $82EF,y`, `lda $82F0,y` and `lda $82F1,y` at `$8E12`, `$8E18` and `$8E1E` pick the three fields of one player's record, with Y = 3 x the player number | | `$832C-$832F` | 4 | `LevelStartParameterTable` | 4 bytes (`$00`,`$C3`,`$03`,`$00`) | `lda $832C,x` at `$8309` copies them into both `$0401-$0404` and `$0405-$0408` at a kick-off | | `$8921-$8940` | 32 | `PlayerSpeedByDistanceTable` | 32 increasing speed values (`$10`,`$10`,`$10`,`$10`,`$11`,`$13`,`$15` ... `$42`,`$44`) - the running speed that applies at each distance step | `lda $8921,y` at `$8890`, with Y = the value divided by four (`lsr a` twice at `$888D`/`$888E`) | | `$8941-$8996` | 86 | *(unreferenced - unreachable code)* | 6502 code: `LDX #$2E`, `LDA $0317,x`, `AND #$08`, `BEQ`, `LDA ($47),y`, `INC $06BD`, `LDA $06BD`, `CMP #$06`, `BEQ`, `LDA #$06`, `JMP` | **no instruction operand lands in this block.** Unreached code, ROM offset `$0941` | | `$89D2-$89D6` | 5 | `PlayerDirectionThresholdTable` | 5 decreasing thresholds (`$5A`,`$58`,`$54`,`$50`,`$4D`) | `cmp $89D2,y` at `$89B4`, compared against the counter at `$06C0` | | `$89D7-$89DB` | 5 | `PlayerDirectionReloadTable` | 5 reload values (`$8F`,`$94`,`$99`,`$9D`,`$A1`) | `lda $89D7,y` at `$89B9` - the value written back into `$06C0` when the counter drops below the threshold | | `$8C07-$8C0B` | 5 | `FieldPlayerPositionTable` | 5 single-byte values (`$14`,`$1D`,`$12`,`$16`,`$0E`) | `lda $8C07,x` at `$8B4F` copies them into `$0700-$0704` (X counting down from 4) | | `$8D44-$8DD3` | 144 | `PlayerOamSpriteTable` | 12 records of 12 bytes - one per player, each covering three sprites (Y, tile, attribute, X per sprite); the first player's record is 14 bytes of `$31` (the "blank" sprite) followed by `$18`,`$0F`, the rest hold the real kit tiles | `lda $8D44,x` at `$8D20` copies 12 bytes into the sprite buffer at `$0701` for each player, with X = 12 x (player - 1); the sprite's Y is set to `$1C` at `$8D2C` | | `$8ED1-$8F20` | 80 | `TitleCreditText` | 5 lines of 16 ASCII characters, padded with spaces: the credit "CREATE BY M.OOYAMA" | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$0ED1`; the same credit appears a second time at `$9F82` | | `$9007-$9012` | 12 | `PlayerSpeedFineTable` | 12 values in an arithmetic progression with step `$15`: `$00`,`$15`,`$2A`,`$3F`,`$54`,`$69`,`$7E`,`$93`,`$A8`,`$BD`,`$D2`,`$E7` | `lda $9007,y` at `$9001` -> `PlayerSpeed,x`; `cmp $9007,y` at `$9F19` compares the same table against a live value | | `$9063-$90B2` | 80 | `SpriteBehaviourDispatchTable` | 16 little-endian 16-bit code addresses (`$2492`,`$4092`,`$4092`,`$BE92`,`$7692`,`$AF92`,`$AF92`,`$BE92`,`$86BE` x3,`$2D93`,`$4E93`,`$F9DA`,`$22DB`,`$57DB`) - the routine each player behaviour dispatches to | **no instruction operand lands in this pointer half.** The rest of the block is code and a small parameter table | | `$90B3-$90F8` | 70 | *(unreferenced - unreachable code)* | 6502 code (`LDA $90D8,y`, `STA $0300`, `INX`, `CPY #$15`, `BNE`, ...) followed by a parameter row `$0D`,`$0E`,`$00`,`$03`,`$00`,`$10`,`$00` x9 and `$02`,`$02`,`$02`,`$04`,`$04`,`$01`,`$82`,`$82`,`$82`,`$84`,`$84`,`$81` | **no instruction operand lands in this block.** Unreached code, ROM offset `$10B3` | | `$9510-$9599` | 138 | `TeamKitColourTable` | 12 players' kit data - the first byte of each entry is the kit colour that the code compares against the player's current colour at `$0301`, and the entries are followed by the repeating per-team sprite groups (`$08 $00 $06 $06 $00`, `$08 $06 $06 $08 $00`, ...) | `lda $9510,y` at `$913E`, with Y = the player index; `cmp $0301,x` at `$9143` tests the kit colour | | `$959A-$95A1` | 8 | `TeamSpriteOffsetTable` | 8 offsets (`$00`,`$07`,`$0D`,`$13`,`$19`,`$1F`,`$25`,`$2B`) - the address of a team's badge in the tile area | `lda $959A,y` at `$915E` -> `$0309,x` | | `$95A2-$95E1` | 64 | `TeamKitDefinitionTable` | a list of team definitions: each entry starts with the marker `$02`, carries an index byte and three or four tile indices, and is closed by the `$FF` terminator. The tile runs spell the badges out - `$50 $51 $52` ("PQR"), `$49 $47 $48` ("IGH"), `$4C $4A $4B` ("LJK"), `$4F $4D $4E` ("OMN") | `lda $95A2,y` at `$9178` -> `$0309,x` | | `$95E2-$97EB` | 522 | *(unreferenced)* | more team definitions and the level's own code: the `$FF $02 ...` team list continues to `$95E1`, after which the block holds further tile/letter groups and, from `$97A0`, 6502 code (`LDA #$0F`, `STA $0573`, `JSR $98B5`, `AND #$0F`, `TAX`) | **no instruction operand lands in this block.** Unreferenced data, ROM offset `$15E2` | | `$990A-$9962` | 89 | *(unreferenced - unreachable code)* | 6502 code: `BNE`, `JSR $AA58`, `AND #$04`, `BEQ`, `LDA ($4A),y` | **no instruction operand lands in this block.** Unreached code, ROM offset `$190A` | | `$99F9-$9A1F` | 39 | `PlayerTargetTable` | groups of tile indices introduced by the separator `$19`: `($18,$38,$58,$78,$98,$B8,$D8)`, `($19,$36,$16,$08)`, `($19,$35,$12,$07)`, `($19,$36,$26,$06)`, `($19,$36,$0F,$06)`, `($19,$17,$38,$0F)`, `($19,$27,$24,$0F)`, `($19,$26,$3B,$0F)`, `($15,$30,$45,$20)`, `($00)` - the cells a player may aim the ball at | `lda $99F9,y` at `$99A0` and `lda $9A00,y` at `$99CC`; `lda $9A1C,y` at `$8B6E` reads the last four bytes of the same run | | `$9BED-$9BF3` | 7 | *(unreferenced)* | 7 bytes (`$9C`,`$87`,`$9C`,`$16`,`$9D`,`$23`,`$9D`) | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$1BED` | | `$9DF1-$9DF8` | 8 | *(unreferenced)* | 8 bytes (`$02`,`$05`,`$05`,`$09`,`$02`,`$0A`,`$0A`,`$06`) | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$1DF1` | | `$9F82-$A091` | 272 | `CreditText` | the title screen: 16 lines of 16 ASCII characters with the credit "CREATE BY M.OOYAMA", followed by the rest of the screen's tile data | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$1F82` | | `$A1F1-$A35C` | 364 | `PlayerPositionRecordTable` | 4-byte records - the first entry points at `$A1FD` (inside the block), then `$A21D`, `$A24D`, `$A27D`, `$A29D`, `$A2DD`, ... - one record per player position, followed by the per-player parameter bytes (`$5B`,`$04`,`$37`, `$00`,`$0B`,`$01`,`$55`, `$00`,`$0B`,`$08`,`$4F`, ...) | `lda $A1F1,y` at `$A0E8` and `lda $A1F2,y`, `lda $A1F3,y`, `lda $A1F4,y` at `$A0ED`, `$A0F2`, `$A0F7` - the four bytes become `Zp88`-`Zp8B`, with Y = 4 x the player index | | `$A595-$A599` | 5 | *(unreferenced)* | 5 bytes (`$A6`,`$79`,`$4C`,`$AD`,`$87`) | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$2595` | | `$A5AA-$B5E2` | 4,153 | `LevelDefinitionData` | the level's data and code: the per-team sprite records are read from the middle of the block (`lda $A7BC,y` at `$83A5`), and the bytes around them are the level's own 6502 code | `lda $A7BC,y` at `$83A5` and `ldx $A7BD,y` at `$83A8` | | `$B891-$B8A9` | 25 | `PlayerActionRecordTable` | 5 records of 5 bytes: `($40,$44,$06,$0F,$04)`, `($40,$48,$06,$0F,$04)`, `($50,$54,$06,$0F,$04)`, `($50,$58,$06,$0F,$04)`, `($50,$58,$06,$0F,$04)` - the action each of the five player slots performs | `ldy $B895,x` at `$B733` (the last field of the record, with X = 5 x the slot), `lda $B894,x` at `$B773`, `ldy $B893,x` at `$B7AB` and `ldy $B891,x` at `$B86C` | | `$BE94-$BF0B` | 120 | `SpriteAttributeRecordTable` | 4-byte records read as (attribute, tile, X, Y): the first record is `($0F,$00,$05,$80)`, then `($20,$20,$20,$20)`, `($15,$0F,$00,$07)`, `($2A,$0F,$00,$08)`, `($3F,$0F,...)` - the sprite attributes and positions of the players and the ball | `lda $BE95,y` at `$BE1A` -> `$0304,x`, `lda $BE96,y` at `$BE20` -> `$0305,x`, `lda $BE97,y` at `$BE26` -> `$0302,x` and `lda $BE98,y` at `$BE2C` -> `$0303,x`; `eor $BE9D` at `$9D43` masks the record | | `$C432-$C501` | 208 | *(unreferenced - unreachable code)* | 6502 code: `JSR $E132`, `JSR $C89A`, `AND #$07`, `JMP $CA07`, `CMP #$C5`, `JMP $C448`, `LDA #$01`, `STA $0560` | **no instruction operand lands in this block.** Unreached code, ROM offset `$4432` | | `$D28E-$D2AD` | 32 | `PitchTileIndexTable` | 32 tile indices (`$AE`,`$B2`,`$B6`,`$BA`,`$BE`,`$AE`,`$BE`,`$B6`,`$D2` x8, `$C2`,`$C4`,`$C8`,`$CA`,`$CE`,`$C2`,`$CE`,`$C8`,`$D2` x8) - the tiles the pitch is drawn with | `lda $D28E,y` at `$D02D` and `lda $D296,y` at `$D033` | | `$D2AE-$D624` | 855 | *(unreferenced - unreachable code)* | 6502 code from `$D2AE` (`JSR $C008`, `STA $D008`, `CMP #$00`, `JSR $C000`, `CMP #$08`) onwards | **no instruction operand lands in this block.** Unreached code, ROM offset `$52AE` | | `$D8CD-$D8E0` | 20 | `PlayerTimerLimitTable` | 5 records of 4 bytes: `($1C,$10,$20,$00)`, `($1B,$10,$20,$00)`, `($1A,$10,$20,$00)`, `($17,$10,$20,$00)`, `($16,$10,$20,$00)` - the time each player may hold the ball | `cmp $D8CD,y` at `$D7C4` compares the limit against the timer at `$A3`, with Y = 4 x the player index; `lda $D8CF,x` at `$D802` and `lda $D8CE,x` at `$D80B` read the same records field by field | | `$DB9A-$DC36` | 157 | `PlayerKickPatternTable` | groups of pitch tile indices separated by the marker `$FE` - e.g. `($00,$07,$0E,$1C,$15,$1C,$0E,$07)`, then `$33`,`$25`,`$33`,`$34`, `$FE`,`$25`,`$FF`, ... - the cell patterns a kick travels through | `cmp $DBF3,y` at `$D916`, `$D93D` and `$D94C`; `lda $DBC4,y` at `$D9B8`, `lda $DBCD,y` at `$D9D6` and `lda $DBEA,y` at `$DA8F` | | `$DDBC-$DDC3` | 8 | *(unreferenced)* | 8 bytes (`$07`,`$03`,`$00`,`$05`,`$06`,`$04`,`$00`,`$01`) | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$5DBC` | | `$DDC4-$DEB2` | 239 | `LevelScriptPointerTable` | little-endian 16-bit pointers into the script bytes that follow in the same block: `$DE43`,`$DE45`,`$DE49`,`$DE4C`,`$DE50`,`$DE54`,`$DE59`,`$DE5D`,`$DE26`,`$DE29`,`$DE2D`, ... - one entry per scripted event | `lda $DDC4,x` at `$DD44` and `$DD60`, `lda $DDC5,x` at `$DD4C` and `$DD66` | | `$DF71-$DFD3` | 99 | *(unreferenced - unreachable code)* | 6502 code: `ADC $AA0A`, `LSR A`, `BCC`, `RTS`, `LDA ($80),Y`, `PHA`, `LDA #$FF`, `CMP #$09`, `TAX` | **no instruction operand lands in this block.** Unreached code, ROM offset `$5F71` | | `$DFF6-$E0E5` | 240 | *(unreferenced)* | 120 little-endian 16-bit values, every one of them small (`$0013`,`$0004`,`$0013`,`$0005`,`$0013`,`$0006`,`$000D`,`$0004`, ... ) - a list of screen positions or cell offsets | **no instruction in this dump reads it.** Unreferenced data, ROM offset `$5FF6` | | `$E132-$E469` | 824 | `SpriteRoutineDispatchTable` | 4-byte records whose first two bytes are a little-endian code address (`$042F`,`$E152`,`$E184`,`$E19A`,`$E1AB`,`$E232`,`$E27D`,`$E2B0`,`$E21E`,`$E349`,`$E349`,`$E355`,`$E380`,`$E3B6`,`$E3C9`,`$E423`, ...) - the routine each sprite behaviour runs - followed by the routines themselves | `lda $E134,y` style reads at the head of the block; the rest of the block is the code the table points at | | `$E676-$F4FF` | 3,722 | `LevelGraphicAndCodeData` | a mixture: 6502 code (`BNE`, `INC $E706`, `RTS`, `LDA $2000`, `STA $8000`) and the pitch's tile data (`$0E $00 $04 $00 $0E $00 $08 $00 $0E $00 $0C $00 $0E $00 $06 $00`) | `ldy PlayerSpeed2,x` at `$9058` and `lda $E67F,y` in the routines at `$E5D4`-`$E605` read the table part | | `$F8B2-$F8CA` | 25 | `KickDistanceTable` | 25 values decreasing in steps of 4 from `$7F` to `$00` - the distance the ball travels for each kick strength | `lda $F8B2,x` at `$F8FF`, with X = (the kick counter - 1) / 2; the value becomes `ZpEC` | | `$FA30-$FA70` | 65 | `PlayerSpeedTable` | 65 bytes in two increasing runs: `$10`-`$1E` and then `$90`-`$9B` - the sound/effect levels the game steps through | `lda $FA71,y` in the routine at `$F827` walks the table that follows | | `$FA71-$FB05` | 149 | `BallTrajectoryTable` | an index byte followed by 4-byte records (target cell, then three offsets) - the ball's flight path | `lda $FA71,y` at `$F827` picks the record, then `lda $FA72,y` at `$F82B`, `lda $FA73,y` at `$F830`, `lda $FA74,y` at `$F835`, `lda $FA75,y` at `$F83A` and `lda $FA76,y` at `$F83F` read its fields | | `$FB0F-$FFF6` | 1,256 | `LevelGraphicData` | the level's graphics in a packed form - rows of tile indices and attribute bytes (`$28 $A7 $28 $A7`, `$68 $68 $6C $68 $8C $88 $84 $70`, ...) | `adc $FCC4,x` at `$C410` reads a byte out of the block | | `$FFF7-$FFF9` | 3 | *(unreferenced)* | 3 bytes of `$FF` | none - fill in front of the vectors | | `$FFFA-$FFFF` | 6 | `CpuVectorTable` | 3 little-endian 16-bit vectors: NMI `$809F`, RESET `$8000`, IRQ `$8000` (the IRQ vector reaches the reset path) | the CPU reads them out of the vector table | ## Build ``` Byte-identical ROM verified. ```