@henols/c64-re-tools 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/skills/acme-build/SKILL.md +34 -48
- package/skills/acme-build/scripts/acme.mjs +2 -19
- package/skills/acme-build/template.a +17 -3
- package/skills/c64-memory-mapping/SKILL.md +35 -0
- package/skills/c64-program-recon/SKILL.md +127 -2
- package/skills/c64-program-recon/references/tool-selection.md +1 -2
- package/skills/c64-program-recon/templates/memory-map.template.md +72 -51
- package/skills/c64-provenance-diff/SKILL.md +1 -1
- package/skills/c64-ram-capture/SKILL.md +12 -1
- package/skills/vice-wedge-triage/SKILL.md +16 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henols/c64-re-tools",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Installer that adds the C64 reverse-engineering skills and the VICE emulator MCP server (@henols/vice-mcp) to a project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"installer"
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@henols/vice-mcp": "0.2.
|
|
47
|
+
"@henols/vice-mcp": "0.2.1"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"sync-skills": "node scripts/sync-skills.mjs",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: acme-build
|
|
3
|
-
description: Assemble Commodore 64 6510 assembly with the ACME cross assembler. Use when asked to assemble, build, compile or link .a/.asm 6502/6510 source, produce a C64 .prg, scaffold a new C64 program, list the symbols a program uses
|
|
3
|
+
description: Assemble Commodore 64 6510 assembly with the ACME cross assembler. Use when asked to assemble, build, compile or link .a/.asm 6502/6510 source, produce a C64 .prg, scaffold a new C64 program, or list the symbols a program uses.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Assembling C64 source with ACME
|
|
@@ -13,10 +13,9 @@ A=.claude/skills/acme-build/scripts/acme.mjs # from the repo root
|
|
|
13
13
|
node $A new game.asm # scaffold a C64 program
|
|
14
14
|
node $A build game.asm # assemble -> .prg .sym .vs .rep
|
|
15
15
|
node $A sym game.asm # the symbols the program uses
|
|
16
|
-
node $A disasm game.prg # object code back into ACME source
|
|
17
16
|
```
|
|
18
17
|
|
|
19
|
-
The script wraps `acme` and
|
|
18
|
+
The script wraps `acme` and nothing else — **assembling only**. Running
|
|
20
19
|
the result on a C64 belongs to the emulator skills (`acme.mjs:3-4` says so, and the
|
|
21
20
|
absent `run` verb is not an omission). It contacts nothing.
|
|
22
21
|
|
|
@@ -133,57 +132,43 @@ you also assemble by hand, so these stay recognised as mnemonics.
|
|
|
133
132
|
|
|
134
133
|
## Disassembly
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
game.dis.a: 28 lines
|
|
141
|
-
Read it as a linear decode: trust the instruction stream, and
|
|
142
|
-
treat strings, tables and the BASIC stub as data. To reassemble,
|
|
143
|
-
define the out-of-range labels it emits (Ld020, Lffd2, ...) and
|
|
144
|
-
indent its illegal-opcode lines to the operand column.
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
The default output is `<stem>.dis.a` — one more `.a` file the agent's Read tool
|
|
148
|
-
refuses (see Troubleshooting). Pass a second positional ending `.asm` for an
|
|
149
|
-
agent-readable listing instead (same stdout shape, `game.dis.asm: 28 lines` in
|
|
150
|
-
place of the first line):
|
|
135
|
+
This skill does not disassemble. Static disassembly of a `.prg` or flat 64K image
|
|
136
|
+
is a **required prerequisite** of this plugin, not an optional accelerator:
|
|
137
|
+
regenerator2000, reached through
|
|
151
138
|
|
|
152
139
|
```bash
|
|
153
|
-
|
|
140
|
+
npx -y @henols/vice-mcp r2000 export-asm game.prg # npm installs
|
|
141
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 export-asm game.prg # in-repo/plugin
|
|
154
142
|
```
|
|
155
143
|
|
|
156
|
-
|
|
144
|
+
A recursive-descent disassembler with an auto-analyzer does not render strings,
|
|
145
|
+
tables and the BASIC stub as instructions, so there are no out-of-range labels
|
|
146
|
+
to hand-define and no illegal-opcode lines to re-indent — the caveats this
|
|
147
|
+
section used to carry were structural to a flat linear decoder and do not
|
|
148
|
+
apply here. The exported source is verified reassemblable by a real ACME
|
|
149
|
+
via `vice-mcp r2000 verify` (evidence:
|
|
150
|
+
`.planning/phases/10-adoption-boundaries-automated-bootstrap-and-the-removal/evidence/10-verify-transcript.txt`).
|
|
151
|
+
`c64-program-recon` points at this same route; it is not restated there.
|
|
157
152
|
|
|
158
|
-
|
|
159
|
-
*=$0801
|
|
160
|
-
L0801 !by$0b;ANC# <- BASIC stub, read as data
|
|
161
|
-
L0802 php
|
|
162
|
-
L0805 SHX L3032, y
|
|
163
|
-
...
|
|
164
|
-
L080d lda #$00 <- code, decoded correctly
|
|
165
|
-
L080f sta Ld021
|
|
166
|
-
L0812 lda #$05
|
|
167
|
-
L0814 sta Ld020
|
|
168
|
-
L081e jsr Lffd2
|
|
169
|
-
L0824 rts
|
|
170
|
-
L0825 pha <- PETSCII string, read as data
|
|
171
|
-
```
|
|
153
|
+
## Setup
|
|
172
154
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
155
|
+
`acme` on `$PATH` is the **only** requirement. The scaffold that `new` writes
|
|
156
|
+
assembles against a bare install with no standard hardware-register library —
|
|
157
|
+
that's deliberate: neither a plain `~/.local/bin/acme` build nor the Debian
|
|
158
|
+
trixie `apt` candidate ships one, so a scaffold that depended on it would fail
|
|
159
|
+
to assemble on a fresh install (Phase 8.1 FINDING-A1).
|
|
177
160
|
|
|
178
|
-
|
|
161
|
+
`$ACME` and the wrapper's auto-probe (`$ACME`, `/usr/local/share/acme`,
|
|
162
|
+
`/usr/share/acme`, `/usr/lib/acme`, `~/.acme`) still exist and still matter —
|
|
163
|
+
but only for **your own** sources that use angle-bracket includes (see
|
|
164
|
+
"Writing source" above), not for the scaffold. If you have that library
|
|
165
|
+
somewhere, point `$ACME` at its directory and angle-bracket includes work as
|
|
166
|
+
before; if you don't, the scaffold doesn't need it.
|
|
179
167
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
(`/usr/share/acme` doesn't exist), ACME release 0.97 "Zem" (31 Jan 2021) at
|
|
185
|
-
`/usr/bin/acme`. **Confidence: HIGH** — read off `acme --version` and the probe
|
|
186
|
-
result, not off a package manifest.
|
|
168
|
+
Re-checked against ACME release 0.97 "Zem" (31 Jan 2021). CI now assembles
|
|
169
|
+
the shipped scaffold on every build with `$ACME` cleared (the "Assemble the
|
|
170
|
+
acme-build scaffold (library-free)" step in `.github/workflows/ci.yml`), so
|
|
171
|
+
this claim is re-checkable rather than a one-machine observation.
|
|
187
172
|
|
|
188
173
|
Copy `acme.mjs` into any project's `.claude/skills/acme-build/scripts/`, and
|
|
189
174
|
`template.a` into `.claude/skills/acme-build/`, to use this elsewhere.
|
|
@@ -197,14 +182,15 @@ This one turns source into bytes. It does not restate what the others carry.
|
|
|
197
182
|
| Where to start on an unknown program, and which address to read next | `c64-program-recon` |
|
|
198
183
|
| What a specific address or bit means, or annotating a listing | `c64-memory-mapping` — `node … lookup '$D018'` |
|
|
199
184
|
| A verified 64K image, or comparing two captures | `c64-ram-capture` |
|
|
200
|
-
|
|
|
185
|
+
| Static disassembly of a `.prg` or flat image | `vice-mcp r2000 export-asm` (see Disassembly above) |
|
|
186
|
+
| **Source in, `.prg` out** | here |
|
|
201
187
|
|
|
202
188
|
## References
|
|
203
189
|
|
|
204
190
|
| File | Covers |
|
|
205
191
|
|---|---|
|
|
206
192
|
| `scripts/acme.mjs` | The driver. Its comments are the contract for every flag above |
|
|
207
|
-
| `template.a` | The scaffold `new` writes: BASIC stub with a computed `SYS`,
|
|
193
|
+
| `template.a` | The scaffold `new` writes: BASIC stub with a computed `SYS`, five local hardware constants (no library needed), no `!to` |
|
|
208
194
|
|
|
209
195
|
Findings that make RE faster go in `.planning/RE-FINDINGS.md` **at the moment you
|
|
210
196
|
find them**, graded with `Evidence:` and `Confidence:`. Promote by re-logging with
|
|
@@ -205,22 +205,6 @@ function cmdNew(argv) {
|
|
|
205
205
|
console.log(`next: node ${selfPath()} build ${path}`);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
// `toacme` ships with ACME and turns object code back into ACME source.
|
|
209
|
-
function cmdDisasm(argv) {
|
|
210
|
-
const src = argv[0];
|
|
211
|
-
if (!src) die("usage: disasm <file.prg> [out.a]");
|
|
212
|
-
const out = argv[1] || src.replace(/\.prg$/i, "") + ".dis.a";
|
|
213
|
-
const r = spawnSync("toacme", ["object", src, out], { encoding: "utf8" });
|
|
214
|
-
if (r.error) die("install the ACME cross assembler and put `toacme` on PATH");
|
|
215
|
-
if (r.status !== 0) die(`toacme: ${(r.stderr || r.stdout).trim()}`);
|
|
216
|
-
const n = readFileSync(out, "utf8").split("\n").filter((l) => /^L[0-9a-f]{4}/.test(l)).length;
|
|
217
|
-
console.log(`${out}: ${n} lines`);
|
|
218
|
-
console.log("Read it as a linear decode: trust the instruction stream, and");
|
|
219
|
-
console.log("treat strings, tables and the BASIC stub as data. To reassemble,");
|
|
220
|
-
console.log("define the out-of-range labels it emits (Ld020, Lffd2, ...) and");
|
|
221
|
-
console.log("indent its illegal-opcode lines to the operand column.");
|
|
222
|
-
}
|
|
223
|
-
|
|
224
208
|
// ------------------------------------------------------------------ options
|
|
225
209
|
|
|
226
210
|
function parseOpts(argv) {
|
|
@@ -247,14 +231,13 @@ function parseOpts(argv) {
|
|
|
247
231
|
// --------------------------------------------------------------------- main
|
|
248
232
|
|
|
249
233
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
250
|
-
const VERBS = { new: cmdNew, build: cmdBuild, sym: cmdSym
|
|
234
|
+
const VERBS = { new: cmdNew, build: cmdBuild, sym: cmdSym };
|
|
251
235
|
if (!cmd || !VERBS[cmd]) {
|
|
252
236
|
console.log(`usage: node ${selfPath()} <command> [options]
|
|
253
237
|
|
|
254
|
-
new <file.a> scaffold a C64 program (BASIC stub
|
|
238
|
+
new <file.a> scaffold a C64 program (BASIC stub, no libraries needed)
|
|
255
239
|
build <file.a> assemble -> .prg .sym .vs .rep
|
|
256
240
|
sym <file.a> list the symbols the program uses
|
|
257
|
-
disasm <file.prg> [out.a] turn object code back into ACME source
|
|
258
241
|
|
|
259
242
|
options: -o FILE --out-dir DIR -f FORMAT --setpc ADDR -DSYM=VAL -I DIR
|
|
260
243
|
--no-report --json`);
|
|
@@ -2,11 +2,25 @@
|
|
|
2
2
|
; Build: node .claude/skills/acme-build/scripts/acme.mjs build THIS.a
|
|
3
3
|
; No !to here on purpose - the driver passes -o, and having both makes ACME
|
|
4
4
|
; warn "Output file already chosen" and silently ignore the !to.
|
|
5
|
+
;
|
|
6
|
+
; No angle-bracket library includes here on purpose. Neither documented ACME
|
|
7
|
+
; provisioning route ships the standard hardware-register library - a bare
|
|
8
|
+
; `~/.local/bin/acme` install and the Debian trixie `apt` candidate were both
|
|
9
|
+
; verified to lack it (Phase 8.1 FINDING-A1) - so a scaffold that depends on
|
|
10
|
+
; it fails to assemble on a fresh install. The five constants below are
|
|
11
|
+
; exactly what this scaffold's body uses, defined locally instead. If you
|
|
12
|
+
; want the full library for your own sources' angle-bracket includes, set
|
|
13
|
+
; $ACME to a directory containing that library (with vic.a inside it);
|
|
14
|
+
; acme.mjs's findAcmeLib() already probes for it and passes it through to
|
|
15
|
+
; the child.
|
|
5
16
|
|
|
6
17
|
!cpu 6510 ; C64's CPU: legal 6502 + the illegal opcodes
|
|
7
|
-
|
|
8
|
-
!
|
|
9
|
-
!
|
|
18
|
+
|
|
19
|
+
!address vic_cborder = $d020 ; VIC-II border color register
|
|
20
|
+
!address vic_cbg = $d021 ; VIC-II background color register
|
|
21
|
+
viccolor_BLACK = 0
|
|
22
|
+
viccolor_GREEN = 5
|
|
23
|
+
!address k_chrout = $ffd2 ; KERNAL: print one PETSCII char
|
|
10
24
|
|
|
11
25
|
* = $0801 ; start of BASIC RAM
|
|
12
26
|
|
|
@@ -186,6 +186,41 @@ were expecting. Because it mutates the repo, `memmap` belongs behind a GSD
|
|
|
186
186
|
command (`/gsd-quick`), per this project's GSD Workflow Enforcement rule — it is
|
|
187
187
|
not a read-only lookup like `lookup` and `annotate`.
|
|
188
188
|
|
|
189
|
+
## Feeding the enum generator
|
|
190
|
+
|
|
191
|
+
`memmap.json`'s structured `bits` entries are the source of the curated register bit-name table used
|
|
192
|
+
to generate program-specific enums for regenerator2000's annotation store (R2000-13): register
|
|
193
|
+
writes disassemble as `lda #D011_YSCROLL3_ROW25_SCREENON_TEXT` instead of a bare `#$1b`. The
|
|
194
|
+
generator is `.claude/mcp/vice/r2000-regbits-gen.ts`; its committed output is
|
|
195
|
+
`.claude/mcp/vice/r2000-regbits.json`; and that output is **digest-pinned** to `memmap.json` — a
|
|
196
|
+
`node r2000-regbits-gen.ts` run compares its own fresh build against the committed file, and CI fails
|
|
197
|
+
if `memmap.json` changed without a re-run.
|
|
198
|
+
|
|
199
|
+
**The honest gap:** only 29 of this file's 959 entries carry a structured `bits` array. `$D015`,
|
|
200
|
+
`$D017`, `$D01A` and `$D01B`–`$D01D` — the sprite-plane bitmask registers a real game writes
|
|
201
|
+
constantly — are **not** among those 29, so the enum generator supplies them from its own curated
|
|
202
|
+
override table (`OVERRIDES` in `r2000-regbits-gen.ts`), not from this file. Widening `memmap.json`'s
|
|
203
|
+
`io` parser (or repairing the OCR damage already present in some `bits` prose, e.g. a letter `O` for
|
|
204
|
+
the digit `0`) so those registers get a real structured entry here is separate work belonging to this
|
|
205
|
+
skill, not the generator.
|
|
206
|
+
|
|
207
|
+
**Installing those bit names into a project's own disassembly:** the table above only builds
|
|
208
|
+
`r2000-regbits.json` — turning a specific project's register *writes* into named enum variants is a
|
|
209
|
+
separate, later step, once a `.regen2000proj` already exists (`r2000 bootstrap`, see
|
|
210
|
+
`c64-program-recon`):
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npx -y @henols/vice-mcp r2000 gen-enums game.regen2000proj # npm install
|
|
214
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 gen-enums game.regen2000proj # in-repo/plugin
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`r2000 gen-enums` requires an EXISTING `.regen2000proj` — it does not bootstrap one from a raw
|
|
218
|
+
input. It reads the project's own disassembly, creates one enum variant per DISTINCT value actually
|
|
219
|
+
written at each matching immediate-load address (named from the curated table above), and prints
|
|
220
|
+
total/paired/unpaired register-store counts plus a per-enum variant count. It exits non-zero, naming
|
|
221
|
+
the reason, when either of its two internal search passes hits its own 10000-row ceiling — pass
|
|
222
|
+
`--max-results` to raise that ceiling for a program whose store exceeds it.
|
|
223
|
+
|
|
189
224
|
## Troubleshooting
|
|
190
225
|
|
|
191
226
|
| Symptom | Fix |
|
|
@@ -115,6 +115,130 @@ same IRQ entry that phase-01 live work established independently (chain `$1103
|
|
|
115
115
|
The method reproduces a known-good result from a static image with no emulator running, and the
|
|
116
116
|
`$1116` pair is new — see `references/control-flow.md` § 2. **Confidence: HIGH** for steps 1-2.
|
|
117
117
|
|
|
118
|
+
## Writing findings into the annotation store
|
|
119
|
+
|
|
120
|
+
Recon's findings are not memory-map prose written once and left to rot — they are entries in a
|
|
121
|
+
queryable annotation store, and the Markdown memory map is a *generated view* of that store (D-24),
|
|
122
|
+
not something you hand-edit yourself.
|
|
123
|
+
|
|
124
|
+
**Open or bootstrap the store**, then hand its path to every call that follows:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npx -y @henols/vice-mcp r2000 bootstrap game.prg # npm install
|
|
128
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 bootstrap game.prg # in-repo/plugin
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Every `r2000_*` tool takes an explicit `project` path pointing at the resulting `.regen2000proj`
|
|
132
|
+
(D-19) — there is no ambient session state naming the store, so which project a call touched is
|
|
133
|
+
always visible in the transcript.
|
|
134
|
+
|
|
135
|
+
**Write findings with the named tools, not a Markdown row:**
|
|
136
|
+
|
|
137
|
+
| Tool | Use for |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `r2000_set_label_name` | Naming a routine or table (`init_screen`, `sprite_table`) |
|
|
140
|
+
| `r2000_set_data_type` | Classifying a block (`code`, `byte`, `address`, `petscii`, …) |
|
|
141
|
+
| `r2000_add_scope` | Marking a handler's extent as a lexical scope |
|
|
142
|
+
| `r2000_set_comment` | Recording the evidence — the carrier for the confidence grade below |
|
|
143
|
+
| `r2000_batch_execute` | Bulk annotation, 5+ independent calls at once — a real memory map is dozens of labels/comments/block ranges, and batching is what makes that affordable under the per-call spawn-load-mutate-save-exit lifecycle |
|
|
144
|
+
|
|
145
|
+
**Grade with the confidence prefix.** Lead every evidence comment with exactly one of these five
|
|
146
|
+
bracket tokens (quoted verbatim from `r2000-confidence.ts`, the parser's own source of truth):
|
|
147
|
+
|
|
148
|
+
`[confirmed-code]` (confirmed code), `[probable-code]` (probable code), `[confirmed-data]`
|
|
149
|
+
(confirmed data), `[probable-data]` (probable data), `[unknown]` (unknown).
|
|
150
|
+
|
|
151
|
+
A typo in the bracket token — wrong case, an underscore, a plural, stray whitespace — **fails
|
|
152
|
+
loudly**; it does not silently degrade into an ungraded comment. As with `RE-FINDINGS.md`, do not
|
|
153
|
+
promote a row by editing its grade in place: re-verify and restate the evidence with a fresh
|
|
154
|
+
`r2000_set_comment` call, so the record of when something stopped being a guess survives.
|
|
155
|
+
|
|
156
|
+
**Query instead of re-deriving.** `r2000_get_symbols`, `r2000_get_comments`, `r2000_get_blocks` and
|
|
157
|
+
`r2000_get_cross_references` answer straight from the store. `r2000_search_disassembly` searches
|
|
158
|
+
labels, comments and instructions together — but `max_results` is **REQUIRED** on this surface,
|
|
159
|
+
because regenerator2000's own default is 50 and silently truncates a full-program pass. The query
|
|
160
|
+
this whole workflow exists to make cheap:
|
|
161
|
+
|
|
162
|
+
> "Show me everything still `[unknown]`" → `r2000_search_disassembly` with `query: "[unknown]"` and
|
|
163
|
+
> an explicit `max_results` set above your program's comment count.
|
|
164
|
+
|
|
165
|
+
(The composite address-details lookup is deliberately not on this surface — D-32, a 64K-project
|
|
166
|
+
defect filed upstream — its answer is reachable as a combination of the tools above.)
|
|
167
|
+
|
|
168
|
+
### Take names to the running machine, and bring live findings back
|
|
169
|
+
|
|
170
|
+
The store and the running emulator are not two independent destinations for a name — writing one
|
|
171
|
+
into the store and discovering one live are two legs of **one loop**, in this order, matching how
|
|
172
|
+
`R2000-14`/`R2000-15` were actually proven (see Phase 11's live walkthrough,
|
|
173
|
+
`evidence/criterion4/WALKTHROUGH.md`):
|
|
174
|
+
|
|
175
|
+
1. **Export what the store already knows.** `r2000 export-lbl <project>` writes `al C:xxxx .Name`
|
|
176
|
+
lines that `stock-symbols.ts`'s own parser accepts — the verb reads the written file back
|
|
177
|
+
through that same parser before it reports success, never trusting a regenerator2000 exit code
|
|
178
|
+
alone.
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npx -y @henols/vice-mcp r2000 export-lbl game.regen2000proj # npm install
|
|
182
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 export-lbl game.regen2000proj # in-repo/plugin
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
2. **Load it into the running machine — `vice_symbols_load`, exactly once.** Load that `.lbl` file
|
|
186
|
+
into the live emulator with `vice_symbols_load`. Call it **exactly once** per regenerated file:
|
|
187
|
+
it REPLACES the machine's symbol table rather than merging into it, so loading an older export a
|
|
188
|
+
second time after the store has moved on would silently discard the newer names.
|
|
189
|
+
3. **Discover something live the static pass could not, then write it to the store first.**
|
|
190
|
+
Disassembling or reading the running machine (`vice_disassemble`, a checkpoint hit, …) can turn
|
|
191
|
+
up a name the static store never had. Write it with `r2000_set_label_name` *before* regenerating
|
|
192
|
+
anything — the store is the merge point (D-29), not your own notes.
|
|
193
|
+
4. **Regenerate the whole `.lbl` and bring it back with `import-lbl`, never an incremental patch.**
|
|
194
|
+
`r2000 import-lbl <project> <lbl>` imports an externally-produced `.lbl` file into the project,
|
|
195
|
+
and reports whether the import was **disk-verified** — re-read from disk in a fresh process,
|
|
196
|
+
never trusted from the child's own success text alone.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npx -y @henols/vice-mcp r2000 import-lbl game.regen2000proj discovered.lbl # npm install
|
|
200
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 import-lbl game.regen2000proj discovered.lbl # in-repo/plugin
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Two traps: `export-lbl` exports **USER** labels only — the auto-generated `a_D011`/`e_FFD2`
|
|
204
|
+
externals never appear in the written file. And both verbs require an EXISTING `.regen2000proj`;
|
|
205
|
+
neither one bootstraps a project from a raw input.
|
|
206
|
+
|
|
207
|
+
`r2000 gen-enums` — turning register writes into named enum variants — is documented in
|
|
208
|
+
`c64-memory-mapping`, alongside the `memmap.json` bit table it consumes.
|
|
209
|
+
|
|
210
|
+
**Generate the memory map; do not hand-author it.** Fill in the provenance sidecar (schema and a
|
|
211
|
+
filled example live in `templates/memory-map.template.md`), then:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npx -y @henols/vice-mcp r2000 render-memmap game.regen2000proj --provenance sidecar.json
|
|
215
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 render-memmap game.regen2000proj --provenance sidecar.json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Add `--check` to detect drift — either a hand edit to the rendered file, or a store change since it
|
|
219
|
+
was last rendered. The rendered file carries a generated-file banner; treat it like every other
|
|
220
|
+
generated artifact in this repo and never hand-edit it.
|
|
221
|
+
|
|
222
|
+
## Static disassembly
|
|
223
|
+
|
|
224
|
+
Turning a `.prg` or a flat 64K image into ACME source, offline, is not part of this
|
|
225
|
+
skill's own method — it is a separate route:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
npx -y @henols/vice-mcp r2000 export-asm game.prg # npm installs
|
|
229
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 export-asm game.prg # in-repo/plugin
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
This is **static**, over a file on disk — `vice_disassemble` (the live-RAM route
|
|
233
|
+
this skill's own table above uses) reads a running emulator's RAM at a checkpoint
|
|
234
|
+
instead. The two are complementary: reach for the static route before the emulator
|
|
235
|
+
is even running, and for `vice_disassemble` once you have a live checkpoint to
|
|
236
|
+
decode from.
|
|
237
|
+
|
|
238
|
+
Extracting from a `.d64` image: name the file inside the image explicitly. The
|
|
239
|
+
tool lists the directory and refuses rather than guess (D-02) — a guess could
|
|
240
|
+
analyse a cracktro or loader stub instead of the game.
|
|
241
|
+
|
|
118
242
|
## Before you touch the emulator
|
|
119
243
|
|
|
120
244
|
Two hazards cost this project real sessions. Both are in `references/observation-hazards.md`; these
|
|
@@ -137,7 +261,8 @@ This one is the route between the stations. It does not restate what the others
|
|
|
137
261
|
|---|---|
|
|
138
262
|
| A verified 64K image, or comparing two captures | `c64-ram-capture` |
|
|
139
263
|
| What a specific address or bit means | `c64-memory-mapping` — `node … lookup '$D018'` |
|
|
140
|
-
| Assembling
|
|
264
|
+
| Assembling | `acme-build` |
|
|
265
|
+
| Static disassembly of a `.prg` or flat image | `vice-mcp r2000 export-asm` (see above) |
|
|
141
266
|
| Whether a byte is original or cracker-changed | `c64-provenance-diff` |
|
|
142
267
|
| The emulator stopped moving — wedged, self-trapped, or respawned | `vice-wedge-triage` |
|
|
143
268
|
| **Which address to read next, and what the answer rules out** | here |
|
|
@@ -152,7 +277,7 @@ This one is the route between the stations. It does not restate what the others
|
|
|
152
277
|
| `references/observation-hazards.md` | Every way a live read gives a wrong answer. **Read before driving.** |
|
|
153
278
|
| `references/tool-selection.md` | Which `mcp__plugin_c64-re-tools_vice__*` call answers which question, and what to delegate |
|
|
154
279
|
| `references/reconstruction.md` | Binary inclusion, behavioural-equivalence correctness bar, SMC labels, label vocabulary |
|
|
155
|
-
| `templates/memory-map.template.md` |
|
|
280
|
+
| `templates/memory-map.template.md` | `render-memmap`'s provenance sidecar schema and the confidence vocabulary — the rendered map itself is generated, not hand-authored |
|
|
156
281
|
|
|
157
282
|
Findings that make RE faster go in `.planning/RE-FINDINGS.md` **at the moment you find them**,
|
|
158
283
|
graded with `Evidence:` and `Confidence:`. Promote by re-logging with the new evidence, never by
|
|
@@ -30,8 +30,7 @@ usage, not measured). Individual rows that have since been exercised live are ma
|
|
|
30
30
|
| What does address X mean? | the `c64-memory-mapping` skill — `node … lookup '$D018'`. **Do not restate its tables.** |
|
|
31
31
|
| Is this byte original or cracker-changed? | the `c64-provenance-diff` skill |
|
|
32
32
|
| A verified 64K image, or comparing two captures | the `c64-ram-capture` skill |
|
|
33
|
-
|
|
|
34
|
-
| Traced disassembly with code/data separation | regenerator2000 — still MEDIUM per `STACK.md`; its first real run is its verification |
|
|
33
|
+
| Traced disassembly with code/data separation | regenerator2000, via `vice-mcp r2000 export-asm` — a recursive-descent disassembler with an auto-analyzer; verified live: its `--verify` run reassembled byte-identically through a real ACME for both a `.prg` and a flat 64K image (`.planning/phases/10-adoption-boundaries-automated-bootstrap-and-the-removal/evidence/10-verify-transcript.txt`) |
|
|
35
34
|
|
|
36
35
|
## Three traps in this table
|
|
37
36
|
|
|
@@ -1,62 +1,83 @@
|
|
|
1
|
-
# Memory map
|
|
2
|
-
|
|
3
|
-
Capture: `<path to the 64K image>` · SHA-256 `<hash>`
|
|
4
|
-
`$01` = `<value>` · VIC bank `<n>` (`$DD00` = `<value>`) · video standard `<PAL/NTSC>`
|
|
5
|
-
Live vector pair: `<$0314/$0315 or $FFFE/$FFFF>` → `<handler>`
|
|
6
|
-
|
|
7
|
-
Every row carries a confidence. Do not promote a row by editing its grade — re-verify and restate
|
|
8
|
-
the evidence, so the record of when something stopped being a guess survives.
|
|
9
|
-
|
|
10
|
-
| Range | Contents | Confidence | Evidence |
|
|
11
|
-
|---|---|---|---|
|
|
12
|
-
| `$0000-$00FF` | Zero page — game variables | | |
|
|
13
|
-
| `$0100-$01FF` | Stack | CONFIRMED | hardware |
|
|
14
|
-
| `$0200-$03FF` | KERNAL work area / vectors | | |
|
|
15
|
-
| `$0400-$07E7` | Screen RAM (if VM resolves here) | | |
|
|
16
|
-
| `$0801-$` | | | |
|
|
17
|
-
| `$D800-$DBFF` | Colour RAM | CONFIRMED | hardware, not banked |
|
|
18
|
-
| `$E000-$FFFF` | RAM under KERNAL (HIRAM=0) or KERNAL ROM | | |
|
|
19
|
-
|
|
20
|
-
Confidence vocabulary — the project's HIGH / MEDIUM / LOW scale, applied to classification:
|
|
21
|
-
|
|
22
|
-
| Grade | Means |
|
|
23
|
-
|---|---|
|
|
24
|
-
| **confirmed code** | Executed during tracing, PC observed inside it |
|
|
25
|
-
| **probable code** | Reachable through a `JSR`/`JMP`/vector, not yet observed executing |
|
|
26
|
-
| **confirmed data** | Never hit as an instruction stream across full gameplay coverage |
|
|
27
|
-
| **probable data** | Indexed-load target, or matches a data shape (sprite blocks, PETSCII, address tables) |
|
|
28
|
-
| **unknown** | No reliable interpretation yet |
|
|
1
|
+
# Memory map generation
|
|
29
2
|
|
|
30
|
-
|
|
31
|
-
|
|
3
|
+
**The memory map is GENERATED, not hand-authored (D-24).** The store — labels, comments, block
|
|
4
|
+
types and scopes written through the `r2000_*` tools described in `../SKILL.md` — is canonical. This
|
|
5
|
+
file used to be a fill-in-the-rows document; it is now the schema for the one input the generator
|
|
6
|
+
needs beyond the store itself, plus the confidence vocabulary that store comments carry.
|
|
32
7
|
|
|
33
|
-
|
|
8
|
+
Run the generator once findings are in the store:
|
|
34
9
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| Charset / bitmap (CB) | | `$D018` bits 1-3 |
|
|
40
|
-
| Mode | | `$D011` bits 5-6, `$D016` bit 4 |
|
|
41
|
-
| Sprite pointers | | VM + `$03F8` |
|
|
10
|
+
```bash
|
|
11
|
+
npx -y @henols/vice-mcp r2000 render-memmap game.regen2000proj --provenance sidecar.json
|
|
12
|
+
node <plugin-root>/.claude/mcp/vice/vice-proxy.ts r2000 render-memmap game.regen2000proj --provenance sidecar.json
|
|
13
|
+
```
|
|
42
14
|
|
|
43
|
-
|
|
15
|
+
Add `--check` to compare the rendered file on disk against a fresh render — it exits non-zero and
|
|
16
|
+
prints the first differing line on **either** a hand edit to the rendered file **or** a store change
|
|
17
|
+
since it was last rendered. There is no way to "fix" drift by editing the rendered file directly:
|
|
18
|
+
the fix is always to re-run the generator (or, if the sidecar itself is stale, correct it and
|
|
19
|
+
re-run). The generated file carries a banner naming the store, the sidecar and a content digest —
|
|
20
|
+
do not strip it.
|
|
44
21
|
|
|
45
|
-
##
|
|
22
|
+
## The provenance sidecar
|
|
46
23
|
|
|
47
|
-
|
|
24
|
+
Some facts belong to the **run** (which capture, which `$01`, which video standard) rather than to
|
|
25
|
+
any address, and the store has no address-keyed shape for them (D-27). They are supplied to the
|
|
26
|
+
renderer as a small JSON sidecar, hand-authored from `c64-ram-capture`'s and `derive.mjs`'s own
|
|
27
|
+
outputs and validated by the renderer — a missing or malformed key is a named error listing every
|
|
28
|
+
problem at once, never a `<placeholder>` silently rendered into a published document.
|
|
29
|
+
|
|
30
|
+
| Key | Type | Where it comes from |
|
|
48
31
|
|---|---|---|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
32
|
+
| `capturePath` | string | The path to the captured 64K image, as given to `c64-ram-capture` |
|
|
33
|
+
| `captureSha256` | string, 64 hex chars | `compare.mjs digest`'s `sha256` — proves which image the map describes |
|
|
34
|
+
| `port01` | string | `derive.mjs vectors`' `$01` value |
|
|
35
|
+
| `dd00` | string | `derive.mjs vic`'s `--dd00` input, i.e. the observed `$DD00` |
|
|
36
|
+
| `vicBank` | string | `derive.mjs vic` — VIC bank derived from `$DD00` bits 0-1, inverted |
|
|
37
|
+
| `screenRam` | string | `derive.mjs vic` — screen RAM derived from `$D018` bits 4-7 |
|
|
38
|
+
| `charsetOrBitmap` | string | `derive.mjs vic` — charset/bitmap derived from `$D018` bits 1-3 (note the char-ROM shadow case) |
|
|
39
|
+
| `mode` | string | `derive.mjs vic` — graphics mode derived from `$D011` bits 5-6 and `$D016` bit 4 |
|
|
40
|
+
| `videoStandard` | `"PAL"` or `"NTSC"` | Known from the capture's origin/hardware context |
|
|
41
|
+
| `liveVectorPair` | string | `derive.mjs vectors` — the live vector pair (`$0314/$0315` or `$FFFE/$FFFF`) |
|
|
42
|
+
| `vectorHandler` | string | The address the live vector pair points at, confirmed live at a checkpoint |
|
|
43
|
+
| `rasterPositions` | string array, optional | One entry per observed `$D012` write on the way out of the live IRQ handler; `derive.mjs sprites` where sprite coordinates are relevant |
|
|
44
|
+
|
|
45
|
+
A fully-filled example, with plausible values in place of placeholders — copy this shape, never the
|
|
46
|
+
literal values:
|
|
53
47
|
|
|
54
|
-
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"capturePath": "captures/game.raw",
|
|
51
|
+
"captureSha256": "3f8a1c9e2b7d4a6f0c5e8b2d9a1f4c7e6b3d0a9c8f5e2b1d4a7c0f3e6b9d2a5c",
|
|
52
|
+
"port01": "$40",
|
|
53
|
+
"dd00": "$06",
|
|
54
|
+
"vicBank": "1 ($4000-$7FFF)",
|
|
55
|
+
"screenRam": "$0400",
|
|
56
|
+
"charsetOrBitmap": "$1000 (ROM shadow)",
|
|
57
|
+
"mode": "text, multicolor off",
|
|
58
|
+
"videoStandard": "PAL",
|
|
59
|
+
"liveVectorPair": "$FFFE/$FFFF",
|
|
60
|
+
"vectorHandler": "$1103",
|
|
61
|
+
"rasterPositions": ["$F8", "$00"]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|---|---|---|---|
|
|
58
|
-
| | `Maybe_` | | |
|
|
65
|
+
## Confidence vocabulary
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
Every comment written into the store through `r2000_set_comment` that grades a finding leads with
|
|
68
|
+
one of these five bracket tokens (the parser in `r2000-confidence.ts` throws on anything that is
|
|
69
|
+
close but not exact — a typo never silently degrades into an ungraded comment):
|
|
70
|
+
|
|
71
|
+
| Grade | Bracket token | Means |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| **confirmed code** | `[confirmed-code]` | Executed during tracing, PC observed inside it |
|
|
74
|
+
| **probable code** | `[probable-code]` | Reachable through a `JSR`/`JMP`/vector, not yet observed executing |
|
|
75
|
+
| **confirmed data** | `[confirmed-data]` | Never hit as an instruction stream across full gameplay coverage |
|
|
76
|
+
| **probable data** | `[probable-data]` | Indexed-load target, or matches a data shape (sprite blocks, PETSCII, address tables) |
|
|
77
|
+
| **unknown** | `[unknown]` | No reliable interpretation yet |
|
|
78
|
+
|
|
79
|
+
Do not force an unknown range through a disassembler and record the output as code. A linear
|
|
80
|
+
decode of data is silently wrong and contaminates everything downstream.
|
|
61
81
|
|
|
62
|
-
-
|
|
82
|
+
**Do not promote a row by editing its grade.** Re-verify and restate the evidence with a fresh
|
|
83
|
+
`r2000_set_comment` call, so the record of when something stopped being a guess survives.
|
|
@@ -231,7 +231,7 @@ addresses.
|
|
|
231
231
|
| A verified 64K image, or proving two captures equivalent | `c64-ram-capture` |
|
|
232
232
|
| Which address to read next, and what the answer rules out | `c64-program-recon` |
|
|
233
233
|
| What a specific address or bit means | `c64-memory-mapping` — `node … lookup '$D018'` |
|
|
234
|
-
| Assembling
|
|
234
|
+
| Assembling | `acme-build` |
|
|
235
235
|
| **Whether a byte is original, cracker-changed, or unknown** | here |
|
|
236
236
|
|
|
237
237
|
Findings that make RE faster go in `.planning/RE-FINDINGS.md` **at the moment you
|
|
@@ -262,6 +262,17 @@ Capture the power-on image as the very first action against a fresh machine, the
|
|
|
262
262
|
idle-capture twice more and run `floor` over the set. State the result as a
|
|
263
263
|
floor, not a complete set — more captures can only widen it.
|
|
264
264
|
|
|
265
|
+
## Feeding the memory map's provenance sidecar
|
|
266
|
+
|
|
267
|
+
`c64-program-recon`'s generated memory map (`vice-mcp r2000 render-memmap`) takes a small provenance
|
|
268
|
+
sidecar as input, and this skill supplies one of its fields: `scripts/compare.mjs digest`'s `sha256`
|
|
269
|
+
and `size` become the sidecar's `captureSha256`, proving which image the rendered map describes. The
|
|
270
|
+
sidecar's other run-scoped keys (`port01`, `dd00`, `vicBank`, `screenRam`, `charsetOrBitmap`, `mode`,
|
|
271
|
+
`liveVectorPair`, `vectorHandler`, `rasterPositions`) come from `c64-program-recon`'s own
|
|
272
|
+
`derive.mjs` — **this skill does not emit the sidecar itself.** The sidecar is hand-authored from
|
|
273
|
+
those two skills' outputs and validated by the renderer, which throws naming every missing or
|
|
274
|
+
malformed key at once rather than rendering a document that silently carries a placeholder.
|
|
275
|
+
|
|
265
276
|
## Which skill does what
|
|
266
277
|
|
|
267
278
|
This one owns the image and its identity. It does not restate what the others carry.
|
|
@@ -271,7 +282,7 @@ This one owns the image and its identity. It does not restate what the others ca
|
|
|
271
282
|
| Which address to read next, and what the answer rules out | `c64-program-recon` |
|
|
272
283
|
| Every way a live read gives a wrong answer | `c64-program-recon` — `references/observation-hazards.md`. **Read before driving.** |
|
|
273
284
|
| What a specific address or bit means | `c64-memory-mapping` — `node … lookup '$D018'` |
|
|
274
|
-
| Assembling
|
|
285
|
+
| Assembling | `acme-build` |
|
|
275
286
|
| Whether a byte is original or cracker-changed, and what `bucketed` means | `c64-provenance-diff` |
|
|
276
287
|
| Whether the emulator is wedged, and whether it is safe to recycle | `vice-wedge-triage` |
|
|
277
288
|
| **A verified 64K image, or proving two captures equivalent** | here |
|
|
@@ -160,6 +160,22 @@ fields above** — unit-proven (`stock-run-until.test.ts`, 21/21, 07-14) but not
|
|
|
160
160
|
re-exercised against a real emulator by this gap-closure batch. **MEDIUM on the fork** — read off
|
|
161
161
|
the tool schema, not reproduced.
|
|
162
162
|
|
|
163
|
+
**A second binary-monitor client is contention, not a wedge, and it has a cheap tell — stock
|
|
164
|
+
only.** Stock VICE's binary monitor services exactly one client; a second `connect()` sits
|
|
165
|
+
unserviced in the backlog with no reply and no EOF. The discriminator: a socket that *accepts* the
|
|
166
|
+
connection but never answers is contention, not a hung emulator — and the broker itself already
|
|
167
|
+
knows whether it holds a lease on that port, which is the thing a human or agent can actually go
|
|
168
|
+
check instead of guessing. Named causes, so a reader knows where to look: a hand-run `nc` session
|
|
169
|
+
left open against the port, a second Claude Code session driving the same instance, VICE's own
|
|
170
|
+
`-remotemonitor`, and any other 6502 debugger that dials in — including regenerator2000's own
|
|
171
|
+
`--vice` flag. **This plugin's own regenerator2000 route can never be one of them:** the launch
|
|
172
|
+
path refuses `--vice` by construction (no caller-supplied argv passthrough exists to inject it in
|
|
173
|
+
the first place) *and* by a scan that throws if the flag is ever present in the final argv — not by
|
|
174
|
+
documentation alone (`R2000-01`, plan 10-01) — so a user chasing a silent emulator can rule this
|
|
175
|
+
project's own r2000 integration out immediately, rather than suspecting it. The standing advice
|
|
176
|
+
does not change: contention is **never** a reason to recycle — the instance is healthy, merely
|
|
177
|
+
claimed elsewhere.
|
|
178
|
+
|
|
163
179
|
## The manual fallback, when `vice_diagnose` cannot answer
|
|
164
180
|
|
|
165
181
|
`vice_diagnose` needs the host broker running. When it reports that no `broker.json` record
|