151 lines
9.4 KiB
JavaScript
151 lines
9.4 KiB
JavaScript
export const meta = {
|
|
name: 'modemwars-finish',
|
|
description: 'Phase 3: annotate the one code chunk that failed, comment the data files, correct stale claims, verify',
|
|
phases: [
|
|
{ title: 'Code', detail: 'redo the fast loader chunk whose agent lost its connection' },
|
|
{ title: 'Data', detail: 'one agent per pure-data file' },
|
|
{ title: 'Fix', detail: 'correct claims the data survey disproved' },
|
|
{ title: 'Verify', detail: 'rebuild, reassemble and compare every unit' },
|
|
],
|
|
}
|
|
|
|
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 link, with a solo trainer,
|
|
"game film" record/playback, a console with STATS / REPAIR / MISC panels and a 40x40 battlefield.
|
|
Work in /home/scott/claude/modemwars.
|
|
|
|
Read first: docs/overview.md and the section of docs/knowledgeBase.md for your unit. disassembly/XREF.txt lists
|
|
callers/readers/writers per source file (references are grouped under the file they come from - only the ones under
|
|
your own file are meaningful). survey/*.json holds the raw survey findings, including survey/data_units.json and
|
|
survey/data_*.json which describe the data blocks in detail.
|
|
|
|
Annotation JSON format (tools/build.py merges every annotations/*.json; everything is unit-scoped):
|
|
{
|
|
"chunk": "<id>", "unit": "<unit>",
|
|
"unitLabels": { "<unit>": { "XXXX": "camelCaseName" } },
|
|
"notes": { "XXXX": { "unit": "<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] }
|
|
}
|
|
"grid" takes a row width and prints that many bytes per line; "bitmap" prints one byte per line as 8 pixels of
|
|
bit art; "sprite" prints 21 rows of 3 bytes as 24-pixel bit art. Addresses: uppercase hex, 4 digits, no "$".
|
|
Plain ASCII only.
|
|
|
|
HARD RULES
|
|
* Write ONLY your own file annotations/30_<chunk>.json. Never edit disassembly/*.s, tools/*, docs/* or another
|
|
agent's annotation file.
|
|
* Do NOT run tools/build.py or disassembly/verify.sh - other agents run in parallel and the build rewrites the
|
|
whole disassembly directory. A verification agent runs at the end.
|
|
* dataTypes are keyed by address across the whole program, so only type an address that is data in EVERY file
|
|
that covers it. $6F00-$87FF and $E000-$EFFF each exist in two different files, and $0200-$04FF in two more:
|
|
check the sibling file before typing an address in those ranges.
|
|
* Never invent facts. Mark uncertainty in the comment itself ("probably", "?").
|
|
`
|
|
|
|
phase('Code')
|
|
const codeChunk = await agent(`${common}
|
|
YOUR CHUNK: unit "boot/fastLoaderC000", file disassembly/boot/fastLoaderC000.s, addresses $C000-$C3FE.
|
|
This is the Electronic Arts fast loader: it installs code in the 1541, then pulls the whole game off the disk
|
|
through a two-bit serial protocol on CIA2 port A while the title picture is displayed. The drive side of the same
|
|
protocol is disassembly/drive/driveFastLoader.s - read it, the two halves explain each other.
|
|
Read the whole file first. Its routine headers already exist; the per-instruction comments do not.
|
|
|
|
TASK: write annotations/30_boot_fastLoaderC000_C000.json.
|
|
1. A "line" comment on EVERY instruction, explaining intent rather than the mnemonic. Name what each branch
|
|
tests. Explain every magic number: CIA2 port A bit meanings, the handshake sequence, the raster test that
|
|
avoids bad lines, VIC register values for the title picture, KERNAL call arguments, DOS command strings.
|
|
2. Improve the routine headers where you understand a routine better, and add "Called from:" lines.
|
|
3. "block" comments before each logical section.
|
|
4. Point out the self-modifying code: which instruction is patched, by whom, with what, and why (the loader
|
|
rebuilds the stack so its final RTS lands at $0461 instead of returning to its caller).
|
|
Validate with python3 -c "import json;json.load(open('annotations/30_boot_fastLoaderC000_C000.json'))".
|
|
Return path, number of line comments, number of routine headers, problems.`,
|
|
{ label: 'annotate:fastLoaderC000', phase: 'Code', schema: RESULT })
|
|
|
|
phase('Data')
|
|
const dataChunks = args.dataChunks
|
|
const dataResults = await pipeline(dataChunks, (c) => agent(`${common}
|
|
YOUR FILE: unit "${c.unit}", disassembly/${c.file}, addresses $${c.startAddr}-$${c.endAddr}. This file is pure
|
|
data - there is no code in it. survey/data_units.json and the other survey/data_*.json files already describe
|
|
much of it; use them, verify what they claim against the bytes, and go further.
|
|
|
|
TASK: write annotations/30_${c.id}.json so that a reader can understand every byte of this file.
|
|
1. Give every distinct structure a label and a "block" comment saying exactly what it is: how many entries, how
|
|
many bytes per entry, what the index means, which code reads it (from XREF.txt, filtered to the right file),
|
|
and what the values mean.
|
|
2. Choose the right dataTypes so the listing renders the structure readably: "grid" with the natural row width
|
|
for tables (e.g. one screen row, one record, one template row), "bitmap"/"sprite" for graphics so the bit art
|
|
lines up, "text" for strings, "addr" for pointer tables. Split a block into several dataTypes entries when it
|
|
contains several structures.
|
|
3. Where a table is one half of a split low-byte/high-byte pointer pair, say so and say where the other half is;
|
|
do NOT type it as "addr".
|
|
4. Add "line" comments to individual rows where a row means something specific (a particular glyph, a particular
|
|
unit, a particular message id). Do not pad every row with a meaningless comment.
|
|
5. Say plainly which parts are unused, leftover or unreferenced, and what the evidence is.
|
|
Validate the JSON parses. Return path, number of comments written (linesCommented), number of structures
|
|
described (routines), problems.`,
|
|
{ label: `data:${c.id}`, phase: 'Data', schema: RESULT }))
|
|
|
|
phase('Fix')
|
|
const fixed = await agent(`${common}
|
|
TASK: the data survey (survey/data_units.json, survey/data_high_FBB8.json) disproved several statements that the
|
|
earlier survey pass wrote into the listing. Correct them by writing annotations/30_corrections.json (same format,
|
|
notes only - it loads after the survey notes and overrides them).
|
|
1. game/highMemoryFBB8: the listing says $FD70 and $FE24 have "no static reference anywhere in the disassembly"
|
|
and that $FF90 is "most likely unused leftover memory". All of that is wrong: $FD70 and $FE24 are
|
|
run-length-encoded sprite streams, unpacked into $0200 by the RLE unpacker at $67CB, which the comcen screens
|
|
overlay calls from $7596 and $7FF0. Check this yourself in the listings, then write correct block comments
|
|
for $FD70, $FE24 and $FF90 (say what $FF90 really holds if you can determine it; if you cannot, say that
|
|
plainly instead of guessing).
|
|
2. Search the other listings for any remaining claim that a block is "unreferenced" or "unused" which the data
|
|
survey contradicts, and correct those too.
|
|
3. Do not touch anything you have not verified.
|
|
Return path, number of corrections (linesCommented), 0 routines, problems.`,
|
|
{ label: 'corrections', phase: 'Fix', schema: RESULT })
|
|
|
|
phase('Verify')
|
|
const verified = await agent(`Work in /home/scott/claude/modemwars. You are the only agent running now, so you may
|
|
run the build.
|
|
1. python3 -c "import json,glob;[json.load(open(f)) for f in glob.glob('annotations/*.json')]" - fix invalid JSON.
|
|
2. grep -lP '[^\\x00-\\x7F]' annotations/*.json docs/*.md 2>/dev/null - replace any non-ASCII character.
|
|
3. python3 tools/build.py 2>&1 | grep -iE "warning|error|traceback"
|
|
4. ./disassembly/verify.sh | grep -v "^OK" - all 24 units must reassemble byte-exact.
|
|
If a unit fails to ASSEMBLE, find the first ca65 error and fix the annotation entry that caused it.
|
|
If a unit assembles but MISCOMPARES, the cause is a dataTypes entry that changes how many bytes are emitted
|
|
(for example a typed block that overlaps a label, or a "text"/"sprite" run whose length is wrong): find it with
|
|
cmp -l disassembly/build/<unit>.bin disassembly/build/<unit>.orig.bin, locate that address in the listing and fix
|
|
the annotation. Never edit disassembly/*.s or tools/*.
|
|
Duplicate-label warnings from build.py are auto-resolved but indicate two annotations claimed the same name: fix
|
|
the annotation that is wrong.
|
|
Repeat until verify prints 24 OK and build.py prints no warnings. Finally report the per-file instruction comment
|
|
coverage using:
|
|
python3 - <<'PY'
|
|
import re, glob
|
|
tot=com=0
|
|
for f in sorted(glob.glob("disassembly/*/*.s")):
|
|
for l in open(f):
|
|
m=re.match(r"^\\s+[a-z]{3}\\s.*?;\\s[0-9A-F]{4}(.*)$", l)
|
|
if m:
|
|
tot+=1
|
|
if m.group(1).strip(): com+=1
|
|
print(tot, com, round(100*com/tot,1))
|
|
PY
|
|
Return path="disassembly", linesCommented=<commented instruction count>, routines=<total instruction count>,
|
|
problems=[everything you had to fix].`,
|
|
{ label: 'verify', phase: 'Verify', schema: RESULT })
|
|
|
|
return { codeChunk, dataResults: dataResults.filter(Boolean), fixed, verified }
|