# Xevious (U) ## Status **Pass 3 complete, with a Pass 1 correction applied on 2026-10-21.** The original static-trace split missed 1,158 bytes of code reached through computed dispatch; the Mesen CDL capture `xevious_mesen.cdl` (present in this folder) was applied with `_tools/cdl_reclass.py`, which re-split the file (1,158 data→code, 48 code→data). The 45 entry points that the correction recovered now carry `Sub_XXXX` names, so there is no `Label_XXXX` residue. Six raw `$XXXX` call operands remain, all inside regions the CDL never observed (listed under **Pass 1 notes**). `make` assembles `header.s`, `prg.s`, `chr.s`, links them with `ld65` and compares the result against `rom.nes`: ``` Byte-identical ROM verified. ``` ### Pass 1 results | Metric | Value | |--------|-------| | PRG size | 32768 bytes (32 KB) at $8000-$FFFF | | Code | 12455 bytes (38.0%) | | Data | 20313 bytes (62.0%) | | Instructions | 6002 | | Label definitions | 868 (624 `Sub_XXXX` internal) | | Zero-page aliases | 156 (in `locals.inc`) | | Indirect dispatches | `jmp ($fe)` at $BA88 (table $BA8B, 16 entries), `jmp ($4)` at $CA07 (table $CA0A, 64 entries) | ### Pass 2 results | Metric | Value | |--------|-------| | `Label_XXXX` names replaced | 781 at Pass 2 + 45 recovered by the CDL re-split | | Raw `$XXXX` jsr/jmp operands replaced | 466 at Pass 2; every re-split operand on an instruction boundary now uses a label | | Remaining raw `$XXXX` jsr/jmp | 6 (inside CDL-silent misdecoded regions — see Pass 1 notes) | | Semantic zero-page aliases | ~20 (in `locals.inc`) | ### Pass 3 results | Metric | Value | |--------|-------| | Semantic entry-point names | 143 (Reset, NmiHandler, IrqHandler, InputRead, ObjInitCall, ObjMoveCall, ObjDrawCall, ObjUpdateCall, SetOamPtrs, WaitVblank, GameModeDispatch, ObjSpriteSetup, ObjCommonUpdate, etc.) | | Internal branch labels | 624 (`Sub_XXXX` address-based, local to routines) | | Named data tables | 32 (see the inventory below) | | Semantic zero-page aliases | ~20 (PPUCTRLShadow, ObjectX, ObjectY, ObjPtr, GameMode, Joypad1Cur, etc.) | ### Data table inventory | Address | Size | Name | What it is | |---------|------|------|------------| | $8000–$A2BF | 8896 B | `PatternData` | Tile/sprite pattern data | | $A6CE–$A6D6 | 9 B | `Data_A6CE` | Undetermined bytes before the VRAM copy loop at $A6D7 | | $A74B–$A768 | 30 B | `VramWritePtrTable` | 15 × 16-bit pointers to the VRAM write descriptors below (read by `lda $A74B,x` at $A6E1) | | $A769–$A8D4 | 364 B | `VramWriteData` | VRAM write descriptors `[lo, hi, count, data…]`; written to $2006/$2007 by the routine at $A6DE | | $A8D5–$A8F4 | 32 B | `DataStreamPtrTable` | 16 × 16-bit pointers to the streams at $ACFB–$B8AA (read by `lda $A8D5,x` at $F3E4); first byte of each stream is a count loaded into Zp12 | | $A8F5–$A9BA | 198 B | `ObjTilePtrTable` | 99 × 16-bit pointers to the tile data at $A9BB–$ACFA (read by `lda $A8F5,y` at $C353, stored per object at $050A/$050B) | | $A9BB–$ACFA | 832 B | `ObjTileData` | Tile/attribute data addressed by `ObjTilePtrTable` | | $ACFB–$B8AA | 2992 B | `DataStreamData` | Data streams addressed by `DataStreamPtrTable` | | $BA8B–$BAAA | 32 B | `IndDispatchFeTable` | 16-entry indirect jump table for `jmp ($fe)` | | $BB55–$BEFA | 934 B | `SpriteAttrTable` | Sprite object attribute table (+ copyright text) | | $C09E–$C1FD | 352 B | `ObjParamTable` | Enemy/object parameter table (byte pairs) | | $C28F–$C304 | 118 B | `ObjInitDirTable` | Object initial direction/speed table | | $CA0A–$CA61 | 88 B | `JumpTable4` | 64-entry indirect jump table for `jmp ($4)` | | $CD37–$CD44 | 14 B | `OamSlotTable` | OAM sprite slot/index table | | $D215–$D249 | 53 B | `ObjSubPtrTable` | Object subroutine pointer table | | $D654–$D6F8 | 165 B | `ObjMoveDeltaTable` | Object movement delta table | | $D833–$D83A | 8 B | `ObjDataPtrTable` | 4 × 16-bit pointers to the layout records at $D840/$D84A/$D864/$D882 | | $D83B–$D8DF | 165 B | `ObjLayoutData` | Object layout records addressed by `ObjDataPtrTable` | | $DF78–$DFA1 | 42 B | `ObjPtrTable` | Object/enemy routine pointer table | | $ED7C–$EE27 | 172 B | `EnemyPtrTable` | Enemy routine pointer table ($F0xx–$F1xx) | | $EF3B–$EFC5 | 139 B | `SpriteLayoutData2` | 4-byte sprite layout records (x offset, y offset, tile, attribute) | | $F0E8–$F1D9 | 242 B | `EnemySpawnTable` | Enemy spawn parameter table (4-byte entries) | | $F53C–$F55B | 32 B | `AttrAddrTable` | 16 × 16-bit PPU attribute-table addresses ($23C0–$23F8, $2BC0–$2BF8) | | $F5D8–$F61C | 69 B | `TextTileData` | Text as font tile indices ($0A = `A`, $24 = space) with $FE/$FF control codes; decodes to PLAYER, GAME OVER, READY, SOLVALOU, LEFT, PAUSE | | $F89E–$F8BD | 32 B | `HandlerPtrTable` | 16 handler pointers called by pha/pha/rts (each stored value is target−1) | | $F957–$F976 | 32 B | `ObjSpeedTable` | Speed bytes read by `lda $F956,y` at $F7CC (1-based index) | | $F977–$F996 | 32 B | `ObjSpeedTable2` | Speed bytes subtracted by `sbc $F976,y` at $F7C5 | | $F997–$F9BC | 38 B | `SpriteLayoutPtrTable` | 19 × 16-bit pointers to the records at $F9BD–$FAF5 | | $F9BD–$FAF5 | 313 B | `SpriteLayoutData` | 4-byte sprite layout records (x offset, y offset, tile, attribute) | | $FC19–$FC4D | 53 B | `ResetVarTable` | Reset variable table | | $FE02–$FE09 | 8 B | `OamIndexTable` | OAM sprite index table | | $FEE6–$FF05 | 32 B | `PaletteData` | 32 bytes of palette data copied to PPUDATA and the $0280 mirror | | $FF06–$FF1C | 23 B | `OamAttrTable` | OAM attribute bytes read by `lda $FF06,y` at $FCAD/$FCC2 | Many smaller data blocks remain unnamed; they are left as classified `.byte` blocks per the no-guessy-rewrites convention. ### Pass 1 notes - The committed Pass 1 split was produced by a static trace and never used the Mesen CDL in this folder. `_tools/cdl_reclass.py` was run against `xevious_mesen.cdl` on 2026-10-21: 1,158 bytes reclassified data→code, 48 code→data, 42 labels emitted, and 3 branch targets resolved to new labels. The recovery is what makes the $A74B–$B8AA and $F9xx tables readable: before it, $A6D7 (a VRAM copy loop) and $F8BD–$F915 (handler entries) sat inside `.byte` blocks. - The pointer table at $D833 is 4 entries and starts at the byte the tracer had decoded as `rti` at $D833; `lda $D833,y` / `lda $D834,y` at $D7E3/$D7E8 read it high/low. The former `ObjCodePtrTable` label at $D834 was one byte late and has been replaced by `ObjDataPtrTable` at $D833. - Two computed-dispatch mechanisms were recovered via declared jump tables: - `jmp ($fe)` at $BA88 indexes a 16-entry table at $BA8B (targets in $BAAB–$BB20 range) - `jmp ($4)` at $CA07 indexes a 64-entry table at $CA0A (targets mostly in $CA–$CB and $C0–$C3 ranges) - One branch at $E40A targets $E3B5, which falls inside the operand of `jsr $CF2F` at $E3B3. Emitted as `.byte` with an explanatory comment since the target cannot be a label. ### Pass 1 residue (CDL-silent, left as found) The CDL has no opinion about these addresses (they were never fetched), so the re-split could not settle them. They are the only remaining inconsistencies and are documented rather than guessed at. - Six raw `$XXXX` call operands have no instruction boundary at the target: `jsr $08A9` at $C62D (`Sub_C626`), `jsr $01D7` at $D24B, `jsr $F230` at $D61E (`Sub_D61B`), `jmp $CE04` at $DF63 ($CE04 is two bytes into `bit $00A2` at $CE03), `jsr $21FF` at $EAA9 and `jsr $E0FF` at $EAAD (both in `Sub_EA9F`). - `Sub_C626`, `Sub_D61B`, `Sub_D63E`, `Sub_EA9F` and `$D24B` decode as code but their bytes form tables (4-byte records, pointer-table tails). They are the source of five of the six raw operands above. - `$D617–$D628` and `$EA9F–$EAC0` are therefore still emitted as instructions; their bytes read as data tables. - Six addresses contain undocumented/illegal opcodes not decoded by the tracer: $9848, $A2C3, $CD89, $E0FF, $E3B5, $F230. ## ROM | Property | Value | |----------|-------| | Format | iNES | | Mapper | 0 | | Board | NROM-256 | | Mirroring | Horizontal | | Four-screen VRAM | no | | Battery | no | | PRG | 32 KB (2 x 16 KB), mapped at CPU $8000-$FFFF | | CHR | 8 KB (1 x 8 KB) | | Trainer | none | | Footer | none | | NMI vector | $FC4E | | RESET vector | $FAF6 | | IRQ vector | $FF1D | | ROM file | `Xevious (U).nes` | | SHA-256 | `1915fb1c30251de0c8f7913720a27b25d3ccef871851722a504d95fbb9d0ca7c` | ### Header notes - none ## Open work - **Pass 1** - complete, and re-split from the Mesen CDL on 2026-10-21 (see Pass 1 notes and residue above). - **Pass 2** - complete. All `Label_XXXX` names are replaced and every raw `jsr`/`jmp` operand that lands on an instruction boundary uses a label. Six CDL-silent operands remain raw (listed above). - **Pass 3** - complete. 143 routine entry points have semantic names; 624 internal labels remain as `Sub_XXXX` address-based names; 32 data tables are named (inventory above); smaller blocks remain classified `.byte` per the no-guessy-rewrites convention. - **Promotion** - done: promoted to `NROM/Xevious (U)` in commit `df11857`. ## Files | File | Purpose | |--------|---------| | `prg.s` | Pass 3 disassembly (semantic entry-point names + Sub_XXXX branch labels) | | `header.s` | iNES header include | | `chr.s` | CHR include | | `prg.bin` | 32768 bytes (32 KB) PRG image | | `chr.bin` | 8192 bytes (8 KB) CHR image | | `rom.nes` | byte-identical copy of the original ROM | | `linker.cfg` | HEADER / PRG / CHR memory areas | | `symbols.inc` | NES hardware register names | | `locals.inc` | zero-page aliases (semantic names applied at Pass 2/3) | | `Makefile` | build and byte-identity check | | `xevious_mesen.cdl` | Mesen Code Data Logger capture (CDLv2) used to correct the split |