export const meta = { name: 'modemwars-annotate', description: 'Phase 2: arbitrate variable names, then write detailed line-by-line comments for every chunk', phases: [ { title: 'Variables', detail: 'one arbiter agent decides zero-page and global variable names' }, { title: 'Rebuild', detail: 'rebuild the listing with the arbitrated names applied' }, { title: 'Annotate', detail: 'one agent per chunk writes annotations/20_.json' }, { title: 'Verify', detail: 'rebuild, reassemble and compare every unit' }, ], } const chunks = args.chunks const RESULT = { type: 'object', properties: { path: { type: 'string' }, linesCommented: { type: 'integer' }, routines: { type: 'integer' }, problems: { type: 'array', items: { type: 'string' } }, }, required: ['path', 'linesCommented', 'routines', 'problems'], } const common = ` You are annotating a reverse-engineered disassembly of "Modem Wars" (Commodore 64, Electronic Arts / Ozark Softscape, Dan Bunten, 1988): a two-player real-time strategy game played over a modem or null-modem link, with a solo trainer, "game film" record/playback, a console with STATS / REPAIR / MISC panels, and a 40x40 battlefield map. Work in /home/scott/claude/modemwars. Read first: docs/overview.md (disk, boot, loader protocol, memory map, overlays, game concepts) and the section of docs/knowledgeBase.md for your unit (every routine and data block found by the survey pass). disassembly/XREF.txt lists callers/readers/writers of every label and of every zero-page location. disassembly/zeropage.inc holds the variable names. The listing is ca65 syntax; every instruction line ends with "; XXXX" (its address) and then the comment text. Each file starts with a Contents table of its routines. Labels are unit-scoped: the same address holds different code in the other overlay variant. Hardware registers are named (VIC_*, SID_*, CIA1_*, CIA2_*). The game runs with $01=$35 (no ROMs, I/O visible) and scans the keyboard and joystick itself. Strings end with a character whose bit 7 is set. Graphics data is rendered as bit art in the comments. Facts established by the survey pass - use these names and do not re-derive them: * Units: 100 records in parallel 100-byte arrays starting at $F640, each array $64 apart - column, row, flags/facing, type (0 GRUNT 1 RIDER 2 BOOMER 3 SPY 4 COMCEN, bit 7 = dead), destination column/row, secondary target, cloak, blitz, stun/dug-in, energy (0-50), move state, draw flags, terrain hidden under the unit, group id. Units 0-49 are player 0, 50-99 player 1; unit 49 and unit 99 are the comcens. * Map: $F000-$F63F, one byte per cell of the 40x40 battlefield. A cell holding $80|index means that unit stands there and the terrain it covers is kept in the per-unit "terrain under" array. * Screen: multicolour bitmap, VIC bank 2 - bitmap $A000, video matrix $8C00, colour RAM $D800. Text is drawn into the bitmap with the game's own font, through the text engine in game/textEngineC000.s. Left panel = whole map at 4x4 pixels per cell; right panel = 7x5 cells at 16x16; console below it. * Opponent: one jump table at $E000 backed by EITHER game/modemDriverE000.s (a real software modem) OR game/trainerAiE000.s (the solo trainer's computer opponent). $E000 per-frame poll, $E003 open/close, $E006 get byte, $E009 send byte, $E00C per-frame, $E015 get key. * Commands: single bytes $80-$A6 plus 0-3 argument bytes, queued in a ring and dispatched through commandHandlerTable at $4D2A. The same stream is what a "game film" records and replays. * Side-dependent branches are self-modified: several "cpy #$32" tests are followed by a BCC/BCS whose opcode byte is written at run time from the player's side. Treat that opcode byte as a variable. Annotation JSON format (the build merges all annotations/*.json; everything is unit-scoped): { "chunk": "", "unit": "", "unitLabels": { "": { "XXXX": "camelCaseName" } }, "notes": { "XXXX": { "unit": "", "routine": ["name - what it does", "In: ...", "Out: ...", "Called from: ..."], "block": ["comment block printed before this address"], "line": "end-of-line comment for the instruction or data at this address" } }, "dataTypes": { "XXXX": ["text"|"addr"|"word"|"byte"|"grid"|"bitmap"|"sprite", length, optionalRowWidth] }, "zpComments": { "XX": "better description of a zero-page variable (do not rename)" } } Addresses: uppercase hex, 4 digits (2 for zero page), no "$". Plain ASCII only - no accented characters, arrows or box-drawing characters. HARD RULES * Write ONLY your own file annotations/20_.json. Never edit disassembly/*.s, tools/*, docs/* or another agent's annotation file, and never edit the generated files annotations/05_*, 06_*, 10_*, 11_*, 15_*. * Do NOT run tools/build.py or disassembly/verify.sh - other agents are running in parallel and the build rewrites the whole disassembly directory. A separate verification agent runs it at the end. * Do not rename anything outside your own address range. You MAY rename labels inside your range when you have a better name (camelCase, specific: waitForRasterBottom, unitHealthTable, isFilmPlaying). * Never invent facts. Mark uncertainty in the comment itself ("probably", "?"). ` phase('Variables') const arb = await agent(`${common} TASK: you are the naming arbiter for variables. survey/variableProposals.json lists, per address, every name and meaning proposed by the survey agents (often several per address, sometimes contradictory). The zero-page section at the end of disassembly/XREF.txt shows every reader and writer. Decide ONE final camelCase name and a one-line description for: (a) every zero-page address that has a proposal or is used by the game (keep "zp_XX" only when nothing can be inferred - but read the readers/writers in the listing before giving up); (b) absolute RAM variables that live outside the code units: $8A00-$8BFF (sprite shapes), $9000-$92FF (VIC shadow registers, buffers, game state), $F640-$FBB7 (unit record arrays and work RAM), $FFD2-$FFFF; (c) the well-known flags inside the main program: $0B7D-$0BB4 (the settings block), $87FF, $90B7, $90EA, $90FB. Resolve conflicts by reading the code, not by counting votes. Names must be unique across the whole program and must not collide with routine names in docs/knowledgeBase.md. Prefer the plural/array form for the per-unit arrays (unitColTable, unitRowTable, unitTypeTable, ...). Write /home/scott/claude/modemwars/annotations/15_variables.json: { "zp": { "XX": ["name", "description"] }, "labels": { "XXXX": "name" }, <- addresses outside every code unit range "unitLabels": { "game/mainProgram0800": { "0BA5": "isSoloTrainer" } }, <- variables inside a unit range "notes": { "XXXX": { "unit": "game/mainProgram0800", "line": "description" } } } You MAY run "python3 tools/build.py 2>&1 | grep -i warning" and "./disassembly/verify.sh | grep -c OK" (expect 24) because you are the only agent running in this phase; fix any duplicate-name warning it reports. Return path, number of zp names decided (linesCommented), number of absolute variables (routines), problems.`, { label: 'arbiter:variables', phase: 'Variables', schema: RESULT }) phase('Rebuild') const rebuilt = await agent(`Work in /home/scott/claude/modemwars. Run: python3 tools/build.py 2>&1 | grep -iE "warning|error|traceback" ./disassembly/verify.sh | grep -c OK (must print 24) python3 tools/chunks.py 700 > /dev/null If verify does not print 24, show the failing unit and its first ca65 error (./disassembly/verify.sh 2>&1 | grep -B2 -A2 Error | head -30), trace it to the offending entry in annotations/15_variables.json (bad label name, a text run crossing a label, a non-ASCII character) and fix that annotation file - never edit disassembly/*.s or tools/*. Repeat until it verifies. Return path="disassembly", linesCommented=0, routines=0, problems=[what you fixed].`, { label: 'rebuild', phase: 'Rebuild', schema: RESULT }) phase('Annotate') const results = await pipeline(chunks, (c) => agent(`${common} YOUR CHUNK: unit "${c.unit}", file disassembly/${c.file}, addresses $${c.startAddr}-$${c.endAddr}. Locate it with: grep -n "; ${c.startAddr}" disassembly/${c.file} and grep -n "; ${c.endAddr}" disassembly/${c.file} then read the whole range with sed -n. Read it all before writing anything. TASK: write the detailed annotation for this chunk to /home/scott/claude/modemwars/annotations/20_${c.id}.json (format above). Requirements: 1. Every routine that starts in your range gets a "routine" header: what it does (1-3 sentences), "In:" the registers and variables it consumes, "Out:" the results and side effects, and "Called from:" its callers taken from XREF.txt when there are only a few. Improve the survey's wording where you understand it better; keep its name unless it is clearly wrong. 2. A "line" comment on essentially EVERY instruction, explaining intent rather than the mnemonic ("start at the left edge of the viewport", not "load 0 into X"). In every compare/branch name the thing being tested ("branch if the unit is dead"). Explain every magic number: screen offsets, colours, screen codes, sprite pointers, VIC/SID/CIA bit meanings, raster lines, timer values, table strides, unit-array offsets ($64 apart). 3. "block" comments before each logical section and loop: what it iterates over and what it maintains. 4. Data inside your range: label it and describe the structure (element size, what the index means). Use dataTypes "text" for strings (exact length, including the terminator byte), "addr" for tables of 16-bit addresses, "grid" with a row width for tables read as rows, "bitmap"/"sprite" for graphics. 5. Self-modifying code: say exactly which instruction is patched, by whom, and with what (labels look like L_1234+1 for the operand byte). 6. Note anything that looks like dead or leftover code, and say why you think so. Work top to bottom; do not skip routines. Follow calls into other files when the meaning of a call matters. When done, validate with: python3 -c "import json;json.load(open('annotations/20_${c.id}.json'))" Return: path, number of line comments written, number of routine headers, problems (mis-traced code, survey names you corrected, unresolved questions).`, { label: `annotate:${c.id}`, phase: 'Annotate', schema: RESULT })) phase('Verify') const verified = await agent(`Work in /home/scott/claude/modemwars. You are the only agent running now. 1. grep -lP '[^\\x00-\\x7F]' annotations/*.json docs/*.md 2>/dev/null - replace any non-ASCII character found. 2. python3 -c "import json,glob;[json.load(open(f)) for f in glob.glob('annotations/*.json')]" - fix invalid JSON. 3. python3 tools/build.py 2>&1 | grep -iE "warning|error|traceback" 4. ./disassembly/verify.sh | grep -v "^OK" - every one of the 24 units must reassemble byte-exact. If a unit fails, find the first ca65 error (./disassembly/verify.sh 2>&1 | grep -B2 -A2 Error | head -30), trace it to the annotation entry that caused it and fix THAT annotation file. Never edit disassembly/*.s or tools/*. Duplicate-name warnings from build.py are auto-resolved but ugly: fix the annotation that caused each one. Repeat until verify reports 24 OK and build.py prints no warnings. Return path="disassembly", linesCommented=0, routines=0, problems=[everything you had to fix].`, { label: 'verify', phase: 'Verify', schema: RESULT }) return { arbiter: arb, rebuilt, chunks: results.filter(Boolean), verified }