@henols/c64-re-tools 0.2.2 → 0.2.4
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/bin/cli.mjs +7 -4
- package/package.json +2 -2
- package/skills/acme-build/SKILL.md +39 -23
- package/skills/acme-build/scripts/acme.mjs +159 -64
- package/skills/acme-build/template.a +1 -1
- package/skills/c64-disk-access/SKILL.md +156 -0
- package/skills/c64-disk-access/scripts/c1541.mjs +569 -0
- package/skills/c64-memory-mapping/SKILL.md +30 -23
- package/skills/c64-memory-mapping/scripts/driver.mjs +1 -1
- package/skills/c64-petcat/SKILL.md +87 -0
- package/skills/c64-petcat/scripts/petcat.mjs +221 -0
- package/skills/c64-program-recon/SKILL.md +93 -39
- package/skills/c64-program-recon/references/control-flow.md +12 -15
- package/skills/c64-program-recon/references/graphics.md +1 -1
- package/skills/c64-program-recon/references/observation-hazards.md +18 -16
- package/skills/c64-program-recon/references/reconstruction.md +1 -2
- package/skills/c64-program-recon/references/sound-and-input.md +6 -8
- package/skills/c64-program-recon/references/tool-selection.md +36 -17
- package/skills/c64-program-recon/scripts/packer-finding.mjs +165 -87
- package/skills/c64-program-recon/templates/memory-map.template.md +2 -2
- package/skills/c64-provenance-diff/SKILL.md +40 -5
- package/skills/c64-provenance-diff/scripts/diff-images.mjs +8 -8
- package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +7 -5
- package/skills/c64-ram-capture/SKILL.md +112 -44
- package/skills/c64-ram-capture/scripts/compare.mjs +2 -2
- package/skills/c64-ram-capture/scripts/derive-transients.mjs +575 -0
- package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +3 -3
- package/skills/c64-ram-capture/scripts/mcp-module.mjs +174 -0
- package/skills/c64-ram-capture/scripts/releases.mjs +1 -1
- package/skills/c64-ram-capture/scripts/vsf-slice.mjs +147 -0
- package/skills/c64-ram-capture/scripts/watch-loads.mjs +15 -15
- package/skills/c64-ram-capture/templates/capture-record.template.md +44 -4
- package/skills/c64-ram-capture/transients/README.md +136 -0
- package/skills/routine-queue-walker/SKILL.md +114 -22
- package/skills/routine-queue-walker/scripts/completeness-report.mjs +465 -0
- package/skills/vice-wedge-triage/SKILL.md +96 -89
- package/skills/c64-ram-capture/scripts/d64-parse.mjs +0 -243
|
@@ -15,11 +15,11 @@ and name the offending address when it is wrong.
|
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
S=src/skills/c64-ram-capture/scripts # from the repo root
|
|
18
|
-
|
|
18
|
+
A=$S/dump-artifacts.mjs
|
|
19
19
|
C=$S/compare.mjs L=$S/releases.mjs
|
|
20
|
+
T=$S/derive-transients.mjs V=$S/vsf-slice.mjs
|
|
21
|
+
TD=src/skills/c64-ram-capture/transients # the derived allow-lists live here
|
|
20
22
|
|
|
21
|
-
node $P directory --image path/to/image.d64 # what's on the disk (--json flags faked entries)
|
|
22
|
-
node $P bam --image path/to/image.d64 # disk name, DOS type, occupied track ranges
|
|
23
23
|
node $A assemble --chunks chunks.json # size + digest, writes nothing
|
|
24
24
|
node $A write-set --release <id> --label <label> \
|
|
25
25
|
--chunks chunks.json --raw raw.json # the four committed artifacts
|
|
@@ -28,10 +28,16 @@ node $L list # the valid --release ids
|
|
|
28
28
|
node $C digest dump.bin # sha256 + size, for the capture record
|
|
29
29
|
node $C compare a.bin b.bin # classify every difference, exit 1 on FAIL
|
|
30
30
|
node $C floor a.bin b.bin c.bin # drift floor across a capture set
|
|
31
|
+
|
|
32
|
+
node $T derive --release <id> --out $TD/<id>.json a.bin b.bin c.bin
|
|
33
|
+
node $T check --allow-list $TD/<id>.json a.bin b.bin
|
|
34
|
+
|
|
35
|
+
node $V slice run1.vsf --out run1.bin # flat 64K image from a .vsf snapshot
|
|
36
|
+
node $V digest run1.vsf # sha256 + size, writing no file
|
|
31
37
|
```
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
own `mcp__plugin_c64-re-tools_vice__*` calls. They contact nothing.
|
|
39
|
+
Every module above reads only committed files and the JSON **you** wrote from
|
|
40
|
+
your own `mcp__plugin_c64-re-tools_vice__*` calls. They contact nothing.
|
|
35
41
|
|
|
36
42
|
**Prerequisite: a resolvable project root.** `scripts/project-paths.mjs` uses
|
|
37
43
|
`C64RE_PROJECT_ROOT` when it is set, and otherwise walks up from the toolkit's
|
|
@@ -51,31 +57,9 @@ variable — the thrown error names both when this fails.
|
|
|
51
57
|
|
|
52
58
|
## Read the disk first
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
$ node $P directory --image demo.d64
|
|
59
|
-
PRG "DEMO GAME" first=5/0 blocks=5
|
|
60
|
-
|
|
61
|
-
$ node $P bam --image demo.d64
|
|
62
|
-
disk name: "DEMO DISK" id: 38 dos type: 2A
|
|
63
|
-
first dir sector: 18/1
|
|
64
|
-
occupied track ranges: 5
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Do not eyeball the directory for fakery — `--json` decides it. Every entry carries
|
|
68
|
-
`suspicious` plus `suspicious_reasons`, set when the block count is 0, when the
|
|
69
|
-
first track/sector falls outside the image, or when it points into a track the BAM
|
|
70
|
-
reports as entirely free. That last case is the signature of an entry claiming a
|
|
71
|
-
file never written to disk.
|
|
72
|
-
|
|
73
|
-
`scripts/d64-parse.test.mjs` proves the detector both **fires** on a synthetic
|
|
74
|
-
faked entry and stays silent on a well-formed one — a guard proven only silent is
|
|
75
|
-
not a guard. It also sweeps whatever real `.d64` corpus the project ships,
|
|
76
|
-
skipping when there is none. A non-null `chain_error` is the separate failure: a
|
|
77
|
-
directory chain that leaves the image or loops, reported instead of hanging.
|
|
78
|
-
**Confidence: HIGH** (synthetic fire-and-silence tests, plus a corpus sweep).
|
|
60
|
+
Disk-image structure — the directory, the block allocation map, a named file's
|
|
61
|
+
sector chain or raw bytes, and directory-fakery detection — is
|
|
62
|
+
`c64-disk-access`'s job now. Read the disk with it before booting anything.
|
|
79
63
|
|
|
80
64
|
## Boot a disk
|
|
81
65
|
|
|
@@ -128,6 +112,17 @@ Read state before you resume, and resume exactly once at the end.
|
|
|
128
112
|
Hold keys down across a gate by releasing them at the trigger checkpoint in
|
|
129
113
|
step 3, never earlier.
|
|
130
114
|
|
|
115
|
+
**Fill the record's reproducibility key in the same step.**
|
|
116
|
+
`templates/capture-record.template.md`'s Identity table carries three rows that
|
|
117
|
+
are one key, not three facts: `binary sha256`, `argv digest` and `seed`. The
|
|
118
|
+
seed alone is **not** the key — measured, the same seed with a reordered argv
|
|
119
|
+
yielded a 76-byte-different image, so two captures whose argv digests differ are
|
|
120
|
+
different keys and must not be compared as a pair. A record with any of those
|
|
121
|
+
three blank is not a reproducible capture and the void protocol applies. The
|
|
122
|
+
table also carries a `capture route` row (`memory-read` or `snapshot`), and
|
|
123
|
+
**on the snapshot route the `$D000-$DFFF` volatility rule below does not
|
|
124
|
+
apply** — a difference there is a real difference.
|
|
125
|
+
|
|
131
126
|
`assemble` runs the same assertions and writes nothing, so it is the cheap check
|
|
132
127
|
on a set of chunks before committing them.
|
|
133
128
|
|
|
@@ -164,12 +159,12 @@ only after the provenance diff partitions loader from cracktro from game — see
|
|
|
164
159
|
|
|
165
160
|
## Find an entry point
|
|
166
161
|
|
|
167
|
-
1. Press past any "hit any key" gate
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
`
|
|
171
|
-
|
|
172
|
-
|
|
162
|
+
1. Press past any "hit any key" gate. **`vice_keyboard_matrix` is permanently unavailable** — the
|
|
163
|
+
binary monitor's `KEYBOARD_FEED` only injects PETSCII text into the KERNAL buffer and cannot
|
|
164
|
+
drive the raw matrix; see `docs/stock-hard-losses.md`. Use `vice_keyboard_type` /
|
|
165
|
+
`vice_keyboard_petscii` when the gate reads the KERNAL buffer, or `vice_joystick_set` when it
|
|
166
|
+
polls the matrix directly; buffer injection stays invisible to a program polling
|
|
167
|
+
`$DC00`/`$DC01` itself.
|
|
173
168
|
2. Step forward in batches with `mcp__plugin_c64-re-tools_vice__vice_execution_step`, reading
|
|
174
169
|
`mcp__plugin_c64-re-tools_vice__vice_registers_get` after each batch.
|
|
175
170
|
3. Stop when the program counter and the stack pointer both settle into a
|
|
@@ -256,8 +251,8 @@ were guaranteed. Region first, bit-count second.
|
|
|
256
251
|
`$E000-$FFFF` (RAM under KERNAL ROM when HIRAM = 0) is deliberately **not**
|
|
257
252
|
excluded. `$FAD8` and `$FC51` do differ across captures, but only two addresses
|
|
258
253
|
out of 8192 — too few for power-on garbage, and unexplained. They still fail, and
|
|
259
|
-
what writes them is an open question.
|
|
260
|
-
|
|
254
|
+
what writes them is an open question. Observed 2026-08-04; graded MEDIUM,
|
|
255
|
+
structural, not reproduced against a second release.
|
|
261
256
|
|
|
262
257
|
**Establish a drift floor** with `floor` across every capture of one checkpoint.
|
|
263
258
|
It reports each address that differed in any pairing, with the distinct values
|
|
@@ -271,6 +266,76 @@ Capture the power-on image as the very first action against a fresh machine, the
|
|
|
271
266
|
idle-capture twice more and run `floor` over the set. State the result as a
|
|
272
267
|
floor, not a complete set — more captures can only widen it.
|
|
273
268
|
|
|
269
|
+
## Derive a per-release transient allow-list
|
|
270
|
+
|
|
271
|
+
`scripts/derive-transients.mjs` is the named, repeatable derivation. **The
|
|
272
|
+
method is what carries forward between releases; no address set ever does.** Two
|
|
273
|
+
verbs:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
node $T derive --release <id> --out $TD/<id>.json run1.bin run2.bin run3.bin
|
|
277
|
+
node $T check --allow-list $TD/<id>.json runA.bin runB.bin
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`derive` takes **N ≥ 3** runs of the same release under the same protocol at the
|
|
281
|
+
same stop and writes the **union of addresses differing across every pairwise
|
|
282
|
+
comparison** — one entry per address, carrying which pairings it differed in,
|
|
283
|
+
the distinct bytes seen, and an empty attribution line for you to fill. Fewer
|
|
284
|
+
than three images is refused naming the count and the minimum; an image that is
|
|
285
|
+
not exactly 65536 bytes is refused naming the path and the length. It prints
|
|
286
|
+
`TRANSIENT_COUNT: <n>` at column 0, so a measurement gets transcribed rather
|
|
287
|
+
than paraphrased.
|
|
288
|
+
|
|
289
|
+
**Over the cap of 64 addresses the derivation is VOID:** non-zero exit, **no
|
|
290
|
+
artifact written**, and the message says what the overflow means — the stop is
|
|
291
|
+
not frame-exact. That is a fact to record, not a threshold to raise. `--cap`
|
|
292
|
+
only ever *narrows*; a value above 64 is refused by name, and the union is never
|
|
293
|
+
truncated to fit, because a truncated list makes every later comparison pass on
|
|
294
|
+
bytes nobody vetted. Overflow is a **measured** outcome: 0 differing addresses
|
|
295
|
+
at a frame-exact `READY` stop, 66 at a frame-anchored autostarted stop at
|
|
296
|
+
jitter 4000 ms, 300 at a wall-clock autostarted stop on a real release, 1242 at
|
|
297
|
+
a wall-clock `READY` stop with the determinism block applied.
|
|
298
|
+
|
|
299
|
+
Re-deriving over an existing artifact is **refused without `--force`**, with the
|
|
300
|
+
no-inheritance rule in the message: an inherited list cannot be distinguished
|
|
301
|
+
afterwards from an honestly derived one.
|
|
302
|
+
|
|
303
|
+
`check` re-checks one pair against an already-committed derivation without
|
|
304
|
+
re-deriving it, printing `CHECK_VERDICT: equivalent | not-equivalent` and exiting
|
|
305
|
+
1 when not equivalent. Note what it does **not** carry: no address range is a
|
|
306
|
+
volatile span, and there is no bit-count tolerance at any address — a one-bit
|
|
307
|
+
difference outside the list fails. Those two rules belong to `compare.mjs` and
|
|
308
|
+
are deliberately not inherited. `src/skills/c64-ram-capture/transients/README.md`
|
|
309
|
+
holds the artifact shape, the committed method and the cap's reasoning.
|
|
310
|
+
|
|
311
|
+
## Slice the image out of a snapshot instead of transcribing it
|
|
312
|
+
|
|
313
|
+
`scripts/vsf-slice.mjs` produces the flat 64K image by slicing a VICE `.vsf`
|
|
314
|
+
snapshot's memory module body. Two verbs:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
node $V slice run1.vsf --out run1.bin # writes exactly 65536 bytes
|
|
318
|
+
node $V digest run1.vsf # sha256 + size, writing no file
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Add `--json` to either for the same summary as one JSON object — it carries the
|
|
322
|
+
snapshot's memory-module minor, the observed body length, and the three CPU port
|
|
323
|
+
read-back values.
|
|
324
|
+
|
|
325
|
+
**The one thing that matters operationally: this route has no transcription
|
|
326
|
+
step, so the `$D000-$DFFF` volatility rule above does *not* apply to an image
|
|
327
|
+
produced this way.** That rule exists because `vice_memory_read` samples live
|
|
328
|
+
I/O. The snapshot array is RAM *under* I/O, not the register read view, so those
|
|
329
|
+
4096 addresses are ordinary RAM in a sliced image and a difference there is a
|
|
330
|
+
real difference. Do not carry the exclusion across from the memory-read route.
|
|
331
|
+
|
|
332
|
+
A malformed snapshot is **refused**, by name, naming the offending value and the
|
|
333
|
+
valid range — it is never truncated into a plausible short image. The `.vsf`
|
|
334
|
+
byte layout lives in exactly one place, `vsf-slice.ts` on the MCP side; this
|
|
335
|
+
script resolves it via `VICE_MCP_DIR`, the in-repo path, or the
|
|
336
|
+
`@henols/vice-mcp` package, and refuses naming every path it tried when no rung
|
|
337
|
+
resolves rather than falling back to a second copy of the layout.
|
|
338
|
+
|
|
274
339
|
## Feeding the memory map's provenance sidecar
|
|
275
340
|
|
|
276
341
|
`c64-program-recon`'s generated memory map (`vice-mcp anno render-memmap`) takes a small provenance
|
|
@@ -288,6 +353,7 @@ This one owns the image and its identity. It does not restate what the others ca
|
|
|
288
353
|
|
|
289
354
|
| Need | Go to |
|
|
290
355
|
|---|---|
|
|
356
|
+
| A disk image's directory, BAM, sector chains, or directory-fakery detection | `c64-disk-access` |
|
|
291
357
|
| Which address to read next, and what the answer rules out | `c64-program-recon` |
|
|
292
358
|
| Every way a live read gives a wrong answer | `c64-program-recon` — `references/observation-hazards.md`. **Read before driving.** |
|
|
293
359
|
| What a specific address or bit means | `c64-memory-mapping` — `node … lookup '$D018'` |
|
|
@@ -326,15 +392,17 @@ split: the workflow fits in one file, which is the right call when it does.
|
|
|
326
392
|
| Path | Covers |
|
|
327
393
|
|---|---|
|
|
328
394
|
| `scripts/compare.mjs` | Difference classification and the drift floor. Pure logic over captures you already have — `node $C` with no arguments prints the rules. |
|
|
329
|
-
| `
|
|
330
|
-
| `scripts/
|
|
395
|
+
| `scripts/vsf-slice.mjs` | `slice` / `digest` — the flat 64K image sliced out of a `.vsf` snapshot with no transcription step. The layout lives in `vsf-slice.ts` on the MCP side; this wrapper resolves it and refuses by name when it cannot. Covered by `scripts/vsf-slice.test.mjs`. |
|
|
396
|
+
| `scripts/derive-transients.mjs` | `derive` / `check` — the per-release transient allow-list, derived from N ≥ 3 runs as the pairwise union, under a committed cap of 64 that **voids** rather than warns. Covered by `scripts/derive-transients.test.mjs`. |
|
|
397
|
+
| `transients/README.md` | The committed derivation method, the artifact shape, the cap's reasoning with its four measured reference points, and the rule that no address set is inherited between releases. Its `.gitignore` refuses every image byte form. |
|
|
398
|
+
| `templates/capture-record.template.md` | The per-capture record: identity including the three-row reproducibility key and the `capture route` row, machine state read in the same paused window, the void checklist, and the per-pairing comparison table. |
|
|
331
399
|
| `scripts/dump-artifacts.mjs` | `assemble` / `chip-state` / `manifest` / `write-set` — the guarded byte work, and the source of every `assembleImage:` message in the table below. |
|
|
332
400
|
| `RELEASES.json.example` | A copyable release-registry shape — see `## Release registry shape` above. |
|
|
333
401
|
|
|
334
|
-
|
|
335
|
-
find them**, graded with `Evidence:` and `Confidence:`. Promote
|
|
336
|
-
the new evidence, never by editing
|
|
337
|
-
|
|
402
|
+
Record findings that make RE faster in your own project notes **at the moment you
|
|
403
|
+
find them**, graded with `Evidence:` and `Confidence:`. Promote a finding by
|
|
404
|
+
re-logging it with the new evidence, never by editing an old grade in place — the
|
|
405
|
+
grade is only worth anything if it says what was actually known when it was written.
|
|
338
406
|
|
|
339
407
|
## Troubleshooting
|
|
340
408
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
//
|
|
18
18
|
// $D000-$DFFF is this module's one departure from what SKILL.md said when it was
|
|
19
19
|
// written: that range is I/O, not RAM, so it can never be stable. See the VOLATILE
|
|
20
|
-
// table below
|
|
20
|
+
// table below for the evidence (observed 2026-08-04).
|
|
21
21
|
|
|
22
22
|
import { readFileSync } from "node:fs";
|
|
23
23
|
import { createHash } from "node:crypto";
|
|
@@ -36,7 +36,7 @@ const VOLATILE = [
|
|
|
36
36
|
// live hardware and two captures can never agree here. Added 2026-08-04 after
|
|
37
37
|
// every divergence across all six committed gameentry pairings landed either
|
|
38
38
|
// here ($D344, $D625, $D628) or in RAM under KERNAL ROM -- see
|
|
39
|
-
//
|
|
39
|
+
// Observed 2026-08-04. Confidence HIGH: structural.
|
|
40
40
|
[0xd000, 0xdfff],
|
|
41
41
|
];
|
|
42
42
|
|