# Urban Champion (JU) — Pass 3 Complete ROM: `Urban Champion (JU)/Urban Champion (JU).nes` (24,592 bytes, iNES). Status: **Pass 3 complete — every data block is interpreted and either named or recorded as unreferenced; whole ROM byte-identical.** `prg.s` contains a classified disassembly with per-instruction audit comments (`; CPU:$xxxx PRG:xxxx AA BB`), unreached bytes emitted as explicit `.byte` blocks with provenance. `make` verifies header + PRG + CHR against `rom.nes`. The Pass 1 split was re-done from the two Mesen Code Data Logger captures in this folder (`urban_baseline.cdl`, `urban_gameplay.cdl`, merged into `urban_merged.cdl`); every routine that the game calls by name now carries a semantic name, and the **data tables have now been interpreted** — see *Pass 3 data-table inventory* below. ## 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 `$CA00` (`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 | |---------|---------| | `$CA02` | `SubCCA02` (falls through) | | `$D07A` | `ClearZeroPageLowerHalf` | | `$CA21` | `SubCCA21` (falls through) | | `$D07E` | `SubCD07E` (falls through) | | `$CA2F` | `SubCCA2F` (falls through) | | `$CC67` | `DrawAttractModeBlock` | | `$CC77` | `HideAllSprites` | | `$C003` | `ResetSoundEngineState` | | `$E7EF` | `RequestSoundEffectMask2Bits1and6` | | `$CA5E` | `SubCCA5E` (falls through) | | `$CC6E` | `DrawAttractModeText` | | `$C063` | `ClearSoundEngineState` | **NMI** is at `$CA91` (`NmiHandler`). It is the per-frame handler; what it calls: | Address | Routine | |---------|---------| | `$CD41` | `DrawPpuBlockList` | | `$CC85` | `DrawQueuedPpuBlock` | | `$CCAF` | `DrawSpritePpuBlock` | **IRQ** is at `$CA00` (`Reset`) - the same address as RESET, so the IRQ vector reaches the reset path. ## ROM Facts | Property | Value | |----------|-------| | Mapper | 0 (NROM) — from the iNES header | | PRG | 16 KB (header claims map at CPU $C000–$FFFF) | | CHR | 8 KB | | Mirroring | Vertical | | Battery-backed flag | clear | | ROM SHA-256 | `3ec5ac8db623dae34a0f4a4909f0af4569b2af0cee6c56bae512c87b7cff8749` | | PRG SHA-256 | `13e7f2a77b01784a479895c0535357978af32f5139d971edf5f5b1936585357e` | ## Interrupt Vectors (taken from PRG $3FFA–$3FFF) | Vector | Target | Note | |--------|--------|------| | NMI | `$CA91` | per-frame heartbeat | | RESET | `$CA00` | non-$C000 entry at $CA00 — trace from here in Pass 1 | | IRQ | `$CA00` | redirects to RESET — unused placeholder | ## Project Contents | File | Purpose | |------|---------| | `Urban Champion (JU).nes` | original ROM image (reference) | | `prg.bin` / `header.bin` / `chr.bin` | split image, produced by `_tools/splitnes.py` | | `prg.s` | Pass 1: classified code/data split re-traced from the Mesen CDL captures, 4,464 instructions, 691 labels, audit comments on every emission; Pass 3: 72 data-table labels added (see the inventory below) | | `symbols.inc` | shared NES hardware constants (PPU/APU/controller) | | `locals.inc` | 153 zero-page aliases (generated `ZpXX` names) | | `linker.cfg` | PRG at $C000, 16 KB, `fill = yes` | | `Makefile` | links `reconstructed.nes` and `cmp`s it against `rom.nes` (whole ROM) | | `reconstructed.nes` | build output from the last verified build | | `urban_baseline.cdl`, `urban_gameplay.cdl`, `urban_merged.cdl` | Mesen Code Data Logger captures used for the Pass 1 re-split and for the data-table inventory | This project is promoted: the build links `header.s` + `prg.s` + `chr.s` and `make` compares the whole `reconstructed.nes` against `rom.nes`, so header, PRG and CHR are all proven. ## Build ```bash cd "NROM/Urban Champion (JU)" make # → cmp reconstructed.nes rom.nes → "Byte-identical ROM verified." ``` ## Pass 1 Details - **Tracer seeds**: RESET ($CA00), NMI ($CA91), plus `--auto-seed` recovered 34 table-referenced routines. - **Forced data override**: `$CB70+$3C` (a cascade of `bne` instructions that are actually a branch table/data, not code). The fall-through through this region was incorrectly classified as code; forced to data with `--data 0xCB70:60`. - **Overlaps**: 6 overlap regions remain (e.g. `$CB8F` `lsr` overlaps `$CB91`), all in the forced-data region — the conflicting decodes are suppressed in the output. - **Invalid opcodes**: 7 addresses reached via static trace but decoding invalid opcodes (`$C658`, `$CAF9`, `$CB4D`, `$CC61`, `$D21B`, `$DBCE`, `$DE24`). These are marked `.byte` with the invalid opcode in the audit comment. - **Indirect jumps**: 3 `jmp ($xxxx)` sites at `$CDFB` (`$0014`), `$C318` (`$00EB`), `$C332` (`$00ED`) — targets not statically resolvable. - **Zero-page aliases**: 153 entries in `locals.inc` (auto-generated by tracer). ## Open work - The **zero-page aliases** in `locals.inc` are the generated `ZpXX` names; the roles are not proven from the code alone. - **Eleven blocks decode as code the tracer could not reach** and would become routines if the tracer were re-seeded at their first byte. Three of them are confirmed executed by the merged CDL — `$CFF2-$D011` (32 bytes, every byte of which the CDL records as code), `$D0B7-$D0CC` and `$D46C-$D49C` — and they are the clearest targets for a re-trace. They are recorded as code in the inventory rather than being given table names. - **The meaning of the byte values in `ScrollStepTable` (`$DFEA`) is not established.** The table's shape, its index and what the code does with the two bytes of each pair are known; what the numbers mean is not. - **The role of `AuxiliaryStateInitialValues` (`$F6D6`)** — the third of three parallel 32-byte state blocks — is not established; the first two are the two fighters. - **The ROM does not name the ten sound slots**, so `SoundEffect1`..`SoundEffect10` are the slot numbers, not the sounds' names. ## Pass 3 data-table inventory Every `; ---- data $XXXX-$YYYY ----` region in `prg.s` is listed here. A region that is only partly table and partly code says so: the tracer's data blocks are contiguous runs of bytes it never reached, so a block can hold a table, a table *and* unreached code, or code alone. The evidence for each row is the consuming instruction in this dump, the bytes themselves, and the merged Mesen CDL capture (`urban_merged.cdl`), which records per PRG byte whether the CPU fetched it as an opcode (Code), read it as data (Data) or executed it. ### The sound engine The largest body of data in this ROM is the sound engine, and its layout falls out of the code: - The routine at `$C1B2` takes a sound slot in `Y`, reads the byte at `$C657+Y` — a **record offset** — and then reads nine bytes starting at `$C657+offset`. The nine bytes are a duration base and **four 16-bit pointers**, one per APU channel (pulse 1, pulse 2, triangle, noise). The pointers are copied to `$F8`/`$FA`/`$FC`/`$FE` and read with `lda (ZpF8),y`, `lda (ZpFA),y`, `lda (ZpFC),y` and `lda (ZpFE),y`. - A **note sequence** is a stream of command bytes: `$00` ends the sequence, `$01-$7F` is a pitch, `$80-$BF` is a duration marker whose *following* byte is the pitch, `$C0-$FE` holds a note and `$FF` stops. - A pitch indexes the **period table** at `$C100` (the APU timer high byte is `$C100+n` with bit 3 forced on, the low byte is `$C101+n`; a zero low byte means "no note"), and a duration marker indexes the **length table** at `$C172` through `(marker & $0F) + the record's base byte`. ### The two fighters The game keeps two parallel 32-byte state blocks in RAM at `$0300` and `$0320`. Their X positions are at `$0303` and `$0323`, and the routine at `$D1CE` subtracts them to measure the gap between the two fighters: a hit registers only when the gap is between 9 and 24 pixels. `$0305`/`$0325` hold the two fighters' move states, and `$0309`/`$0329` are the state bytes the draw routines test. The three tables at `$F696`, `$F6B6` and `$F6D6` are the initial values of those blocks. ### Inventory | Address | Size | Name | Structure | Read by | |---------|------|------|-----------|---------| | `$C06A-$C06D` | 4 | - | four bytes `$3A $7F $0E $40` sitting between the APU-init routine at `$C063` and the routine at `$C06E`. As little-endian words they are `$7F3A` and `$400E`; `$400E` is the APU noise-period register, but nothing in this dump names the pair, so no name is given | not referenced by any instruction | | `$C0A1-$C0AD` | 13 | `NoiseEnvelopeTable` | 13 x 8-bit APU `$400C` noise-control values, written one per frame while the counter `$010A` counts up; the final `$00` terminates the envelope | `lda $C0A1,y` at `$C08D` | | `$C100-$C18C` | 141 | `NotePitchPeriodTable` (`$C100`), `NoteDurationTable` (`$C172`) | `$C100-$C171` = 57 x 16-bit LE APU timer periods, indexed by the (even) pitch number: the high byte is `$C100+n` with bit 3 forced on, the low byte is `$C101+n`, and a zero low byte means "no note" (the code branches on it). `$C172-$C18C` = 27 x 8-bit note lengths in frames, indexed by `(duration marker & $0F) + the sound record's base byte` | `lda $C172,y` at `$C02C`, `lda $C101,y` at `$C035`, `lda $C100,y` at `$C03D` | | `$C1FE-$C20E` | 17 | `NoiseControlTable` (`$C200`), `NoisePeriodTable` (`$C201`), `NoiseLengthTable` (`$C202`) | `$C1FE-$C1FF` = two `$FF` bytes no instruction reaches. `$C200-$C20E` = three parallel 8-bit arrays of five APU noise values — control (`$400C`), period (`$400E`) and length (`$400F`) — indexed by the same sound index | `lda $C200,y` at `$C013`, `lda $C201,y` at `$C019`, `lda $C202,y` at `$C01F` | | `$C36E-$C375` | 8 | - | eight bytes (`$1E $7F $05 $18 $15 $7F $02 $88`) that repeat the shape of the noise-effect table at `$C200`; nothing in this dump names them | not referenced by any instruction | | `$C42C-$C443` | 24 | - | six 4-byte APU pulse-channel presets: duty/envelope (`$9F` = duty 2, length `$1F`, constant volume), sweep (`$83`, `$8B`, `$8D`, `$82`, `$93`), volume (`$E0`, `$FF`) and sweep target/period. Nothing in this dump names them | not referenced by any instruction | | `$C500-$C507` | 8 | - | two more 4-byte APU presets (`$B0 $A4 $96 $00` and `$B0 $A4 $8D $00`) | not referenced by any instruction | | `$C5A0-$C5A3` | 4 | - | four bytes `$95 $B5 $40 $B0`; read as little-endian words they are `$B595` and `$B040`, which point outside the PRG image, so they are not pointers | not referenced by any instruction | | `$C65C-$C65D` | 2 | `SoundEffectRecordOffset5`, `SoundEffectRecordOffset6` | two bytes of the sound-effect record area. The routine at `$C1B2` reads nine bytes per record from `$C657+v` to `$C65F+v` (v = the byte at `$C657+Y]`), and these two supply the low and high bytes of a record's third 16-bit sequence pointer. The same two bytes are entries 5 and 6 of the ten-entry record-offset table that starts at `$C657` (`$0A $13 $1C $25 $2E $37 $40 $49 $52 $5B`); the tracer emitted only `$C65C`/`$C65D` of that table as data because the six bytes before it are reached only by falling through the always-taken branch at `$C655` | `lda $C65C,y` at `$C1D5`, `lda $C65D,y` at `$C1DA` | | `$C666-$C9FF` | 922 | `SoundEffect1Sequence1` (`$C937`), `SoundEffect2Sequence1` (`$C810`), `SoundEffect2Sequence2` (`$C82B`), `SoundEffect2Sequence3` (`$C845`), `SoundEffect2Sequence4` (`$C91B`), `SoundEffect3Sequence1` (`$C874`), `SoundEffect3Sequence2` (`$C85F`), `SoundEffect3Sequence3` (`$C87E`), `SoundEffect3Sequence4` (`$C893`), `SoundEffect4Sequence1` (`$C93D`), `SoundEffect4Sequence2` (`$C958`), `SoundEffect4Sequence3` (`$C972`), `SoundEffect5Sequence1` (`$C8A7`), `SoundEffect5Sequence2` (`$C8D3`), `SoundEffect5Sequence3` (`$C8FE`), `SoundEffect6Sequence1` (`$C983`), `SoundEffect6Sequence2` (`$C99C`), `SoundEffect6Sequence3` (`$C9B2`), `SoundEffect6Sequence4` (`$C9C8`), `SoundEffect7Sequence3` (`$C76A`), `SoundEffect7Sequence4` (`$C7E9`), `SoundEffect8Sequence1` (`$C6BB`), `SoundEffect8Sequence2` (`$C6FD`), `SoundEffect8Sequence3` (`$C73B`), `SoundEffect8Sequence4` (`$C761`), `SoundEffect9Sequence3` (`$C799`), `SoundEffect10Sequence3` (`$C7C6`) | the sound-effect record table and the note sequences its pointers name. The ten nine-byte records sit at `$C661`+9n (a duration base byte and four 16-bit pointers, one per APU channel); the record area straddles the block's first boundary, because the tracer emitted `$C661`/`$C662` in a `.byte` row before it and decoded `$C663-$C665` as instructions. The twenty-seven sequences that follow are zero-terminated command streams (see *The sound engine* above). `SoundEffect7Sequence4` is shared by slots 7, 9 and 10; `SoundEffect2Sequence4` is shared by slots 2, 4 and 5 | `lda (ZpF8),y` at `$C225` and `$C239`, `lda (ZpFA),y` at `$C263` and `$C274`, `lda (ZpFC),y` at `$C29E` and `$C2CA`, `lda (ZpFE),y` at `$C2F4` and `$C303`, through the pointers in the records at `$C661`+9n | | `$CAF8-$CAF8` | 1 | - | a single `$F0` — the BEQ opcode — between `cpy #$03` at `$CAF6` and the routine at `$CAF9` | not referenced by any instruction | | `$CB4A-$CB4A` | 1 | - | a single `$A5` (LDA immediate) between two instructions | not referenced by any instruction | | `$CB4D-$CB4D` | 1 | - | a single `$03`; the tracer reached it but the opcode is not a valid 6502 instruction | not referenced by any instruction | | `$CB6B-$CB73` | 9 | - | the branch cascade around `$CB70`. The bytes decode as 4 consecutive instructions, but the merged CDL records six of them being **read as data** and three as jump targets, so they are the branch offsets of a dispatch table rather than code | not referenced by any instruction; the merged CDL records six of the bytes being read as data and three as jump targets | | `$CB76-$CB77` | 2 | - | unreferenced; two bytes in the branch cascade around `$CB70` | not referenced by any instruction | | `$CB7E-$CB81` | 4 | - | unreferenced; four bytes in the branch cascade around `$CB70` | not referenced by any instruction | | `$CB8E-$CB8E` | 1 | - | a single `$CC` (CPY immediate) | not referenced by any instruction | | `$CB92-$CB92` | 1 | - | a single `$5E`; the merged CDL records it as an opcode fetch, so it is the start of an instruction the tracer could not reach | not referenced by any instruction | | `$CB9C-$CB9C` | 1 | - | a single `$A2` (LDX immediate); the merged CDL records it as an opcode fetch | not referenced by any instruction | | `$CBA2-$CBA2` | 1 | - | a single `$A2` (LDX immediate); the merged CDL records it as an opcode fetch | not referenced by any instruction | | `$CC61-$CC66` | 6 | - | three 16-bit LE pointers into the PRG image (`$CFC3`, `$CFF2`, `$D00F`) — a dispatch table. No instruction in this dump names it, but the merged CDL records all six bytes being read as data: the indirect read that reaches it is one the tracer cannot follow | not referenced by any instruction (read indirectly, per the CDL) | | `$CFF2-$D011` | 32 | - | unreferenced by any instruction, but the merged CDL records **every** byte of it as code and the block was executed. It is code the tracer could not reach, not data | not referenced by any instruction (executed, per the CDL) | | `$D0B7-$D0CC` | 22 | - | unreferenced; 18 of the 22 bytes are recorded as code by the merged CDL — code the tracer could not reach | not referenced by any instruction | | `$D152-$D173` | 34 | - | unreferenced; decodes as a straight-line routine (`lda $2B` / `bne` / `lda #$01` / `sta $0301` / `sta $0321` / `sta $0341` / `lda #$03` / `sta $25` / `rts` / the same stores with `$00` / `jmp $D1A4`) — code the tracer could not reach | not referenced by any instruction | | `$D21B-$D242` | 40 | - | 20 x 16-bit LE code pointers into the PRG image (`$D2B2`, `$D26E`, `$D361`, `$D3FA`, `$D398`, `$D36C`, `$D4C4`, `$D5A2`, `$D5C4`, `$D656`, `$D501`, `$D6AB`, `$D261`, `$D80C`, `$D834`, `$D875`, `$D8B4`, `$D8EA`, `$D916`, `$D97D`) — a dispatch table. No instruction in this dump names it; the merged CDL records ten of its bytes being read as data | not referenced by any instruction (read indirectly, per the CDL) | | `$D344-$D34A` | 7 | - | unreferenced; decodes as `lda $88` / `beq` / `jmp $D29E` — code the tracer could not reach | not referenced by any instruction | | `$D361-$D366` | 6 | - | unreferenced; decodes as `lda $3E` / `beq` / `dec $3E` — code the tracer could not reach | not referenced by any instruction | | `$D46C-$D49C` | 49 | - | unreferenced; 42 of the 49 bytes are recorded as code by the merged CDL — code the tracer could not reach | not referenced by any instruction | | `$D656-$D7DA` | 389 | - | unreferenced; the merged CDL records no execution of it, but the bytes decode as 183 consecutive instructions, so it is code the tracer could not reach rather than data | not referenced by any instruction | | `$D8B4-$DA10` | 349 | - | unreferenced; decodes as 166 consecutive instructions — code the tracer could not reach | not referenced by any instruction | | `$DAD9-$DB04` | 44 | - | unreferenced; decodes as 20 consecutive instructions (`lda $A0` / `beq` / `lda #$32` / `ldx #$14` / `sta $0500,x` / `dex` / `bpl` / the PPU buffer setup) — code the tracer could not reach | not referenced by any instruction | | `$DBCE-$DBD0` | 3 | - | three bytes that decode as the undocumented opcode `$DB` and its operand; not a table | not referenced by any instruction | | `$DDD1-$DDF6` | 38 | - | unreferenced; decodes as 21 consecutive instructions — code the tracer could not reach | not referenced by any instruction | | `$DE24-$DE26` | 3 | - | the first three bytes of a six-entry 16-bit LE code-pointer table (`$DE36`, `$DEC9`, `$DED8`, `$DEF0`, `$DEF4`, `$DF19`) that the tracer split in two, because it decoded the bytes in between as instructions. No instruction in this dump names it; the merged CDL records the three bytes being read as data | not referenced by any instruction (read indirectly, per the CDL) | | `$DE2D-$DE30` | 4 | - | the tail of the same six-entry code-pointer table. The merged CDL records two of its bytes being read as data | not referenced by any instruction (read indirectly, per the CDL) | | `$DEC9-$DECC` | 4 | - | unreferenced; decodes as `lda #$01` / `sta $19` — code the tracer could not reach | not referenced by any instruction | | `$DFEA-$E03F` | 86 | `ScrollStepTable` (`$DFEA`), `ScrollTargetTable` (`$E00C`) | `$DFEA-$E00B` = 17 x (byte, byte) pairs indexed by the current scroll row (`$8F` x 2): the first byte is loaded into `$8E` and counted down (the code at `$DF42` tests it and `$DF53` decrements it), the second into `$AC`. `$E00C-$E03F` = 25 x (X, Y) pairs — the scroll targets — indexed by the waypoint number (`$71` x 2) and used by the loop at `$DFDA` that walks the background scroll to the target. What the byte *values* mean is not established | `lda $DFEA,x` at `$DF60`, `lda $DFEB,x` at `$DF65`, `lda $E00C,x` at `$DFCB`, `lda $E00D,x` at `$DFD0` | | `$E082-$E08C` | 11 | - | eleven bytes the merged CDL records as data but that no instruction in this dump names; as little-endian words they are `$E9E0`, `$FCE0`, `$1DE0`, `$C0E1`, `$ABE1`, which is not a recognisable structure | not referenced by any instruction (read indirectly, per the CDL) | | `$E3C6-$E3DF` | 26 | `SpriteTileIndexTable` (`$E3C6`), `SpriteAttributeTable` (`$E3C7`), `AlternateSpriteTileTable` (`$E3D7`) | `$E3C6-$E3D6` and `$E3C7-$E3D7` are two parallel 17-byte tables indexed by the sprite number `$72`: a CHR tile index (copied to the PPU write buffer at `$0501`) and an OAM attribute byte (kept in `$0C` and OR-ed into the sprite's attribute at `$E6A6`). `$E3D7-$E3DF` = nine more CHR tile indices, copied to `$04F7-$04FF` when `$72` is `$12` | `lda $E3C7,x` at `$E35A`, `lda $E3C6,x` at `$E35F`, `lda $E3D7,x` at `$E390` | | `$E813-$FFFF` | 6125 | `SpriteOffsetTable` (`$E8E5`), `PpuBlockPointerTable1` (`$F5B2`), `PpuBlock1` (`$F5BE`), `PpuBlock2` (`$F5D6`), `PpuBlock3` (`$F5FA`), `PpuBlock4` (`$F61E`), `PpuBlock5` (`$F642`), `PpuBlock6` (`$F666`), `InitialOamRecords` (`$F68A`), `Player1StateInitialValues` (`$F696`), `Player2StateInitialValues` (`$F6B6`), `AuxiliaryStateInitialValues` (`$F6D6`), `FighterOamRecords` (`$F6F6`), `Player1TileStrip` (`$F747`), `Player2TileStrip` (`$F75C`), `PpuBlockPointerTable2` (`$F771`), `ScriptBlockPointerTable` (`$F77D`), `ScriptBlock1` (`$F7AF`), `ScriptBlock2` (`$F7B6`), `ScriptBlock3` (`$F7BD`), `ScriptBlock4` (`$F7C4`), `ScriptBlock5` (`$F7CE`), `ScriptBlock6` (`$F7D7`), `ScriptBlock7` (`$F7E0`), `ScriptBlock8` (`$F7E3`), `ScriptBlock9` (`$F7E6`), `ScriptBlock10` (`$F7E9`), `FighterMotionScript` (`$F7F1`), `BackgroundOamRecords` (`$F8CC`), `StatusTileList` (`$F941`), `PpuBlockPointerTable3` (`$F949`), `PpuBlockPointerTable4` (`$F9CF`) | the game's graphics and script data. `$E8E5-$E8F4` = 16 per-sprite position offsets. `$F5B2-$F5BD` = six 16-bit LE pointers to **PPU write blocks** — each block is `[PPU address high][PPU address low][tile count][tile bytes]`, and the routine at `$CDFE` writes them to the nametable; the six blocks at `$F5BE`, `$F5D6`, `$F5FA`, `$F61E`, `$F642` and `$F666` write to `$3F00`. `$F68A-$F695` = three identical 4-byte records `$00 $01 $20 $00`, copied to `$05F0-$05FB` by the reset path at `$CA5E` (nothing in this dump reads that RAM). `$F696`, `$F6B6` and `$F6D6` = the three 32-byte initial values of the RAM state blocks at `$0300`, `$0320` and `$0340` (the first two are the two fighters — see *The two fighters* above). `$F6F6-$F745` = twenty 4-byte OAM records (Y, tile, attribute, X) for a fighter graphic, ended by a `$2F` Y byte. `$F747` and `$F75C` = the two players' tile strips, each a list of CHR tile indices ended by `$FF`, drawn when the player's state byte `$0309`/`$0329` is 1. `$F771-$F77C` = five more PPU write blocks (plus a null entry). `$F77D-$F790` = ten 16-bit LE pointers to **script blocks**: each is a list of (value, value) pairs ended by `$FE` or `$FF`. `$F7F1` = the fighter motion script: (dx, dy) pairs added to the fighter's position at `$32`/`$33`, with `$FD` (a one-byte parameter), `$FE` (branch back three bytes) and `$FF` (end) as commands. `$F8CC-$F8E7` = seven more 4-byte OAM records, copied to `$0240-$025B` (the OAM shadow's sprites 16-22). `$F941-$F948` = eight CHR tile indices copied to the PPU write buffer at `$0500`. `$F949` and `$F9CF` = two more pointer tables to nametable row blocks | `lda $F68A,x` at `$CA5E`, `lda $F5B2,y` at `$CC8D` and `ldx $F5B3,y` at `$CC90`, `lda $F771,x` at `$D178` and `ldy $F772,x` at `$D17B`, `lda $F747,x` at `$DA4F`, `lda $F75C,x` at `$DA80`, `lda $F940,y` at `$DBAA`, `lda $F6FA,y` at `$DCD4`, `lda $F6F6,y` at `$DCE7`, `lda $F8CC,x` at `$DDF9`, `lda $F9CF,x` at `$E23C` and `lda $F9D0,x` at `$E241`, `lda $F7F1,y` at `$E41A`, `$E42F`, `$E435`, `$E43B` and `$E444`, `lda $F696,x` at `$E4C2`, `lda $F6B6,x` at `$E4CD`, `lda $F6D6,x` at `$E4D8`, `lda $F77D,y` at `$E4FE` and `ldx $F77E,y` at `$E501`, `lda $E8E5,x` at `$E65C` and `$E6B1`, `lda $F949,x` at `$E71A` and `lda $F94A,x` at `$E71F` | ### Notes on the blocks that are not tables - The blocks that "decode as N consecutive instructions" are code, not data: the tracer never reached them, so they are still emitted as `.byte` rows, but their contents are instructions. They are left unnamed here on purpose; seeding the tracer at the first byte of each would turn them back into routines. The merged CDL confirms three of them (`$CFF2-$D011`, `$D0B7-$D0CC` and `$D46C-$D49C`) were actually executed during the recorded session. - The one-byte regions at `$CAF8`, `$CB4A`, `$CB4D`, `$CB8E`, `$CB92`, `$CB9C` and `$CBA2` hold a single byte each and sit between two instructions; they are stray bytes, not tables. - `$C06A-$C06D`, `$C36E-$C375`, `$C42C-$C443`, `$C500-$C507` and `$C5A0-$C5A3` all sit in and around the sound engine and hold APU constants, but no instruction in this dump names them, so they are recorded here with their contents rather than given a name. - `$CC61-$CC66`, `$D21B-$D242`, `$DE24-$DE26`, `$DE2D-$DE30` and `$E082-$E08C` are read by the CPU — the CDL says so — but not by any instruction this dump contains: the reads are indirect and the tracer cannot follow them. They are left unnamed for the same reason. - The layout of the `$F6F6` OAM record list and of the ten `ScriptBlock`s is established, but the *game* names behind them are not: the ROM never says which sound is which slot, and the ten script blocks are not identified either. ## Pass 2 Details The Pass 1 split was re-done from the Mesen CDL captures after the project was promoted, so the semantic renaming was redone on the new split. What the dump establishes about the game's structure is recorded here and in the data-table inventory above. - **Routine names**: every routine the game reaches through a `jsr` or a `jmp` carries a semantic name. The sound engine is named for what it does to the APU (`UpdateSoundEngine`, `UpdateApuChannels`, `LoadSoundEffectRecord`, `WritePulse1PeriodAndTimer`, `LookupNoteDuration`, ...), the graphics side for what it draws (`DrawPpuBlockList`, `DrawQueuedPpuBlock`, `QueuePpuBlocks`, `HideAllSprites`, ...), the controller code for what it reads (`ReadControllers`, `ReadControllerButtons`), and the sound-request helpers for the mask bit each of them sets (`RequestSoundEffectSlot0`, `RequestPulse1Preset0`, `StopAllSound`, ...). The three vector entries (`Reset`, `NmiHandler`) and the 72 data-table labels are named as well. The old `label_map.tsv` and `routinemap.tsv` in this folder refer to the pre-re-split disassembly and no longer match `prg.s`. - **Zero-page aliases**: the generated `ZpXX` names in `locals.inc` are still in place; the behavioural aliases of the earlier pass have to be re-derived. - **The 4,464 traced instructions** are unchanged in the re-split; what changed is which of them the tracer could reach. ## Mesen CDL Verification This folder holds three Code Data Logger captures: `urban_baseline.cdl`, `urban_gameplay.cdl` and their merge `urban_merged.cdl`. The jump-target flag in a capture names the addresses the CPU actually branched to, and re-tracing with those seeds reaches the code behind the game's computed dispatch. Every "code the tracer could not reach" row of the inventory above was checked against the merged capture. | Region | Merged CDL | Disposition | |--------|-------------|-------------| | `$C006-$C025`, `$C032-$C04D`, `$C22F-$C24C`, `$C26A-$C287`, `$C2A8-$C2DD`, `$C2F9-$C30B` | CODE | missed init routines — now code in the re-split | | `$CFC3-$D011`, `$D09D-$D173`, `$D469-$D7DA`, `$DBFD-$DCF5`, `$DE21-$DEC8`, `$DED8-$DF4B`, `$E11D-$E1CC`, `$E350-$E40D` | CODE | missed routines — now code in the re-split | | `$CFF2-$D011` | **CODE, and executed** | still a data block: the tracer cannot follow the indirect read that reaches it | | `$D0B7-$D0CC`, `$D46C-$D49C` | CODE | still data blocks; code the tracer could not reach | | `$CB6B-$CBA2` | DATA | the branch cascade is dead — confirmed | | `$CC61-$CC66`, `$D21B-$D242`, `$DE24-$DE30`, `$E082-$E08C` | DATA | read by the CPU through an indirect read the tracer cannot follow |