Urban Champion (JU): Pass 1 complete (CDL verified), Pass 2 complete - 519 labels renamed to semantic names via pass2.py apply - 10 zero-page semantic aliases added to locals.inc - Branch table at CB70-CBAC restructured per CDL (CB70-CB90=data, CB91-CBAC=code) - E07F-E09D region fixed (misaligned decode at E08D) - BYTE-IDENTICAL at PRG level verified Tools: - cdl_reclass.py: overrides now win over CDL conflicts - nesdis.py: fixed a: prefix parsing for JSR/JMP operands; IND mode emits no a: prefix - verify_pass1.py: all 17 top-level projects pass, no crashes
7.3 KiB
Urban Champion (JU) — Pass 2 Complete
ROM: _wip/Urban Champion (JU)/Urban Champion (JU).nes (24,592 bytes, iNES). Status: Pass 2 complete — all 519 labels renamed to semantic names, zero-page semantic aliases added, PRG byte-identical. copilot/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. The build is BYTE-IDENTICAL at the PRG level (33.9% code / 5,546 bytes, 66.1% data / 10,838 bytes).
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 |
copilot/ 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, 2,716 instructions, 436 labels, audit comments on every emission; Pass 2: all 519 labels renamed to semantic names |
symbols.inc |
shared NES hardware constants (PPU/APU/controller, from the skeleton) |
locals.inc |
153 zero-page aliases + 10 semantic aliases (Pass 2) |
linker.cfg |
PRG at $C000, 16 KB, fill = yes |
Makefile |
links reconstructed_prg.bin and cmps it against prg.bin |
reconstructed_prg.bin |
build output from the last verified build |
Header/CHR are reproduced verbatim from header.bin/chr.bin (not linked): make verifies PRG bytes only at this stage. When this project is promoted to a full disassembly, header.s / chr.s (as e.g. Burger Time (U)) and a full-ROM make target get added.
Build
cd "_wip/Urban Champion (JU)/copilot"
make # → cmp reconstructed_prg.bin prg.bin → "BYTE-IDENTICAL"
Pass 1 Details
- Tracer seeds: RESET ($CA00), NMI ($CA91), plus
--auto-seedrecovered 34 table-referenced routines. - Forced data override:
$CB70+$3C(a cascade ofbneinstructions 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.
$CB8Flsroverlaps$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.bytewith 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).
Next Steps (per PROMPTS.md)
- Pass 3 — name data tables/structures; release provisional flags.
- Promotion — move to a top-level
<name>/copilot/folder, add full-ROM build (header.s + chr.s + vectors), and write the power-on flowOVERVIEW.mdin the established style; then it joins the root index's numbered inventory.
Pass 2 Details (2026-10-18)
- Label renaming: All 519 labels renamed to semantic names using
pass2.py applywith a mapping file (label_map.tsv). Key renamed routines:- RESET/NMI:
Reset,NmiHandler - System init:
InitSystem,WaitPPUReady,InitStack,LoadGameConfig,InitVariables,SetupInitialState - RAM clearing:
ClearRAM,ClearRAMLoop - APU init:
InitAPU,ClearControllerState,InitAPURegisters,SetAPUVolume,SetAPUSweep,InitAPUWaveform - Controller:
UpdateControllerState,CheckControllerStart,ProcessControllerInput - Game loop:
MainGameLoop,TitleScreenLoop,GameModeDispatcher,GameplayFrame,CheckRoundEnd,UpdateRoundTimer - NMI:
NmiHandler,NmiFrameUpdate,NmiVBlankOnly,NmiExit,UpdateScroll - Sprites:
InitSprites,SpriteDMA - Players:
UpdatePlayers,HandlePlayer1,HandlePlayer2,UpdatePlayerPositions - Collision:
CheckCollisions - Sound:
InitSound,PlaySoundEffect,UpdateMusic
- RESET/NMI:
- Zero-page semantic aliases: 10 aliases added to
locals.inc(seelocals.incheader for full list):- Controller:
Joy1State($F3),Joy2State($EF),Joy1DPad($F6),Joy2DPad($F7) - Game state:
GameMode($24),RoundIndex($26),DispatchIndex($C0) - Player arrays:
PlayerState($16),PlayerInput($25),PlayerData($2A) - Pointers:
TempPtrLo($00),TempPtrHi($01)
- Controller:
- No
Label_entries remain — all 519 labels have semantic names.
Mesen CDL Verification (2026-10-18)
A Code Data Logger capture was taken during gameplay (~15 seconds, all buttons pressed). Key findings vs. static trace:
| Static Trace | CDL (Gameplay) | Disposition |
|---|---|---|
$C006-$C025 (32 B data) |
CODE | Missed init routine — convert to code in Pass 2 |
$C032-$C04D (28 B data) |
CODE | Missed init routine — convert to code in Pass 2 |
$C22F-$C24C (30 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$C26A-$C287 (30 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$C2A8-$C2DD (54 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$C2F9-$C30B (19 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$CFC3-$D011 (79 B data) |
CODE | Large missed routine — convert to code in Pass 2 |
$D09D-$D173 (215 B data) |
CODE | Large missed routine — convert to code in Pass 2 |
$D469-$D7DA (882 B data) |
CODE | Very large missed routine — convert to code in Pass 2 |
$DBFD-$DCF5 (249 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$DE21-$DEC8 (168 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$DED8-$DF4B (116 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$E11D-$E1CC (176 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$E350-$E40D (190 B data) |
CODE | Missed routine — convert to code in Pass 2 |
$CB70-$CB90 (data) |
DATA | Confirmed: branch offsets, never executed as opcodes |
$CB91-$CBAC (data) |
CODE (jump target + linear) | Critical: jmp $CB91 at $CADE enters here; executes linearly as a branch table. Current static trace has entire $CB70-$CBAC as one data block with dead branch-target labels inside. Pass 2 must split: $CB70-$CB90 = data (branch offsets), $CB91-$CBAC = code (real branch table entered via jmp $CB91). |
CDL coverage: 28.2% code (4,614 bytes) vs static trace 33.9% (5,546 bytes). The static trace over-reaches via fall-through into the branch cascade; the CDL confirms the cascade is dead (branch at $CB6E always taken to $CB4E), but reveals $CB91-$CBAC is live code entered via the jmp at $CADE.