@henols/c64-re-tools 1.1.0 → 2.0.0-rc.10
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/LICENSE +21 -0
- package/README.md +154 -37
- package/distribution/package.ts +21 -0
- package/distribution/plugin.ts +122 -0
- package/package.json +34 -40
- package/skills/c64-assembler/SKILL.md +76 -0
- package/skills/c64-assembler/scripts/assemble.ts +110 -0
- package/skills/c64-assembler/scripts/compare.ts +84 -0
- package/skills/c64-basic/SKILL.md +66 -0
- package/skills/c64-basic/scripts/basic.ts +46 -0
- package/skills/c64-disk/SKILL.md +93 -0
- package/skills/c64-disk/scripts/containers.ts +124 -0
- package/skills/c64-disk/scripts/disk.ts +120 -0
- package/skills/c64-emulator/SKILL.md +134 -0
- package/skills/c64-knowledge/SKILL.md +166 -0
- package/skills/c64-knowledge/scripts/knowledge.ts +492 -0
- package/skills/c64-memory-map/SKILL.md +79 -0
- package/skills/c64-memory-map/scripts/memmap.ts +92 -0
- package/skills/c64-provenance/SKILL.md +110 -0
- package/skills/c64-provenance/scripts/compare.ts +152 -0
- package/skills/c64-provenance/scripts/provenance.ts +334 -0
- package/skills/c64-provenance/scripts/runtime.ts +191 -0
- package/skills/c64-provenance/scripts/separate.ts +140 -0
- package/skills/c64-reverse-engineering/SKILL.md +144 -0
- package/skills/c64-source/SKILL.md +126 -0
- package/skills/c64-source/scripts/generate.ts +490 -0
- package/skills/c64-source/scripts/loader.ts +571 -0
- package/skills/c64-source/scripts/opcodes.ts +61 -0
- package/skills/c64-source/scripts/source.ts +595 -0
- package/skills/c64-static-analysis/SKILL.md +119 -0
- package/skills/c64-static-analysis/scripts/analyze.ts +275 -0
- package/skills/c64-static-analysis/scripts/coverage.ts +78 -0
- package/skills/c64-static-analysis/scripts/findings.ts +75 -0
- package/skills/c64-static-analysis/scripts/idioms.ts +149 -0
- package/skills/c64-static-analysis/scripts/instructions.ts +105 -0
- package/skills/c64-testing/SKILL.md +103 -0
- package/skills/c64-testing/references/scenario-format.md +116 -0
- package/skills/c64-testing/scripts/checklist.ts +51 -0
- package/skills/c64-testing/scripts/run.ts +189 -0
- package/skills/c64-testing/scripts/scenario.ts +493 -0
- package/skills/c64-testing/scripts/test.ts +65 -0
- package/skills/c64-unpacker/SKILL.md +135 -0
- package/skills/c64-unpacker/scripts/evidence.ts +207 -0
- package/skills/c64-unpacker/scripts/loads.ts +209 -0
- package/skills/c64-unpacker/scripts/machine.ts +80 -0
- package/skills/c64-unpacker/scripts/stages.ts +130 -0
- package/skills/c64-unpacker/scripts/unpack.ts +683 -0
- package/src/c64.ts +411 -0
- package/src/cli/ap-sdk.ts +44 -0
- package/src/cli/cleanup.ts +194 -0
- package/src/cli/main.ts +125 -0
- package/src/cli/status.ts +61 -0
- package/src/host/main.ts +134 -0
- package/src/host/server.ts +392 -0
- package/src/host/tools/c1541.ts +284 -0
- package/src/host/tools/index.ts +35 -0
- package/src/host/tools/petcat.ts +77 -0
- package/src/host/tools/status.ts +47 -0
- package/src/host/tools/version.ts +41 -0
- package/src/host/vice/adapter.ts +804 -0
- package/src/host/vice/binary-monitor.ts +389 -0
- package/src/host/vice/chips.ts +118 -0
- package/src/host/vice/connect.ts +34 -0
- package/src/host/vice/process.ts +413 -0
- package/src/host/vice/screen.ts +184 -0
- package/src/host/vice/session.ts +1426 -0
- package/src/host/vice/text-monitor.ts +223 -0
- package/src/host-client/autostart.ts +55 -0
- package/src/host-client/connect.ts +241 -0
- package/src/host-client/coverage.ts +63 -0
- package/src/host-client/steps.ts +170 -0
- package/src/host-client/tools.ts +89 -0
- package/src/host-client/transfer.ts +126 -0
- package/src/host-client/vice-session.ts +316 -0
- package/src/knowledge/database.ts +132 -0
- package/src/knowledge/history.ts +163 -0
- package/src/knowledge/import.ts +391 -0
- package/src/knowledge/read.ts +342 -0
- package/src/knowledge/schema.ts +229 -0
- package/src/knowledge/write.ts +663 -0
- package/src/mcp/main.ts +134 -0
- package/src/mcp/output.ts +32 -0
- package/src/mcp/server.ts +316 -0
- package/src/mcp/tools/debug.ts +327 -0
- package/src/mcp/tools/execution.ts +117 -0
- package/src/mcp/tools/input.ts +72 -0
- package/src/mcp/tools/machine.ts +100 -0
- package/src/mcp/tools/media.ts +129 -0
- package/src/mcp/tools/memory.ts +250 -0
- package/src/mcp/tools/video.ts +271 -0
- package/src/native/acme.ts +116 -0
- package/src/native/discover.ts +179 -0
- package/src/native/dxa.ts +166 -0
- package/src/native/ghidra/analyze.ts +86 -0
- package/src/native/ghidra/index.ts +173 -0
- package/src/native/ghidra/language/c64rt_6510.cspec +34 -0
- package/src/native/ghidra/language/c64rt_6510.ldefs +15 -0
- package/src/native/ghidra/language/c64rt_6510.pspec +15 -0
- package/src/native/ghidra/language/c64rt_6510.slaspec +596 -0
- package/src/native/ghidra/scripts/C64Export.java +274 -0
- package/src/native/ghidra/scripts/C64Prepare.java +60 -0
- package/src/native/local.ts +34 -0
- package/src/native/processes.ts +350 -0
- package/src/native/run.ts +172 -0
- package/src/native/staging.ts +83 -0
- package/src/native/status.ts +113 -0
- package/src/native/types.ts +257 -0
- package/src/native/watchdog.ts +78 -0
- package/src/package.ts +21 -0
- package/src/platform.ts +441 -0
- package/src/project.ts +39 -0
- package/src/protocol/framing.ts +132 -0
- package/src/protocol/messages.ts +259 -0
- package/src/protocol/tools.ts +266 -0
- package/src/protocol/vice.ts +1348 -0
- package/src/script.ts +60 -0
- package/src/time.ts +15 -0
- package/src/trace.ts +194 -0
- package/THIRD-PARTY-NOTICES.md +0 -26
- package/bin/cli.mjs +0 -283
- package/skills/acme-build/SKILL.md +0 -260
- package/skills/acme-build/scripts/acme.mjs +0 -341
- package/skills/acme-build/template.a +0 -53
- package/skills/c64-disk-access/SKILL.md +0 -159
- package/skills/c64-disk-access/scripts/c1541.mjs +0 -569
- package/skills/c64-memory-mapping/SKILL.md +0 -631
- package/skills/c64-memory-mapping/memmap.json +0 -8800
- package/skills/c64-memory-mapping/scripts/driver.mjs +0 -553
- package/skills/c64-petcat/SKILL.md +0 -87
- package/skills/c64-petcat/scripts/petcat.mjs +0 -221
- package/skills/c64-program-recon/SKILL.md +0 -706
- package/skills/c64-program-recon/references/control-flow.md +0 -180
- package/skills/c64-program-recon/references/graphics.md +0 -79
- package/skills/c64-program-recon/references/observation-hazards.md +0 -143
- package/skills/c64-program-recon/references/reconstruction.md +0 -133
- package/skills/c64-program-recon/references/sound-and-input.md +0 -74
- package/skills/c64-program-recon/references/tool-selection.md +0 -78
- package/skills/c64-program-recon/scripts/derive.mjs +0 -364
- package/skills/c64-program-recon/scripts/packer-finding.mjs +0 -709
- package/skills/c64-program-recon/templates/memory-map.template.md +0 -101
- package/skills/c64-provenance-diff/SKILL.md +0 -292
- package/skills/c64-provenance-diff/scripts/diff-images.mjs +0 -984
- package/skills/c64-provenance-diff/scripts/recovery-schema.mjs +0 -395
- package/skills/c64-ram-capture/RELEASES.json.example +0 -17
- package/skills/c64-ram-capture/SKILL.md +0 -474
- package/skills/c64-ram-capture/scripts/compare-cross-binary.mjs +0 -525
- package/skills/c64-ram-capture/scripts/compare.mjs +0 -258
- package/skills/c64-ram-capture/scripts/derive-transients.mjs +0 -575
- package/skills/c64-ram-capture/scripts/dump-artifacts.mjs +0 -317
- package/skills/c64-ram-capture/scripts/mcp-module.mjs +0 -174
- package/skills/c64-ram-capture/scripts/project-paths.mjs +0 -81
- package/skills/c64-ram-capture/scripts/releases.mjs +0 -109
- package/skills/c64-ram-capture/scripts/vsf-slice.mjs +0 -147
- package/skills/c64-ram-capture/scripts/watch-loads.mjs +0 -581
- package/skills/c64-ram-capture/templates/capture-record.template.md +0 -99
- package/skills/c64-ram-capture/transients/README.md +0 -137
- package/skills/routine-queue-walker/SKILL.md +0 -394
- package/skills/routine-queue-walker/scripts/completeness-report.mjs +0 -465
- package/skills/vice-wedge-triage/SKILL.md +0 -269
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// The c64-testing run script (D45): runs steps in its own emulator, from a
|
|
2
|
+
// program or a snapshot file, and writes what the steps ask for into a
|
|
3
|
+
// directory: screens, coverage maps, snapshot files, and results.json with
|
|
4
|
+
// the observations. One side, no comparison. Exit status: 0 when every step
|
|
5
|
+
// ran, 1 when a step did not, 2 for a bad call.
|
|
6
|
+
|
|
7
|
+
import { parseArgs } from "node:util";
|
|
8
|
+
|
|
9
|
+
import { formatC64Address, parseC64Address } from "#src/c64.ts";
|
|
10
|
+
import { runProgram } from "#src/host-client/autostart.ts";
|
|
11
|
+
import { readCoverage } from "#src/host-client/coverage.ts";
|
|
12
|
+
import { ACTION_STEP_KEYS, ACTION_STEP_KINDS, checkActionStep, isInteger, isObject, runActionStep, type ActionStep } from "#src/host-client/steps.ts";
|
|
13
|
+
import { MAX_MEMORY_READ, WireFailure } from "#src/host-client/tools.ts";
|
|
14
|
+
import { readProjectFile, writeProjectFile } from "#src/host-client/transfer.ts";
|
|
15
|
+
import { ViceSessionClient } from "#src/host-client/vice-session.ts";
|
|
16
|
+
import { resolveProjectPath } from "#src/project.ts";
|
|
17
|
+
import { runScript, UsageError } from "#src/script.ts";
|
|
18
|
+
import { loadSymbols } from "./scenario.ts";
|
|
19
|
+
|
|
20
|
+
const USAGE = `run.ts <run.json> --out <dir>
|
|
21
|
+
|
|
22
|
+
<run.json> is a run file relative to the project directory: where to start,
|
|
23
|
+
optional symbols, and the steps. See references/scenario-format.md, section
|
|
24
|
+
Runs. The files that the steps write, and results.json, go into <dir>. The
|
|
25
|
+
result is one JSON object.`;
|
|
26
|
+
|
|
27
|
+
type RunStep =
|
|
28
|
+
| ActionStep
|
|
29
|
+
| { snapshot: string }
|
|
30
|
+
| { save: string }
|
|
31
|
+
| { screen: string }
|
|
32
|
+
| { coverage: string }
|
|
33
|
+
| { observe: string; memory?: Array<string | { at: string; size?: number }>; registers?: boolean; vicii?: boolean; sprites?: number[] };
|
|
34
|
+
|
|
35
|
+
interface Run {
|
|
36
|
+
name: string;
|
|
37
|
+
videoStandard?: "pal" | "ntsc";
|
|
38
|
+
start: { program: string; load?: boolean } | { snapshot: string };
|
|
39
|
+
symbols?: string;
|
|
40
|
+
steps: RunStep[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const OWN_KINDS = ["snapshot", "save", "screen", "coverage", "observe"] as const;
|
|
44
|
+
const OBSERVE_KEYS = ["observe", "memory", "registers", "vicii", "sprites"];
|
|
45
|
+
/** A name of a file that a step writes: letters, digits, dots, underscores or hyphens. */
|
|
46
|
+
const NAME = /^[A-Za-z0-9._-]{1,64}$/;
|
|
47
|
+
|
|
48
|
+
function readRun(path: string): Run {
|
|
49
|
+
let value: unknown;
|
|
50
|
+
try {
|
|
51
|
+
value = JSON.parse(readProjectFile(path).bytes.toString("utf8"));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof WireFailure) throw error;
|
|
54
|
+
throw new UsageError(`the run ${path} is not JSON: ${(error as Error).message}`);
|
|
55
|
+
}
|
|
56
|
+
const fail = (message: string): never => {
|
|
57
|
+
throw new UsageError(`run ${path}: ${message}`);
|
|
58
|
+
};
|
|
59
|
+
if (!isObject(value)) return fail("is not a JSON object");
|
|
60
|
+
if (typeof value.name !== "string" || value.name === "") fail("needs a name");
|
|
61
|
+
if (value.videoStandard !== undefined && value.videoStandard !== "pal" && value.videoStandard !== "ntsc") fail("videoStandard must be pal or ntsc");
|
|
62
|
+
const start = value.start;
|
|
63
|
+
if (!isObject(start) || (typeof start.program !== "string") === (typeof start.snapshot !== "string")) fail("start needs program or snapshot");
|
|
64
|
+
if (isObject(start) && start.load !== undefined && typeof start.load !== "boolean") fail("start.load must be true or false");
|
|
65
|
+
if (value.symbols !== undefined && typeof value.symbols !== "string") fail("symbols must be knowledge, none or a JSON file");
|
|
66
|
+
if (!Array.isArray(value.steps) || value.steps.length === 0) fail("needs steps");
|
|
67
|
+
const labels = new Set<string>();
|
|
68
|
+
(value.steps as unknown[]).forEach((step, index) => {
|
|
69
|
+
const at = `step ${index + 1}`;
|
|
70
|
+
if (!isObject(step)) return fail(`${at} must be an object`);
|
|
71
|
+
const kinds = "observe" in step ? ["observe"] : Object.keys(step).filter((key) => (ACTION_STEP_KINDS as readonly string[]).includes(key) || (OWN_KINDS as readonly string[]).includes(key));
|
|
72
|
+
if (kinds.length !== 1) return fail(`${at} must have exactly one of ${[...ACTION_STEP_KINDS, ...OWN_KINDS].join(", ")}`);
|
|
73
|
+
const kind = kinds[0]!;
|
|
74
|
+
const allowed = kind === "observe" ? OBSERVE_KEYS : (ACTION_STEP_KEYS[kind] ?? [kind]);
|
|
75
|
+
for (const key of Object.keys(step)) if (!allowed.includes(key)) fail(`${at} has the key ${key}, which a ${kind} step does not have`);
|
|
76
|
+
if ((ACTION_STEP_KINDS as readonly string[]).includes(kind)) return checkActionStep(kind, step, at, fail);
|
|
77
|
+
const name = step[kind];
|
|
78
|
+
if (kind === "snapshot") {
|
|
79
|
+
if (typeof name !== "string" || name === "") fail(`${at}: snapshot needs a project file`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (typeof name !== "string" || !NAME.test(name)) fail(`${at}: ${kind} needs a name of 1 to 64 letters, digits, dots, underscores or hyphens`);
|
|
83
|
+
if (kind !== "observe") return;
|
|
84
|
+
if (labels.has(name as string)) fail(`${at} needs a new observation name`);
|
|
85
|
+
labels.add(name as string);
|
|
86
|
+
if (step.memory !== undefined && (!Array.isArray(step.memory) || step.memory.some((item) => typeof item !== "string" && !(isObject(item) && typeof item.at === "string" && (item.size === undefined || isInteger(item.size, 1, MAX_MEMORY_READ)))))) {
|
|
87
|
+
fail(`${at}: memory is a list of names, $ addresses, or objects with at and size`);
|
|
88
|
+
}
|
|
89
|
+
if (step.registers !== undefined && typeof step.registers !== "boolean") fail(`${at}: registers must be true or false`);
|
|
90
|
+
if (step.vicii !== undefined && typeof step.vicii !== "boolean") fail(`${at}: vicii must be true or false`);
|
|
91
|
+
if (step.sprites !== undefined && (!Array.isArray(step.sprites) || !step.sprites.every((sprite) => isInteger(sprite, 0, 7)))) fail(`${at}: sprites is a list of numbers from 0 to 7`);
|
|
92
|
+
});
|
|
93
|
+
return value as unknown as Run;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function runFile(path: string, out: string): Promise<{ output: Record<string, unknown>; failed: boolean }> {
|
|
97
|
+
const run = readRun(path);
|
|
98
|
+
const symbols = loadSymbols({ program: "", symbols: run.symbols ?? "none" });
|
|
99
|
+
const at = (location: string): number => {
|
|
100
|
+
if (location.startsWith("$")) return parseC64Address(location);
|
|
101
|
+
const address = symbols.get(location);
|
|
102
|
+
if (address === undefined) throw new UsageError(`the symbols have no ${location}`);
|
|
103
|
+
return address;
|
|
104
|
+
};
|
|
105
|
+
const files: string[] = [];
|
|
106
|
+
const write = (name: string, bytes: Uint8Array) => {
|
|
107
|
+
writeProjectFile(`${out}/${name}`, bytes, { what: "--out" });
|
|
108
|
+
files.push(name);
|
|
109
|
+
};
|
|
110
|
+
const observations: Record<string, unknown> = {};
|
|
111
|
+
const client = await ViceSessionClient.open({ videoStandard: run.videoStandard ?? "pal" });
|
|
112
|
+
let done = 0;
|
|
113
|
+
let reason: string | undefined;
|
|
114
|
+
try {
|
|
115
|
+
await client.reset({ mode: "hard", run: false });
|
|
116
|
+
if ("snapshot" in run.start) {
|
|
117
|
+
await client.snapshotRestore(readProjectFile(run.start.snapshot).bytes);
|
|
118
|
+
} else if (run.start.load === true) {
|
|
119
|
+
await client.execution({ action: "advance-frames", count: 150, space: "c64" });
|
|
120
|
+
await client.programLoad({ path: run.start.program });
|
|
121
|
+
} else {
|
|
122
|
+
const failed = await runProgram(client, run.start.program, readProjectFile(run.start.program).bytes);
|
|
123
|
+
if (failed !== undefined) reason = failed === "not-loaded" ? "The program was not in memory after autostart." : "The program did not start after RUN; it needs a BASIC start line.";
|
|
124
|
+
}
|
|
125
|
+
await client.memmap({ action: "clear" });
|
|
126
|
+
for (const step of reason === undefined ? run.steps : []) {
|
|
127
|
+
if ("snapshot" in step) {
|
|
128
|
+
await client.snapshotRestore(readProjectFile(step.snapshot).bytes);
|
|
129
|
+
} else if ("save" in step) {
|
|
130
|
+
await client.snapshot({ action: "save", name: "run-save" });
|
|
131
|
+
write(`${step.save}.vsf`, await client.snapshotExport("run-save"));
|
|
132
|
+
await client.snapshot({ action: "discard", name: "run-save" });
|
|
133
|
+
} else if ("screen" in step) {
|
|
134
|
+
write(`${step.screen}.png`, Buffer.from((await client.screenCapture()).png, "base64"));
|
|
135
|
+
} else if ("coverage" in step) {
|
|
136
|
+
write(`${step.coverage}.bin`, await readCoverage(client));
|
|
137
|
+
await client.memmap({ action: "clear" });
|
|
138
|
+
} else if ("observe" in step) {
|
|
139
|
+
const seen: Record<string, unknown> = {};
|
|
140
|
+
if (step.memory !== undefined) {
|
|
141
|
+
const memory: Record<string, string> = {};
|
|
142
|
+
for (const item of step.memory) {
|
|
143
|
+
const spec = typeof item === "string" ? { at: item } : item;
|
|
144
|
+
memory[spec.at] = (await client.memoryRead({ address: at(spec.at), size: spec.size ?? 1, space: "c64", view: "cpu" })).data;
|
|
145
|
+
}
|
|
146
|
+
seen.memory = memory;
|
|
147
|
+
}
|
|
148
|
+
if (step.registers === true) {
|
|
149
|
+
const registers = await client.registersGet("c64");
|
|
150
|
+
seen.registers = { ...registers, pc: formatC64Address(registers.pc) };
|
|
151
|
+
}
|
|
152
|
+
if (step.vicii === true) seen.vicii = await client.vicii();
|
|
153
|
+
if (step.sprites !== undefined) seen.sprites = (await client.sprites(step.sprites)).sprites;
|
|
154
|
+
observations[step.observe] = seen;
|
|
155
|
+
} else {
|
|
156
|
+
const notReached = await runActionStep(client, step, at);
|
|
157
|
+
if (notReached !== undefined && "runUntil" in step) {
|
|
158
|
+
reason = `Step ${done + 1} did not reach its target in ${step.timeoutFrames} frames. It stopped by ${notReached.stopReason} at ${formatC64Address(notReached.pc)}.`;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
done++;
|
|
163
|
+
}
|
|
164
|
+
const pc = (await client.registersGet("c64")).pc;
|
|
165
|
+
const results = { name: run.name, completed: reason === undefined, steps: run.steps.length, done, ...(reason === undefined ? {} : { reason }), pc: formatC64Address(pc), files: [...files], observations };
|
|
166
|
+
write("results.json", Buffer.from(`${JSON.stringify(results, null, 2)}\n`));
|
|
167
|
+
return { output: { ...results, files, observations: Object.keys(observations), out }, failed: reason !== undefined };
|
|
168
|
+
} finally {
|
|
169
|
+
await client.close();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
await runScript(
|
|
174
|
+
async () => {
|
|
175
|
+
const { values, positionals } = parseArgs({ args: process.argv.slice(2), strict: true, allowPositionals: true, options: { out: { type: "string" }, help: { type: "boolean" } } });
|
|
176
|
+
if (values.help) throw new UsageError("");
|
|
177
|
+
if (positionals.length !== 1) throw new UsageError("give one run file");
|
|
178
|
+
if (values.out === undefined) throw new UsageError("run needs --out <dir>");
|
|
179
|
+
try {
|
|
180
|
+
resolveProjectPath(values.out);
|
|
181
|
+
} catch {
|
|
182
|
+
throw new UsageError(`--out must be a path relative to the project directory that stays inside it, not ${values.out}`);
|
|
183
|
+
}
|
|
184
|
+
const { output, failed } = await runFile(positionals[0]!, values.out);
|
|
185
|
+
if (failed) process.exitCode = 1;
|
|
186
|
+
return output;
|
|
187
|
+
},
|
|
188
|
+
{ usage: USAGE },
|
|
189
|
+
);
|