Pinball disassembly project. you will disassemble a NES rom. I will provide you with the skeleton project as a tar.gz file renamed to txt. build the byte identical rom. once you have completed a first pass to identify the code vs the data blocks, stop and provide me with the code. The goal is a byte-identical disassembly. Identify code and data sections. After the first pass, take a second pass and replace the known labels. Pass a third time to better identify use of code and data tables naming more appropriately. with code blocks that are generated, each method will have a local and global description, the parameters and the return value(s). for each instruction, include a comment at the end of the line with the offset followed by the actual bytes that are expected to be encoded. during building the byte exact source, include the remaining incbin line so we can cycle through builds quickly and efficently. there are 2 files for symbols. symbols.inc which has NES specific values for APU, PPU, and controller. locals.inc has application specific values for zero page variables and forward references while decoding. --- ## Promotion requirements (must all be satisfied before a project is promoted out of `_wip//` into its mapper folder, e.g. `NROM//`) ### Pass 3: Symbols and locals applied Before any promotion, `_tools/apply_symbols.py` must have been run on `prg.s`, substituting `symbols.inc`/`locals.inc` names into operands: - All NES hardware registers (PPU, APU, controller) must be referenced by their symbolic names from `symbols.inc`, never as raw `$2000-$4017` literals. - Zero-page variables must use their `locals.inc` names where those aliases exist; multi-alias addresses require a `--sites` file for per-routine resolution. - Any remaining numeric operands that could have been named but were left as residue must be documented in `locals.inc` with a recorded reason. - `make` must print **Byte-identical ROM verified.** (whole-ROM compare against `rom.nes` in the project folder) after symbol application. ### Pass 3: ROM-specific semantic naming Before promotion, every routine, table, data block, and zero-page alias must carry a name derived from **observed ROM behavior**, not generic placeholders: - Method/table names must describe *what they do in this game*. Names like `Actor`, `Object`, `Entity`, `Sprite` are placeholders and are **not acceptable** unless the ROM genuinely has no more specific semantic basis. - Example: In a game about soldiers, a routine that updates soldier position must be named something like `UpdateSoldierPosition` or `MoveSoldier` — **never** `UpdateActor` or `MoveEntity`. A table holding soldier health must be `SoldierHealthTable`, not `ActorDataTable`. - Data tables must be named for their content (e.g., `LevelLayoutTable`, `EnemyPatternTable`, `TitleScreenText`), not generically (`DataTable1`, `Buffer`). - Zero-page aliases in `locals.inc` must use behavioral names (e.g., `SoldierX`, `BulletY`, `FrameCounter`), not neutral `ZpXX` names, unless the role is genuinely undetermined after tracing. ### Pass 3: Data tables are interpreted, not merely labelled Before promotion, every data block in the PRG must be **interpreted**: the project must say what the bytes *are* and **who reads them**. A name is not an interpretation. For each named table the project must record: - **What the bytes are** — the structure, the record size, the encoding, the units, and the range of valid values where the ROM makes them provable. - **The consumer** — the instruction(s) in this dump that read the table, by address, and what the code does with what it reads. "The engine reads these" is not an answer; "`lda $C0A3,y` at $E7B4 indexes the table by player index and the result becomes the sprite Y position" is. - **How the structure was derived** — from the consuming code, from the values themselves, or from a known NES format. A structure that was guessed must be recorded as a guess, with the evidence that supports it. A table whose layout is not yet understood must say so explicitly ("32 bytes of unknown layout, read as a 32-entry pointer table by `lda $C123,x` at $E1A0, entry semantics not yet established") rather than being given a plausible-sounding name. A block that no instruction in the dump reads is recorded as unreferenced data with its ROM provenance, not named after something it might be. The **data-table inventory** in the project `README.md` is the record of this work: one line per named block, giving the address range, the name, the structure, and the consuming instruction. ### Pass 3: Method comments describe the method Every routine header carries a `Global:` / `Local:` / `Parameters:` / `Returns:` / `Clobbers:` block. Each field must be **about that routine**: - `Global:` — what the routine is for in this game, in terms of the game's own concepts. "Updates the player's score display" is right; "control-flow entry identified from its parent routine" is not. - `Local:` — what this routine actually does, step by step, from the instructions below it. It must describe *this* code, not the label. - `Parameters:` / `Returns:` — the actual inputs and outputs, in the routine's own terms (register contents, zero-page fields, PPU state). "Undetermined" is allowed only where the code genuinely does not settle it, and then it must say why. Two rules follow from this: - **No comment may reference a previous pass.** "Pass 1 reachable entry at $C000", "Pass 1 control-flow target", "semantic naming will be refined in a future pass" and "Undetermined in Pass 1" are all forbidden. The source describes the ROM, not the process used to read the ROM. The process history lives in `CHANGELOG.md` and the project `README.md`. - **No comment may be filler.** A header that says the same thing about every routine in the project carries no information and is not acceptable as a description of any of them. If a routine's role has not been worked out, write what the instructions do and say the role is not yet established — do not paste a template. ### Pass 3: Branch and jump targets use named labels Before promotion, every branch (`beq`, `bne`, `bcc`, `bcs`, `bpl`, `bmi`, `bvc`, `bvs`) and every jump/call (`jmp`, `jsr`) must target a **symbolic label**, never a raw address literal: - Branch targets must be labeled with descriptive names derived from their role (e.g., `UpdateSoldierPosition_Loop`, `CheckHit_NotHit`, `GameMode_Return`). - `jsr`/`jmp` targets must use semantic entry-point names (e.g., `SubC0400` is acceptable as an address-derived entry point only if no better semantic name exists; a routine that plays music must be `PlayMusic` or similar). - No branch or jump operand may be a raw `$XXXX` address in `prg.s`. - Labels used as branch/jump targets must be defined exactly once in `prg.s`. - Every label that is the target of a branch or jump must have a name that reflects its role in program flow (e.g., `_Loop`, `_Next`, `_Done`, `_Store`, `_Return`, `_Skip`, `_Exit`), not just `Label_C000`. ### Verification checklist before promotion 1. `make` in the project folder prints **Byte-identical ROM verified.** 2. `prg.s` contains no raw `$2000-$4017` hardware literals — all use `symbols.inc` names. 3. `prg.s` contains no raw `$XXXX` jump/branch targets — every `jsr`, `jmp`, `beq`, `bne`, `bcc`, `bcs`, `bpl`, `bmi`, `bvc`, `bvs` targets a symbolic label. 4. `prg.s` contains no neutral `ZpXX` zero-page references unless truly undetermined; all others use `locals.inc` aliases. 5. `prg.s` has no generic placeholder names (`Actor`, `Object`, `Entity`, `DataTable1`, `Buffer`, `SubC` for anything with an identifiable role). 6. `locals.inc` documents every remaining `ZpXX` neutral slot with a recorded reason. 7. Project `README.md` contains a power-on flow analysis (RESET/NMI/main-loop walk) and a Pass 3 data-table inventory. 8. `apply_symbols.py` residue report shows no resolvable operand left numeric without justification. 9. `CHANGELOG.md` carries the project's Pass 1, Pass 2, Pass 3 and Promotion rows, with dates. 10. **Every data block in the PRG is interpreted** — the `README.md` data-table inventory lists each named block with its structure and the consuming instruction, and every block not named says why (unreferenced, or layout not yet established). A block that is merely given a name is not enough. 11. **Every routine header describes its own routine** — the `Global:`/`Local:`/`Parameters:`/`Returns:` fields are specific to the code they sit above, and no header mentions a pass, a stage of the work, or any future refinement. 12. **The tier passes its own audit** — `_tools/audit_promoted.py` checks every item above for every promoted project, and reports nothing. Run it before declaring the tier clean: ```bash python3 _tools/audit_promoted.py --build ``` The audit is a checker, not a fixer: it never edits a project. It reports the file and the line of every finding, so a finding is a place to go and look. ### Pass 3: Changelog entry `CHANGELOG.md` is the repository's running log, and it has two parts. **For every change, of any kind, to any project** - a disassembly, a rename, a comment repair, a tool fix, a documentation fix - a line goes into the **Pending** list at the top of the file, naming the project and saying what changed. The list is what the **next git commit's message is written from**, so the message and the log cannot drift apart. When the commit is made, the entries move to **History** under the commit they went in with, and **Pending** starts empty again. **For every milestone a project reaches**, the milestone also gets a row in the milestone index at the end of the file, which carries one section per project with a row for each of the four milestones - **Pass 1** (the code/data split), **Pass 2** (the labels replaced by names), **Pass 3** (the semantic naming) and **Promotion** (the move into the top-level mapper folder): - The row carries the **date** of the milestone and a sentence saying what it produced. Use the numbers the project README and the root `README.md` already carry: the instruction count, the code coverage, the number of named labels, the number of semantic zero-page aliases and the number of named data tables. - A milestone that has not happened gets a `-` in the date column and a note saying what is still open, so a reader can see at a glance which projects are done and which are not. - The entry is added **in the same commit** as the milestone it records, so the changelog and the source never disagree.