# AGENTS.md ## What this repo is Eleven AI-generated disassemblies of NES ROMs. The contract (stated in `PROMPTS.md` and verified by `make` in every `copilot/` folder) is **byte-identical rebuilds**: `reconstructed.nes` must match the original ROM exactly. Not for distribution (`README.md`). ## Layout - Each project owns a folder with a `copilot/` build dir (`prg.s`, `header.s`, `chr.s`, `linker.cfg`, `symbols.inc`, `locals.inc`, `Makefile`, and a copy/symlink `rom.nes`) and a per-project `OVERVIEW.md` at the folder root (power-on flow analysis). - Root `OVERVIEW.md` is the index: vectors, build fixes, and summary for all 11 projects. **Keep it in sync** when a project changes — its DK Jr. entries drifted badly before being corrected. - `PROMPTS.md` is the workflow contract. `default_project.tar.gz` is the empty-project skeleton (prg.s, symbols.inc, linker.cfg, Makefile, locals.inc). `_ROMS/` is the master ROM store; `_tools/` has `splitnes.py` and `chr2gif.py`. ## Commands ```bash cd "/copilot" && make # builds reconstructed.nes, then cmp vs rom.nes ``` `make`'s default target includes the byte-identity `cmp` — a build that doesn't print "Byte-identical ROM verified." is a failure. Requires `ca65`/`ld65` on PATH. ## Irreversible conventions (do not "clean up" past these) - Every instruction carries a trailing `; PRG:$xxxx AA BB` comment with PRG offset and exact emitted bytes. This is the byte-identity audit trail — preserve it even while renaming labels or restructuring files. - Unreached/unresolved bytes stay as data (`Label_/Data/` `.byte` or `.incbin` blocks with ROM provenance), never deleted or reformatted. - Routine headers follow the template in `prg.s`: global + local description, parameters, returns, clobbers. - Two separate symbol files: `symbols.inc` = NES hardware constants only; `locals.inc` = zero-page and forward references. Unresolved zero-page bytes keep neutral `ZpXX` names rather than speculative names. - Pass discipline: Pass 1 = code/data split (stop for review), Pass 2 = label replacement, Pass 3 = semantic naming of data tables/structures. Do not skip straight to guessy rewrites. ## Hard-won quirks (details in root `OVERVIEW.md` per-project "Build fix") - The flat disassembler mislabels `bne` as `bpl`; fixes use `bne *-$XX` to reproduce the original bytes (Clu Clu Land, Defender 2, Excitebike). - Zero pages are reused across modes; aliases may be required in `locals.inc` (e.g. `RandomStateAlt = $E4` beside `RandomState0 = $54`). - Donkey Kong Jr. is architecturally different: reset entry is $C66E, the reset path idles forever in an LFSR loop, and the entire frame state machine runs inside NMI. Its `DeferredTaskHandlerTable` entry 0 deliberately mirrors the IRQ vector word at $BFFE. - IRQ vectors often point at unused/data targets in these games — verify before assuming a handler is broken. - Unlicensed titles break static-trace assumptions: Pyramid (U) pokes a mapper unlock at `$4100` (beyond the register space) despite a mapper-0 header, and dispatches through `jmp ($0002)` built from RAM — trace via its two queue tables (`$C3A6`, `$CCD5`), and do NOT fall-through-seed gaps that follow `rts`/`jmp`/`brk` (that misclassifies data as code). - ROM filenames contain spaces; builds `incbin` a space-free `rom.nes` inside `copilot/`. Keep that copy/symlink working and don't rename it. - `.gitignore` contains `ROMS/*` but the actual directory is `_ROMS` — the pattern matches nothing, so ROM binaries stay tracked. Check before committing large blobs.