Files
disasm/_wip/Urban Champion (JU)/OVERVIEW.md
T
tmerritt b7104a8f80 MUSCLE (U): Fix verify_pass1.py crash (a: prefix parsing), CDL reclass overrides
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
2026-10-18 20:04:34 -04:00

107 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `cmp`s 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
```bash
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-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).
## Next Steps (per `PROMPTS.md`)
1. **Pass 3** — name data tables/structures; release provisional flags.
2. **Promotion** — move to a top-level `<name>/copilot/` folder, add full-ROM build (header.s + chr.s + vectors), and write the power-on flow `OVERVIEW.md` in 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 apply` with 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`
- **Zero-page semantic aliases**: 10 aliases added to `locals.inc` (see `locals.inc` header 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)
- **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`.