Promote Joypad Test Cartridge (U) from _wip to top-level project
Moves Joypad Test Cartridge (U) from _wip/ to Joypad Test Cartridge (U)/ with full ROM build: - Adds header.s, chr.s, linker.cfg (HEADER+PRG+CHR segments) - Updates Makefile to build and verify reconstructed.nes vs rom.nes - Adds rom.nes copy + copilot/rom.nes symlink per repo convention - Verified byte-identical build Pass 1 complete: full code/data classification (~3.5% code / ~96.5% data) Pass 2 complete: routine headers for all labels, semantic naming for key routines (Reset, NmiHandler, VramWrite, PpuScrollLatch, VramPtrWrite, RetAddrUpdate, etc.), zero-page aliases in locals.inc Root OVERVIEW.md updated: - Removes Joypad Test Cartridge from _wip skeleton table (22 -> 21 skeletons) - Adds Joypad Test Cartridge as #14 in numbered inventory (BYTE-IDENTICAL, Pass 2) - Updates summary count from 13 to 14 projects
This commit is contained in:
1 parent
c79a52e354
commit
b43b4a149d
24 files changed
+1963
-87
No files matched your search
@@ -0,0 +1,182 @@
|
||||
# Joypad Test Cartridge (U) — Game Flow from Power-On
|
||||
|
||||
This document describes the execution flow of the disassembled Joypad Test Cartridge (U) PRG bank (16 KB, mapped at CPU **$C000–$FFFF**), starting from power-on. It is based entirely on `copilot/prg.s` in the repository. The rebuild is verified **byte-identical** to the original `prg.bin`.
|
||||
|
||||
---
|
||||
|
||||
## 1. ROM and Memory Layout
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Format | iNES |
|
||||
| PRG | 1 × 16 KB (mapped at CPU $C000–$FFFF) |
|
||||
| CHR | 1 × 8 KB |
|
||||
| Mirroring | Horizontal |
|
||||
| PRG bank start | CPU $C000 |
|
||||
| Source file | `copilot/prg.s` (~1,360 lines with headers) |
|
||||
| Output | `reconstructed_prg.bin` (16,384 bytes) |
|
||||
|
||||
### Interrupt vectors (CPU $FFFA–$FFFF)
|
||||
|
||||
| Vector | Address | Handler |
|
||||
|--------|---------|---------|
|
||||
| NMI | $CCF7 | `NmiHandler` |
|
||||
| RESET | $C000 | `Reset` |
|
||||
| IRQ / BRK | $C000 | Reuses `Reset` |
|
||||
|
||||
---
|
||||
|
||||
## 2. Build Status
|
||||
|
||||
`make` in `copilot/` assembles `prg.s` with `ca65`, links with `ld65`, and verifies against `prg.bin`.
|
||||
|
||||
**Verification result: BYTE-IDENTICAL** — `reconstructed_prg.bin` matches `prg.bin` exactly (all 16,384 bytes), and `prg.bin` matches the PRG bank of `Joypad Test Cartridge (U).nes`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Pass Status
|
||||
|
||||
| Pass | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| Pass 1 | Complete | Code/data split — ~580 bytes traced code (~3.5%) or explicit `.byte` data (~96.5%) with `; CPU:$xxxx PRG:$xxxx` annotations |
|
||||
| Pass 2 | Complete | Routine headers added for all labeled positions; key routines semantically named (`Reset`, `NmiHandler`, `VramWrite`, `PpuScrollLatch`, etc.); zero-page aliases in `locals.inc` |
|
||||
| Pass 3 | Partial | Some data table references named in code; larger data regions (test patterns, button mappings) remain as `.byte` blocks |
|
||||
|
||||
---
|
||||
|
||||
## 4. Reset — CPU $FFFC → $C000
|
||||
|
||||
**`Reset`** ($C000)
|
||||
|
||||
On power-on or reset:
|
||||
|
||||
1. **Wait for three PPU warm-up vblanks** — `WaitVblank2` ($C005) and `WaitVblank3` ($C00A) each poll `$2002` bit 7, following an initial poll at $C000.
|
||||
2. **Clear RAM** — `ClearRAM1_Loop` ($C013) clears pages $0000–$07FF via indirect indexed store (`STA VramPtrLo,X` / `STA $0100,X` etc.).
|
||||
3. **Initialize PPU** — `InitPPU_1` ($C031): `STA $2000` with $10 (PPUCTRL, NMI disabled), shadow in `PpuCtrlShadow` ($EF); `STA $2001` with $00 (PPUMASK off), shadow in `PpuMaskValue` ($EE).
|
||||
4. **OAM DMA** — `OamDma` ($C03F): `STA $4014` with $02 (DMA from $0200–$02FF).
|
||||
5. **Clear controller state** — `ClearController` ($C044): clears $2003 (OAMADDR) and state variables.
|
||||
6. `JSR PpuScrollLatch` ($CE3C) — latch scroll and control registers.
|
||||
7. `LDX #$FF / TXS` — initialize stack pointer.
|
||||
8. `JSR PpuMaskSetup` ($CD2F) — set PPUMASK from shadow.
|
||||
9. **Clear RAM again** — `ClearRAM2_Loop` ($C05D) clears pages $0000–$06FF.
|
||||
10. **Re-initialize PPU** — `InitPPU_2` ($C078): same as InitPPU_1.
|
||||
11. **VRAM write** — `VramWriteSetup1` ($C086): set up address/length for a VRAM write via `VramWrite` ($CDEE).
|
||||
12. `JSR ClearOAM_Routine` ($C174) — clear OAM with sprite flush value $F4.
|
||||
13. `JSR VramPtrWrite` ($CD3F) — write VRAM data via pointer table.
|
||||
14. `ToggleFlag` ($C095) — toggle `VramToggleFlag` ($CF).
|
||||
15. `JSR PpuScrollLatch` ($CE3C) and `JSR VramPtrWrite` ($CD3F) again.
|
||||
16. **Main loop** — spins, toggling $CF each iteration and calling PPU/VRAM update routines.
|
||||
|
||||
---
|
||||
|
||||
## 5. NMI / Frame Heartbeat — `NmiHandler` ($CCF7)
|
||||
|
||||
The NMI fires once per frame:
|
||||
|
||||
1. **Save registers** (`PHA / TXA / PHA / TYA / PHA`).
|
||||
2. `JSR PpuCtrlSetup` ($CD17) — latch PPUSTATUS, clear NMI bit in `PpuCtrlShadow` ($EF), write to PPUCTRL.
|
||||
3. `JSR OamDmaSetup` ($CD24) — set OAMADDR=0, trigger OAM DMA ($4014=$02).
|
||||
4. Check `$0300` (vblank flag) — if nonzero, `JSR VramBurstWrite` ($CE4F) to burst-write VRAM data from table at $0302.
|
||||
5. `JSR PpuScrollLatch` ($CE3C) — latch scroll and control.
|
||||
6. Clear `NmiFlagByte` ($E0) to 0.
|
||||
7. **Restore registers** (`PLA / TAX / PLA / TAY / PLA / RTI`).
|
||||
|
||||
---
|
||||
|
||||
## 6. Key Subroutines
|
||||
|
||||
| Label | Address | Description |
|
||||
|-------|---------|-------------|
|
||||
| `Reset` | $C000 | Cold-start initialization: vblank waits, RAM clear, PPU init, OAM DMA |
|
||||
| `WaitVblank2` | $C005 | Wait for second PPU warm-up vblank |
|
||||
| `WaitVblank3` | $C00A | Wait for third PPU warm-up vblank |
|
||||
| `ClearRAM1_Loop` | $C013 | Clear RAM pages $00–$07 |
|
||||
| `InitPPU_1` | $C031 | Initialize PPUCTRL and PPUMASK |
|
||||
| `OamDma` | $C03F | Trigger OAM DMA via $4014 |
|
||||
| `ClearController` | $C044 | Clear OAMADDR and controller state |
|
||||
| `PpuScrollLatch` | $CE3C | Latch PPU scroll X/Y and control register |
|
||||
| `InitStack` | $C052 | Initialize stack pointer to $FF |
|
||||
| `PpuMaskSetup` | $CD2F | Set PPUMASK from shadow register |
|
||||
| `ClearRAM2_Loop` | $C05D | Clear RAM pages $00–$06 |
|
||||
| `InitPPU_2` | $C078 | Re-initialize PPU control and mask |
|
||||
| `VramWriteSetup1` | $C086 | Set up VRAM write parameters |
|
||||
| `ClearOAM_Call` | $C08F | Call OAM clear routine |
|
||||
| `VramPtrWrite1` | $C092 | Write VRAM data via pointer table |
|
||||
| `ToggleFlag` | $C095 | Toggle VramToggleFlag ($CF) |
|
||||
| `SetupScroll2` | $C097 | Latch PPU scroll and control |
|
||||
| `VramPtrWrite2` | $C09A | Write VRAM data via pointer table again |
|
||||
| `ClearOAM_Routine` | $C174 | Clear OAM ($0200–$02FF) with $F4 |
|
||||
| `NmiHandler` | $CCF7 | Per-frame NMI: PPU updates, OAM DMA, VRAM burst write |
|
||||
| `NmiContinue` | $CD0A | Continue NMI: latch PPU scroll, clear flag |
|
||||
| `PpuCtrlSetup` | $CD17 | Latch PPUSTATUS, write PPUCTRL from shadow |
|
||||
| `OamDmaSetup` | $CD24 | Set OAMADDR=0, trigger OAM DMA |
|
||||
| `PpuMaskSetup` | $CD2F | Set PPUMASK from shadow |
|
||||
| `VramPtrWrite` | $CD3F | Write VRAM via pointer table (uses RetAddrUpdate) |
|
||||
| `VramWriteLoop` | $CD45 | Loop writing bytes from indirect pointer to PPUDATA |
|
||||
| `VramPtrTableRead` | $CD99 | Read from pointer table at $0000 |
|
||||
| `RetAddrUpdate` | $CDC8 | Get return address from stack, advance by 2 |
|
||||
| `VramWrite` | $CDEE | Set PPU address, write data bytes to PPUDATA |
|
||||
| `VramBurstWrite` | $CE4F | Burst-write VRAM from table at $0302 |
|
||||
|
||||
---
|
||||
|
||||
## 7. Zero-Page Aliases (`locals.inc`)
|
||||
|
||||
| Alias | Address | Role |
|
||||
|-------|---------|------|
|
||||
| `VramPtrLo` | $00 | VRAM write pointer low byte |
|
||||
| `VramPtrHi` | $01 | VRAM write pointer high byte |
|
||||
| `VramLen` | $02 | VRAM write data length |
|
||||
| `PpuScrollX` | $03 | PPU horizontal scroll |
|
||||
| `RetAddrTempLo` | $05 | Return address temp lo |
|
||||
| `RetAddrTempHi` | $06 | Return address temp hi |
|
||||
| `PpuMaskShadow` | $0E | PPUMASK shadow |
|
||||
| `PpuCtrlShadow` | $EF | PPUCTRL shadow (NMI bit cleared) |
|
||||
| `VramToggleFlag` | $CF | Frame toggle flag |
|
||||
| `NmiFlagByte` | $E0 | NMI flag byte |
|
||||
| `JoyBtnStateBase` | $4E | Base for indexed button states |
|
||||
| `FrameCounterLo` | $53 | Frame counter low byte |
|
||||
| `FrameCounterHi` | $54 | Frame counter high byte |
|
||||
|
||||
Full list in `copilot/locals.inc`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Data Regions
|
||||
|
||||
The PRG is approximately 3.5% code / 96.5% data. The data regions include:
|
||||
|
||||
- **$C09E–$C173** (214 bytes): Main loop inline code/data (pointer table references, VRAM write parameters)
|
||||
- **$C17F–$CCF6** (2,936 bytes): Test patterns, button mapping tables, screen data
|
||||
- **$CD39–$CD3E** (6 bytes): PPU mask toggle code fragment
|
||||
- **$CE98–$FFFF** (12,648 bytes): Test pattern data, button mapping tables, screen layout data
|
||||
|
||||
Notable data structures:
|
||||
- Pointer table at $0000–$0001 (read by `VramPtrTableRead` at $CD99)
|
||||
- VRAM burst write table at $0302 (read by `VramBurstWrite` at $CE4F)
|
||||
- Button mapping / test pattern tables in the $C000–$FFFF range
|
||||
|
||||
---
|
||||
|
||||
## 9. Build
|
||||
|
||||
```bash
|
||||
cd "_wip/Joypad Test Cartridge (U)/copilot"
|
||||
make # → ca65 + ld65 + cmp reconstructed_prg.bin prg.bin → "BYTE-IDENTICAL"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Notes
|
||||
|
||||
- The NMI handler drives per-frame updates; the main loop spins waiting for NMI phase changes.
|
||||
- `RetAddrUpdate` ($CDC8) implements a return-address modification trick: it reads the return address from the stack, advances it by 2, and stores it back. This is used with `VramPtrWrite` ($CD3F) to implement runtime-computed control flow (effectively a coroutine/threaded dispatch).
|
||||
- The IRQ vector points to $C000 (Reset) and is effectively unused.
|
||||
- Large portions of the PRG are test pattern and button mapping data, not executable code.
|
||||
|
||||
---
|
||||
|
||||
## 11. Next Steps
|
||||
|
||||
- **Promotion** — move to a top-level `Joypad Test Cartridge (U)/` folder, add `header.s` / `chr.s` + full-ROM `make` target (as e.g. `Burger Time (U)`), then join the root `OVERVIEW.md` numbered inventory.
|
||||
- **Pass 3 continuation** — name data tables (button mappings at $C000–$FFFF, test patterns), refine zero-page aliases for the untraced code region.
|
||||
@@ -0,0 +1,25 @@
|
||||
CA65 ?= ca65
|
||||
LD65 ?= ld65
|
||||
|
||||
all: reconstructed.nes verify
|
||||
|
||||
prg.o: prg.s symbols.inc locals.inc
|
||||
$(CA65) -g -o $@ $<
|
||||
|
||||
header.o: header.s
|
||||
$(CA65) -g -o $@ $<
|
||||
|
||||
chr.o: chr.s
|
||||
$(CA65) -g -o $@ $<
|
||||
|
||||
reconstructed.nes: header.o prg.o chr.o linker.cfg
|
||||
$(LD65) -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o $@ header.o prg.o chr.o
|
||||
|
||||
verify: reconstructed.nes
|
||||
cmp reconstructed.nes rom.nes
|
||||
@echo "Byte-identical ROM verified."
|
||||
|
||||
clean:
|
||||
rm -f *.o reconstructed.nes reconstructed.map reconstructed.lbl
|
||||
|
||||
.PHONY: all verify clean
|
||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
||||
.segment "CHR"
|
||||
.incbin "chr.bin"
|
||||
File renamed without changes.
@@ -0,0 +1,2 @@
|
||||
.segment "HEADER"
|
||||
.incbin "header.bin"
|
||||
@@ -0,0 +1,10 @@
|
||||
MEMORY {
|
||||
HEADER: start = $0000, size = $0010, fill = yes;
|
||||
PRG: start = $C000, size = $4000, file = %O, fill = yes;
|
||||
CHR: start = $0000, size = $2000, file = %O, fill = yes;
|
||||
}
|
||||
SEGMENTS {
|
||||
HEADER: load = HEADER, type = ro;
|
||||
PRG: load = PRG, type = ro;
|
||||
CHR: load = CHR, type = ro;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
; Joypad Test Cartridge (U) zero-page aliases - Pass 2
|
||||
; Semantic names for addresses with observed usage patterns; ZpXX names for
|
||||
; addresses whose role is not yet uniquely determined.
|
||||
|
||||
VramPtrLo = $00 ; VRAM write pointer low byte (indirect, used by VramWriteLoop/VramWrite)
|
||||
VramPtrHi = $01 ; VRAM write pointer high byte (indirect)
|
||||
VramLen = $02 ; VRAM write data length counter
|
||||
PpuScrollX = $03 ; PPU horizontal scroll value (latched to $2005)
|
||||
Zp04 = $04 ; Temporary zero-page storage
|
||||
RetAddrTempLo = $05 ; Return address temp low byte (used by RetAddrUpdate)
|
||||
RetAddrTempHi = $06 ; Return address temp high byte (used by RetAddrUpdate)
|
||||
Zp0D = $0D ; Temporary zero-page storage
|
||||
PpuMaskShadow = $0E ; PPUMASK shadow register (bits cleared for sprite/bg)
|
||||
PpuScrollY = $0EC ; PPU vertical scroll value (latched to $2005)
|
||||
PpuScrollX_Init = $0ED ; PPU horizontal scroll value written during init
|
||||
PpuMaskValue = $0EE ; PPUMASK value written during init (also shadow)
|
||||
PpuCtrlShadow = $EF ; PPUCTRL shadow register (NMI bit cleared via and #$7F)
|
||||
VramToggleFlag = $CF ; Flag toggled each frame (asl/rol at $C095-$C09D)
|
||||
NmiFlagByte = $E0 ; Flag cleared during NMI ($CD0F)
|
||||
|
||||
; Controller / button state (indexed by button number via $4E,X)
|
||||
JoyBtnStateBase = $4E ; Base address for indexed button states ($4E,X)
|
||||
JoyBtnState_P1_A = $4E ; Player 1 button A state/timer
|
||||
JoyBtnState_P1_B = $4F ; Player 1 button B state/timer
|
||||
JoyBtnState_P1_Select = $50 ; Player 1 select state/timer (if used)
|
||||
JoyBtnState_P1_Start = $51 ; Player 1 start state/timer
|
||||
JoyBtnState_P1_Up = $52 ; Player 1 up state/timer
|
||||
FrameCounterLo = $53 ; Frame counter low byte (incremented each frame)
|
||||
FrameCounterHi = $54 ; Frame counter high byte
|
||||
JoyBtnTimer_P1_Down = $55 ; Player 1 down button timer
|
||||
JoyBtnTimer_P1_Left = $56 ; Player 1 left button timer
|
||||
JoyBtnTimer_P1_Right = $57 ; Player 1 right button timer
|
||||
JoyBtnTimer_P1_Start2 = $5C ; Player 1 start timer (second instance)
|
||||
JoyBtnTimer_P1_A2 = $61 ; Player 1 button A timer (second instance)
|
||||
JoyBtnTimer_P2_Down = $5E ; Player 2 down button timer
|
||||
JoyBtnTimer_P2_Left = $59 ; Player 2 left button timer
|
||||
JoyBtnTimer_P2_Right = $64 ; Player 2 right button timer
|
||||
JoyBtnTimer_P2_A = $5F ; Player 2 button A timer
|
||||
JoyBtnTimer_P2_B = $5A ; Player 2 button B timer
|
||||
|
||||
; Generic zero-page aliases (role not yet uniquely determined)
|
||||
Zp60 = $60 ; Temporary zero-page storage
|
||||
Zp65 = $65 ; Temporary zero-page storage
|
||||
Zp66 = $66 ; Temporary zero-page storage
|
||||
Zp68 = $68 ; Temporary zero-page storage
|
||||
Zp69 = $69 ; Temporary zero-page storage
|
||||
File renamed without changes.
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,35 @@
|
||||
al 00CE89 .Label_CE89
|
||||
al 00CE6E .Label_CE6E
|
||||
al 00CE5F .Label_CE5F
|
||||
al 00CE8A .Label_CE8A
|
||||
al 00CE4F .VramBurstWrite
|
||||
al 00CE3C .PpuScrollLatch
|
||||
al 00CE33 .Label_CE33
|
||||
al 00CE39 .Label_CE39
|
||||
al 00CE16 .Label_CE16
|
||||
al 00CE12 .Label_CE12
|
||||
al 00CDEE .VramWrite
|
||||
al 00CDC8 .RetAddrUpdate
|
||||
al 00CDB1 .Label_CDB1
|
||||
al 00CDA3 .Label_CDA3
|
||||
al 00CD99 .VramPtrTableRead
|
||||
al 00CD8D .Label_CD8D
|
||||
al 00CD74 .Label_CD74
|
||||
al 00CD71 .Label_CD71
|
||||
al 00CD6E .Label_CD6E
|
||||
al 00CD69 .Label_CD69
|
||||
al 00CD5C .Label_CD5C
|
||||
al 00CD45 .VramWriteLoop
|
||||
al 00CD3F .VramPtrWrite
|
||||
al 00CD2F .PpuMaskSetup
|
||||
al 00CD24 .OamDmaSetup
|
||||
al 00CD17 .PpuCtrlSetup
|
||||
al 00CD0A .NmiContinue
|
||||
al 00CCF7 .NmiHandler
|
||||
al 00C178 .Label_C178
|
||||
al 00C174 .ClearOAM_Routine
|
||||
al 00C05D .ClearRAM2_Loop
|
||||
al 00C013 .ClearRAM1_Loop
|
||||
al 00C00A .WaitVblank3
|
||||
al 00C005 .WaitVblank2
|
||||
al 00C000 .Reset
|
||||
+6
@@ -1,13 +1,19 @@
|
||||
Modules list:
|
||||
-------------
|
||||
header.o:
|
||||
HEADER Offs=000000 Size=000010 Align=00001 Fill=0000
|
||||
prg.o:
|
||||
PRG Offs=000000 Size=004000 Align=00001 Fill=0000
|
||||
chr.o:
|
||||
CHR Offs=000000 Size=002000 Align=00001 Fill=0000
|
||||
|
||||
|
||||
Segment list:
|
||||
-------------
|
||||
Name Start End Size Align
|
||||
----------------------------------------------------
|
||||
CHR 000000 001FFF 002000 00001
|
||||
HEADER 000000 00000F 000010 00001
|
||||
PRG 00C000 00FFFF 004000 00001
|
||||
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1 @@
|
||||
../rom.nes
|
||||
File renamed without changes.
Binary file not shown.
+15
-4
@@ -154,11 +154,21 @@ This document provides a consolidated overview of all NES disassembly projects i
|
||||
- **Build fix:** None needed; byte-identical out of the box (`make` in `copilot/`). Pass 1 complete — full code/data classification (~20% code / ~80% data); Pass 2 partial — routine headers and semantic naming for key routines (`Reset`, `NmiHandler`, `MainStateDispatch`, `MainLoopBody`, etc.); zero-page aliases in `locals.inc` (`GameMode`=$30, `NmiPhase`=$20, `NmiFlag`=$22, `Timer2F`=$2F, etc.)
|
||||
- **Details:** `Wild Gunman (U)/OVERVIEW.md`
|
||||
|
||||
### 14. Joypad Test Cartridge (U)
|
||||
- **Folder:** `Joypad Test Cartridge (U)/`
|
||||
- **ROM:** `rom.nes` (24,592 bytes)
|
||||
- **Source:** `copilot/prg.s` (~1,360 lines with routine headers)
|
||||
- **Linker:** HEADER at $0000 (16 bytes), PRG at $C000 (16 KB), CHR at $0000 (8 KB)
|
||||
- **Vectors:** NMI → $CCF7 (`NmiHandler`), RESET → $C000 (`Reset`), IRQ → $C000 (reuses `Reset`)
|
||||
- **Key subsystems:** NMI-driven frame loop — main loop spins waiting on NMI phase; NMI handler does OAM DMA ($4014), PPU scroll/control latch, and burst VRAM writes from table at $0302; `VramPtrWrite` ($CD3F) uses a return-address modification trick at `RetAddrUpdate` ($CDC8) for runtime-computed dispatch; `VramWrite` ($CDEE) sets PPU address and writes data to PPUDATA; reset clears RAM, initializes PPU ($2000=$10, $2001=$00), triggers OAM DMA, and enters main loop
|
||||
- **Build fix:** None needed; byte-identical out of the box (`make` in `copilot/`). Pass 1 complete — full code/data classification (~3.5% code / ~96.5% data); Pass 2 complete — routine headers for all labels, semantic naming for key routines, zero-page aliases in `locals.inc` (`VramPtrLo`=$00, `PpuCtrlShadow`=$EF, `FrameCounterLo`=$53, etc.)
|
||||
- **Details:** `Joypad Test Cartridge (U)/OVERVIEW.md`
|
||||
|
||||
---
|
||||
|
||||
## Skeleton Projects (under `_wip/`)
|
||||
|
||||
22 additional ROMs awaiting full disassembly. Each is set up as a Pass-0 skeleton (`default_project.tar.gz`): `copilot/` contains the split `prg.bin`/`header.bin`/`chr.bin`, a symbolic skeleton `prg.s`, and builds **BYTE-IDENTICAL** at the PRG level. Mapper 0 / 16 KB PRG / 8 KB CHR for all.
|
||||
21 additional ROMs awaiting full disassembly. Each is set up as a Pass-0 skeleton (`default_project.tar.gz`): `copilot/` contains the split `prg.bin`/`header.bin`/`chr.bin`, a symbolic skeleton `prg.s`, and builds **BYTE-IDENTICAL** at the PRG level. Mapper 0 / 16 KB PRG / 8 KB CHR for all.
|
||||
|
||||
| Project | Vectors (NMI/RESET/IRQ) | Mir. | Status |
|
||||
|---------|--------------------------|------|--------|
|
||||
@@ -169,7 +179,6 @@ This document provides a consolidated overview of all NES disassembly projects i
|
||||
| `Hogan's Alley (JU)` | $C088 / $C000 / $C000 | H | BYTE-IDENTICAL |
|
||||
| `Ice Climber (JE)` | $C076 / $C014 / $FFF0 | V | BYTE-IDENTICAL |
|
||||
| `Ice Climber (U)` | $C076 / $C014 / $FFF0 | V | BYTE-IDENTICAL |
|
||||
| `Joypad Test Cartridge (U)` | $CCF7 / $C000 / $C000 | H | BYTE-IDENTICAL |
|
||||
| `Lode Runner (U)` | $C02C / $C000 / $FFF9 | H | BYTE-IDENTICAL |
|
||||
| `Lunar Pool (U)` | $FF65 / $E80C / $FF64 | H | BYTE-IDENTICAL |
|
||||
| `MUSCLE (U)` | $86CB / $8000 / $8764 | H | BYTE-IDENTICAL |
|
||||
@@ -196,6 +205,7 @@ Notable vector/header observations for Pass 1 work:
|
||||
- **Raid on Bungeling Bay (U) vs [a2]**: `[a2]` differs in mirroring only per header, but PRGs differ — separate builds.
|
||||
- **Pyramid (U)** — *already at Pass 1*: interesting unlicensed Sachen boot ($4100 mapper pokes, "AV" stack-byte check, two queued-event dispatchers). See its `OVERVIEW.md`.
|
||||
- **CIRCUS** — *at Pass 2/3*: full recursive disassembly complete. NMI-driven frame state machine (main loop is a spin loop at $C057). Menu text at $C763 ("CONTINUE", "STAGE", "SELECT", "PLAY"). Object state machine at $DC3F–$FE7B. Audio sequence data at $FABB–$FFFB. See `_wip/CIRCUS/OVERVIEW.md`.
|
||||
- **Joypad Test Cartridge (U)** — *at Pass 2*: minimal code (~3.5%), data-heavy test cartridge. NMI handler drives per-frame PPU/OAM updates. Uses return-address modification trick at `RetAddrUpdate` ($CDC8) with `VramPtrWrite` ($CD3F) for runtime-computed dispatch. See `Joypad Test Cartridge (U)/OVERVIEW.md`.
|
||||
|
||||
## Common Patterns Across All Projects
|
||||
|
||||
@@ -249,7 +259,7 @@ make verify # (where supported by the Makefile)
|
||||
|
||||
## Summary
|
||||
|
||||
All 13 projects in this repository rebuild **byte-identical** to their original ROMs:
|
||||
All 14 projects in this repository rebuild **byte-identical** to their original ROMs:
|
||||
|
||||
| # | Project | Build Status |
|
||||
|---|---------|--------------|
|
||||
@@ -266,7 +276,8 @@ All 13 projects in this repository rebuild **byte-identical** to their original
|
||||
| 11 | Donkey Kong Jr. (JU) | BYTE-IDENTICAL (Pass 1) |
|
||||
| 12 | CIRCUS | BYTE-IDENTICAL (Pass 2/3) |
|
||||
| 13 | Wild Gunman (U) | BYTE-IDENTICAL (Pass 2) |
|
||||
| 14 | Joypad Test Cartridge (U) | BYTE-IDENTICAL (Pass 2) |
|
||||
|
||||
Every project builds successfully with `make` in the `copilot/` folder, and every reconstructed ROM matches the original byte-for-byte. Donkey Kong Jr. shares the same byte-identical build guarantee; its remaining Pass 2/3 work is semantic renaming and table decoding, not build-correctness.
|
||||
|
||||
The 23 `_wip/` skeleton projects above also all rebuild byte-identical (PRG level); all Pass 1 work and promotion to this index's numbered inventory is future work.
|
||||
The 21 `_wip/` skeleton projects above also all rebuild byte-identical (PRG level); all Pass 1 work and promotion to this index's numbered inventory is future work.
|
||||
@@ -1,53 +0,0 @@
|
||||
# Joypad Test Cartridge (U) — Skeleton Project
|
||||
|
||||
ROM: `_wip/Joypad Test Cartridge (U)/Joypad Test Cartridge (U).nes` (24,592 bytes, iNES). Status: **SKELETON — not yet disassembled.** The entire 16 KB PRG bank is a single unclassified block; `copilot/prg.s` is the default-project skeleton (`default_project.tar.gz`) that reproduces it as one `.incbin`. The skeleton build is **byte-identical** at the PRG level.
|
||||
|
||||
|
||||
## 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 | Horizontal |
|
||||
| Battery-backed flag | clear |
|
||||
| ROM SHA-256 | `dd64415bfddb17e9c284cdf3f99bb4eb9c468a6ef509cebe8a5f68856ef5fb51` |
|
||||
| PRG SHA-256 | `dc8988c978f0db51473c3a0fb0b31e568ba0203534665f220dc00cbc78cfdc7d` |
|
||||
|
||||
## Interrupt Vectors (taken from PRG $3FFA–$3FFF)
|
||||
|
||||
| Vector | Target | Note |
|
||||
|--------|--------|------|
|
||||
| NMI | `$CCF7` | per-frame heartbeat |
|
||||
| RESET | `$C000` | cold start at $C000 — the usual pattern in this repo |
|
||||
| IRQ | `$C000` | redirects to RESET — unused placeholder |
|
||||
|
||||
## `copilot/` Contents
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `Joypad Test Cartridge (U).nes` | original ROM image (reference) |
|
||||
| `prg.bin` / `header.bin` / `chr.bin` | split image, produced by `_tools/splitnes.py` |
|
||||
| `prg.s` | Pass-1 skeleton: `.incbin "prg.bin", $0000, $4000` (whole PRG) |
|
||||
| `symbols.inc` | shared NES hardware constants (PPU/APU/controller, from the skeleton) |
|
||||
| `locals.inc` | project-local zero-page aliases — empty for now (filled in 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, which is exactly what a skeleton proves. 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/Joypad Test Cartridge (U)/copilot"
|
||||
make # → cmp reconstructed_prg.bin prg.bin → "BYTE-IDENTICAL"
|
||||
```
|
||||
|
||||
## Next Steps (per `PROMPTS.md`)
|
||||
|
||||
1. **Pass 1** — replace the blanket `.incbin` with classified code/data: trace reachability from RESET ($C000) and NMI ($CCF7), emit per-instruction `; PRG:$xxxx AA BB` annotations, keep unreached bytes as explicit `.byte` blocks.
|
||||
2. **Pass 2** — replace `Label_`/`Sub_` names with semantic routine names; fill `locals.inc`.
|
||||
3. **Pass 3** — name data tables/structures; release provisional flags.
|
||||
4. **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.
|
||||
@@ -1,10 +0,0 @@
|
||||
all: reconstructed_prg.bin verify
|
||||
prg.o: prg.s symbols.inc prg.bin
|
||||
ca65 -g -o $@ prg.s
|
||||
reconstructed_prg.bin: prg.o linker.cfg
|
||||
ld65 -C linker.cfg -m reconstructed.map -Ln reconstructed.lbl -o $@ prg.o
|
||||
verify: reconstructed_prg.bin prg.bin
|
||||
cmp reconstructed_prg.bin prg.bin
|
||||
@echo BYTE-IDENTICAL
|
||||
clean:
|
||||
rm -f prg.o reconstructed_prg.bin reconstructed.map reconstructed.lbl
|
||||
@@ -1,6 +0,0 @@
|
||||
MEMORY {
|
||||
PRG: start = $C000, size = $4000, file = %O, fill = yes;
|
||||
}
|
||||
SEGMENTS {
|
||||
PRG: load = PRG, type = ro;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
; Joypad Test Cartridge (U) zero-page aliases (declared during Pass 2).
|
||||
; These intentionally conservative names will be refined in Passes 2 and 3.
|
||||
@@ -1,12 +0,0 @@
|
||||
; Joypad Test Cartridge (U) NES disassembly - Pass 1 (skeleton)
|
||||
; Generated from the supplied 16 KiB PRG image.
|
||||
; Pass 1 classifies recursively reachable code, inline address tables, vectors,
|
||||
; and untraced/data regions. All PRG data bytes are emitted explicitly with .byte directives.
|
||||
|
||||
.setcpu "6502"
|
||||
|
||||
.include "symbols.inc"
|
||||
.include "locals.inc"
|
||||
|
||||
.segment "PRG"
|
||||
.incbin "prg.bin", $0000, $4000
|
||||
Whitespace-only changes.
Reference in new issue
Block a user