@henols/c64-re-tools 2.0.1 → 2.1.0-rc.2
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/README.md +8 -4
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +24 -9
- package/skills/c64-assembler/scripts/assembler.ts +95 -0
- package/skills/c64-basic/SKILL.md +17 -9
- package/skills/c64-basic/scripts/basic.ts +26 -36
- package/skills/c64-disk/SKILL.md +17 -7
- package/skills/c64-disk/scripts/disk.ts +130 -104
- package/skills/c64-disk/scripts/lib/containers.ts +10 -12
- package/skills/c64-docs/SKILL.md +47 -28
- package/skills/c64-docs/references/style.md +6 -6
- package/skills/c64-docs/scripts/docs.ts +323 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +1 -1
- package/skills/c64-emulator/SKILL.md +11 -7
- package/skills/c64-knowledge/SKILL.md +34 -21
- package/skills/c64-knowledge/scripts/knowledge.ts +597 -718
- package/skills/c64-knowledge/scripts/lib/listing.ts +5 -7
- package/skills/c64-memory-map/SKILL.md +18 -10
- package/skills/c64-memory-map/scripts/memory-map.ts +81 -0
- package/skills/c64-provenance/SKILL.md +82 -26
- package/skills/c64-provenance/scripts/lib/compare.ts +8 -12
- package/skills/c64-provenance/scripts/lib/leftovers.ts +442 -0
- package/skills/c64-provenance/scripts/lib/library.ts +15 -25
- package/skills/c64-provenance/scripts/lib/runtime.ts +12 -37
- package/skills/c64-provenance/scripts/lib/separate.ts +9 -10
- package/skills/c64-provenance/scripts/provenance.ts +222 -252
- package/skills/c64-reverse-engineering/SKILL.md +18 -15
- package/skills/c64-source/SKILL.md +28 -16
- package/skills/c64-source/scripts/lib/loader.ts +52 -77
- package/skills/c64-source/scripts/lib/source.ts +33 -42
- package/skills/c64-source/scripts/{generate.ts → source.ts} +68 -109
- package/skills/c64-static-analysis/SKILL.md +24 -13
- package/skills/c64-static-analysis/scripts/lib/coverage.ts +3 -4
- package/skills/c64-static-analysis/scripts/lib/findings.ts +2 -2
- package/skills/c64-static-analysis/scripts/lib/instructions.ts +1 -1
- package/skills/c64-static-analysis/scripts/lib/macros.ts +6 -8
- package/skills/c64-static-analysis/scripts/{analyze.ts → static-analysis.ts} +58 -118
- package/skills/c64-testing/SKILL.md +94 -19
- package/skills/c64-testing/references/recording-format.md +125 -0
- package/skills/c64-testing/references/scenario-format.md +24 -19
- package/skills/c64-testing/scripts/{diff.ts → lib/diff-files.ts} +18 -69
- package/skills/c64-testing/scripts/lib/diff.ts +5 -3
- package/skills/c64-testing/scripts/lib/play.ts +334 -0
- package/skills/c64-testing/scripts/lib/recording.ts +662 -0
- package/skills/c64-testing/scripts/lib/replay.ts +105 -0
- package/skills/c64-testing/scripts/{run.ts → lib/runs.ts} +36 -59
- package/skills/c64-testing/scripts/lib/scenario.ts +73 -81
- package/skills/c64-testing/scripts/testing.ts +189 -0
- package/skills/c64-unpacker/SKILL.md +42 -25
- package/skills/c64-unpacker/scripts/lib/evidence.ts +3 -4
- package/skills/c64-unpacker/scripts/lib/loads.ts +12 -10
- package/skills/c64-unpacker/scripts/lib/machine.ts +0 -2
- package/skills/c64-unpacker/scripts/lib/stages.ts +14 -19
- package/skills/c64-unpacker/scripts/{unpack.ts → unpacker.ts} +109 -211
- package/src/c64.ts +35 -20
- package/src/cli/main.ts +2 -2
- package/src/errors.ts +38 -0
- package/src/graphics.ts +3 -1
- package/src/host/server.ts +3 -2
- package/src/host/vice/session.ts +4 -4
- package/src/host-client/entry-state.ts +106 -0
- package/src/host-client/image.ts +21 -10
- package/src/host-client/steps.ts +285 -108
- package/src/host-client/transfer.ts +21 -15
- package/src/host-client/vice-session.ts +1 -1
- package/src/knowledge/database.ts +3 -23
- package/src/knowledge/expression.ts +8 -11
- package/src/knowledge/history.ts +2 -2
- package/src/knowledge/import.ts +13 -14
- package/src/knowledge/names.ts +50 -0
- package/src/knowledge/program.ts +11 -10
- package/src/knowledge/read.ts +16 -21
- package/src/knowledge/schema.ts +9 -3
- package/src/knowledge/text.ts +5 -5
- package/src/knowledge/write.ts +93 -137
- package/src/mcp/inputs.ts +105 -0
- package/src/mcp/output.ts +2 -13
- package/src/mcp/server.ts +12 -60
- package/src/mcp/tools/debug.ts +17 -12
- package/src/mcp/tools/execution.ts +5 -32
- package/src/mcp/tools/input.ts +7 -32
- package/src/mcp/tools/media.ts +6 -5
- package/src/mcp/tools/memory.ts +11 -38
- package/src/mcp/tools/video.ts +6 -5
- package/src/native/c64run.ts +162 -0
- package/src/native/discover.ts +8 -0
- package/src/native/dxa.ts +7 -6
- package/src/native/ghidra/analyze.ts +2 -1
- package/src/native/ghidra/index.ts +2 -1
- package/src/native/processes.ts +4 -3
- package/src/native/status.ts +25 -4
- package/src/native/types.ts +3 -7
- package/src/opcodes.ts +13 -13
- package/src/platform.ts +9 -9
- package/src/project.ts +16 -26
- package/src/protocol/messages.ts +3 -21
- package/src/protocol/vice.ts +7 -7
- package/src/script.ts +491 -49
- package/skills/c64-assembler/scripts/assemble.ts +0 -138
- package/skills/c64-docs/scripts/generate.ts +0 -109
- package/skills/c64-docs/scripts/link.ts +0 -239
- package/skills/c64-memory-map/scripts/memmap.ts +0 -92
- package/skills/c64-testing/scripts/checklist.ts +0 -51
- package/skills/c64-testing/scripts/test.ts +0 -65
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# c64-re-tools
|
|
2
2
|
|
|
3
|
+
<img src="assets/branding/c64-re-tools-transparent.svg" alt="C64 RE Tools" width="440">
|
|
4
|
+
|
|
5
|
+
|
|
3
6
|
Let an AI agent work on Commodore 64 programs. The agent drives a real VICE
|
|
4
7
|
emulator and runs ACME, DXA and Ghidra. Use it to build new C64 apps and games,
|
|
5
8
|
and to reverse engineer old ones.
|
|
@@ -51,7 +54,7 @@ npx -y --package=@henols/c64-re-tools@latest c64-re-tools-host
|
|
|
51
54
|
|
|
52
55
|
### Dev container
|
|
53
56
|
|
|
54
|
-
This is the preferred way. The container gets Node.js 24, ACME, dxa, Java 21 and
|
|
57
|
+
This is the preferred way. The container gets Node.js 24, ACME, dxa, Java 21, Ghidra and c64run,
|
|
55
58
|
installs the toolkit into the project when it is created, and updates it at
|
|
56
59
|
each start. Add to `.devcontainer/devcontainer.json`:
|
|
57
60
|
|
|
@@ -67,7 +70,7 @@ needs a Debian or Ubuntu image. `runArgs` is for Docker on Linux.
|
|
|
67
70
|
|
|
68
71
|
### Without a container
|
|
69
72
|
|
|
70
|
-
Install ACME, dxa and
|
|
73
|
+
Install ACME, dxa, Ghidra and c64run as in [docs/install-tools.md](docs/install-tools.md).
|
|
71
74
|
Then, in your project:
|
|
72
75
|
|
|
73
76
|
```
|
|
@@ -119,7 +122,7 @@ program its own folder. A folder with `.c64-re-tools/` is a project.
|
|
|
119
122
|
| c64-unpacker | Packing evidence, and capture of the unpacked program. |
|
|
120
123
|
| c64-static-analysis | Analysis with DXA and Ghidra into the knowledge. |
|
|
121
124
|
| c64-knowledge | Symbols, regions, comments and references, with history. |
|
|
122
|
-
| c64-provenance | Compares releases, and keeps a library of shared routines. |
|
|
125
|
+
| c64-provenance | Compares releases, finds leftovers of the development and of cracks, and keeps a library of shared routines. |
|
|
123
126
|
| c64-source | ACME source from the knowledge, checked byte for byte. |
|
|
124
127
|
| c64-docs | Documentation of a program from its knowledge and source. |
|
|
125
128
|
| c64-reverse-engineering | Leads an investigation and picks the next skill. |
|
|
@@ -129,7 +132,8 @@ program its own folder. A folder with `.c64-re-tools/` is a project.
|
|
|
129
132
|
| Variable | Meaning |
|
|
130
133
|
| --- | --- |
|
|
131
134
|
| `C64RT_VICE`, `C64RT_C1541`, `C64RT_PETCAT` | Full path of the VICE tool, when it is not on `PATH` (host). |
|
|
132
|
-
| `C64RT_ACME`, `C64RT_DXA` | Full path of the tool, when it is not on `PATH`. |
|
|
135
|
+
| `C64RT_ACME`, `C64RT_DXA`, `C64RT_C64RUN` | Full path of the tool, when it is not on `PATH`. |
|
|
136
|
+
| `C64RT_C64_ROMS` | The C64 ROM folder of c64run, when it is not `share/c64-re-tools/roms` next to its `bin`. |
|
|
133
137
|
| `C64RT_GHIDRA` | The Ghidra installation directory, when `analyzeHeadless` is not on `PATH`. |
|
|
134
138
|
| `C64RT_HOST` | `host:port` of the Host Runtime, when it is not on 127.0.0.1, `host.docker.internal` or `host.containers.internal` at port 6464. |
|
|
135
139
|
| `C64RT_VIDEO` | `pal` (default) or `ntsc`. |
|
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@ This skill does not run the program and does not change project knowledge.
|
|
|
23
23
|
The script runs ACME on this machine. It copies the source root to a temporary directory, assembles it there and writes the program into the project. The host runtime is not necessary.
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
node <skill>/scripts/
|
|
26
|
+
node <skill>/scripts/assembler.ts build src main.a --out build/game.prg
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
| Option | Meaning |
|
|
@@ -37,14 +37,26 @@ node <skill>/scripts/assemble.ts --source-root src --entry main.a --out build/ga
|
|
|
37
37
|
|
|
38
38
|
The script copies the full source root. Put all files that the program includes under it. Version control directories stay out.
|
|
39
39
|
|
|
40
|
-
The
|
|
40
|
+
The target CPU is the 6510. The output is a C64 PRG, or the bytes only with `--plain`.
|
|
41
|
+
|
|
42
|
+
### Calling the script
|
|
43
|
+
|
|
44
|
+
- Call a script as `node <skill>/scripts/<script>.ts <command> [subjects] [options]`. The command comes first.
|
|
45
|
+
- `<script>.ts --help` lists the commands. `<script>.ts <command> --help` gives the subjects and the options of one command. The help is JSON: `{"text": "..."}`.
|
|
46
|
+
- An address is `$` and 1 to 4 hex digits, for example `'$c000'` or `'$fb'`, or a name. A name is a symbol, else an analyzer name that only one analyzer gives. Quote an address in a shell.
|
|
47
|
+
- A range is `<start>-<end>`, for example `'$c000-$cfff'`. In a JSON file, a range is `start` and `end`.
|
|
48
|
+
- Bytes in a JSON file are hex in a field `data`, for example `"data": "a900"`.
|
|
49
|
+
- The result is one JSON object on one line. A file that the script writes shows as `{"out", "bytes", "sha256"}`. A list shows as `{"count", "listed"}`, and `rest` tells where to get the rest of a long list.
|
|
50
|
+
- An error is `{"error": {"code": "...", "message": "..."}}`. The message tells what is wrong and what to do.
|
|
51
|
+
- The exit status is 0 for done or PASS, 1 for FAIL, 2 for an error and 3 for INCONCLUSIVE.
|
|
41
52
|
|
|
42
53
|
### Project folder
|
|
43
54
|
|
|
44
55
|
- Paths are relative to the project folder. The project folder is the nearest folder at or above the working directory that holds `.c64-re-tools/`.
|
|
45
56
|
- If the working directory is not in a project and has projects below it, the script refuses and lists them. Then give `--project <folder>` to each script.
|
|
46
57
|
- Write each file that you make for your work in `.c64-re-tools/local/` of the project. Git ignores that folder. Examples are results, batches, step files, runs, snapshots and memory dumps.
|
|
47
|
-
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence
|
|
58
|
+
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence test:<file>` or `--evidence file:<file>` of c64-knowledge. These two refuse a file in `.c64-re-tools/local/`.
|
|
59
|
+
- Do not move a coverage map after `coverage` of c64-knowledge imported it. `--evidence coverage:<map>` names the map by its path at the import. The knowledge keeps the coverage and the SHA-256 of the map, so the evidence stays valid.
|
|
48
60
|
- Put a run that is evidence in `runs/<name>/`, with its run file, its results and the snapshot files that it uses. Change the paths in the run file to the new paths. Then git keeps them, and another person can do the run again.
|
|
49
61
|
- A shell redirect (`>`) is relative to the working directory, not to the project folder. From the folder above the project, write `<folder>/.c64-re-tools/local/result.json`.
|
|
50
62
|
|
|
@@ -52,11 +64,11 @@ The script prints one JSON object. The target CPU is the 6510. The output is a C
|
|
|
52
64
|
|
|
53
65
|
1. Find the source root and the entry source. The entry source sets its own start address with `* = $0801` or similar.
|
|
54
66
|
2. Run the script.
|
|
55
|
-
3. If `
|
|
67
|
+
3. If `result` is `PASS`, the PRG is at `out`. `loadRange` tells where it loads. `symbols` gives each symbol:
|
|
56
68
|
- Kind `address` has a value in `loadRange`. It gives the `address`.
|
|
57
69
|
- Kind `constant` has a value outside `loadRange`. It gives the `value` as `$` and four or more hex digits. A label in a `!pseudopc` block can be a constant, because its value is the run address, not the load address.
|
|
58
70
|
- `used` is `false` when the source does not use the symbol.
|
|
59
|
-
4. If `assembled` is `false`, read `diagnostics`. Each one has a severity, a file, a line and a message. Correct the source and run the script again.
|
|
71
|
+
4. If `result` is `FAIL` and `assembled` is `false`, read `diagnostics`. Each one has a severity, a file, a line and a message. Correct the source and run the script again.
|
|
60
72
|
5. To run the program, give the PRG path to the c64-emulator skill.
|
|
61
73
|
|
|
62
74
|
The source of the c64-source skill has switches. Each switch is 0 when the build does not define it. `--define CRACK=1` puts in the patches of the switch `CRACK`. With `--compare`, only the bytes of those patches are different.
|
|
@@ -74,15 +86,18 @@ A symbol from the rebuilt source can give the address of a routine in the rebuil
|
|
|
74
86
|
|
|
75
87
|
## Result
|
|
76
88
|
|
|
77
|
-
|
|
89
|
+
`result` is `PASS` or `FAIL`.
|
|
78
90
|
|
|
79
|
-
|
|
91
|
+
- `PASS`: the PRG is in the project at `out`, with its load range and symbols.
|
|
92
|
+
- `FAIL` with `assembled: false`: the source has errors. `diagnostics` gives them.
|
|
93
|
+
- `FAIL` with `assembled: true`: `--compare` found differences. The PRG is at `out`.
|
|
80
94
|
|
|
81
|
-
|
|
95
|
+
With `--compare`, `compare` gives `identical` and `segments`: one check for each segment of the program in the knowledge. The `result` of a check is `identical`, `different` with `firstDifference`, or `not covered` when the build does not hold the segment.
|
|
82
96
|
|
|
83
97
|
## Failure and conflicts
|
|
84
98
|
|
|
85
|
-
- `assembled: false`: the source has errors.
|
|
99
|
+
- `FAIL` with `assembled: false`: the source has errors. Correct the source.
|
|
100
|
+
- `FAIL` with `assembled: true`: the build is not identical to the program in the knowledge. Read `compare.segments`.
|
|
86
101
|
- `installation-incomplete`, or `machine-unavailable` with a message that the host runtime does not run: stop the task. Give the message to the user and wait for the user. The message gives the remedy. Do not make a replacement for the tool or the host runtime, for example your own disk parser, disassembler or emulator script.
|
|
87
102
|
- `invalid-input`: an option or a path is wrong. Read the message and correct the call.
|
|
88
103
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// The c64-assembler script: assembles project source with ACME, which it runs
|
|
2
|
+
// itself, and writes the program into the project.
|
|
3
|
+
|
|
4
|
+
import { formatC64Address } from "#src/c64.ts";
|
|
5
|
+
import { Refusal } from "#src/errors.ts";
|
|
6
|
+
import { readProjectTree, writeReported } from "#src/host-client/transfer.ts";
|
|
7
|
+
import { openForRead } from "#src/knowledge/database.ts";
|
|
8
|
+
import { knowledgeNames } from "#src/knowledge/names.ts";
|
|
9
|
+
import { requireProgram, type ProgramImage } from "#src/knowledge/program.ts";
|
|
10
|
+
import { assemble } from "#src/native/acme.ts";
|
|
11
|
+
import { localToolContext } from "#src/native/local.ts";
|
|
12
|
+
import type { AcmeParams } from "#src/native/types.ts";
|
|
13
|
+
import { OPTIONS, runScript, UsageError } from "#src/script.ts";
|
|
14
|
+
import { compareBuild } from "./lib/compare.ts";
|
|
15
|
+
|
|
16
|
+
function parseDefine(text: string): [string, number | boolean] {
|
|
17
|
+
const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.+)$/.exec(text);
|
|
18
|
+
if (match === null) throw new UsageError(`--define needs NAME=VALUE, not ${text}`);
|
|
19
|
+
const value = match[2]!;
|
|
20
|
+
if (value === "true" || value === "false") return [match[1]!, value === "true"];
|
|
21
|
+
if (/^\$[0-9a-fA-F]+$/.test(value)) return [match[1]!, Number.parseInt(value.slice(1), 16)];
|
|
22
|
+
if (/^-?\d+$/.test(value)) return [match[1]!, Number(value)];
|
|
23
|
+
throw new UsageError(`--define ${match[1]} needs an integer or true/false, not ${value}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await runScript({
|
|
27
|
+
name: "assembler",
|
|
28
|
+
summary: "Assembles project source with ACME and writes the program into the project.",
|
|
29
|
+
names: knowledgeNames,
|
|
30
|
+
commands: [
|
|
31
|
+
{
|
|
32
|
+
name: "build",
|
|
33
|
+
summary: "Assembles the source and writes the program. result is PASS when it assembles (and with --compare, when it is the program of the knowledge), else FAIL with the diagnostics or the differences.",
|
|
34
|
+
subjects: [
|
|
35
|
+
{ name: "<source-root>", help: "the folder in the project to assemble from: all of it is sent" },
|
|
36
|
+
{ name: "<entry>", help: "the main source file, relative to the source root" },
|
|
37
|
+
],
|
|
38
|
+
options: {
|
|
39
|
+
out: { ...OPTIONS.out, value: "<file.prg>", help: "where to write the program, relative to the project folder", required: true },
|
|
40
|
+
include: { kind: "text", value: "<dir>", multiple: true, help: "an include folder, relative to the source root" },
|
|
41
|
+
define: { kind: "text", value: "NAME=VALUE", multiple: true, help: "an assembler symbol: an integer ($hex allowed) or true/false" },
|
|
42
|
+
"set-pc": { kind: "address", value: "<address>", help: "the start address when the source sets none, for example '$0801'" },
|
|
43
|
+
compare: { kind: "flag", help: "check the program byte by byte against the program in the knowledge, each segment" },
|
|
44
|
+
space: { ...OPTIONS.space, help: "with --compare: the knowledge space of the program: c64 (the default), drive8, or bank0 to bank255" },
|
|
45
|
+
plain: { kind: "flag", help: "write the bytes only, without the 2-byte load address of a PRG" },
|
|
46
|
+
"all-symbols": { kind: "flag", help: "list also the constants that the source does not use" },
|
|
47
|
+
},
|
|
48
|
+
notes: "Symbol values are $ and at least four hex digits. Put the program into the knowledge with knowledge.ts image of the c64-knowledge skill before --compare.",
|
|
49
|
+
run: async (call) => {
|
|
50
|
+
const [sourceRoot, entry] = [call.subject(0), call.subject(1)];
|
|
51
|
+
const out = call.string("out")!;
|
|
52
|
+
// Read the program before ACME runs, so a project without it stops at once.
|
|
53
|
+
let program: ProgramImage | undefined;
|
|
54
|
+
if (call.flag("compare")) {
|
|
55
|
+
const db = openForRead();
|
|
56
|
+
try {
|
|
57
|
+
program = requireProgram(db, call.space);
|
|
58
|
+
} finally {
|
|
59
|
+
db?.close();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const setPc = call.address("set-pc");
|
|
63
|
+
const tree = readProjectTree(sourceRoot);
|
|
64
|
+
if (!tree.files.some((file) => file.path === entry)) throw new Refusal("not-found", `${entry} is not a file in ${sourceRoot}. Give the main source file relative to the source root.`);
|
|
65
|
+
const params: AcmeParams = { files: tree.files, entrySource: entry, includeDirs: call.strings("include"), defines: Object.fromEntries(call.strings("define").map(parseDefine)), ...(setPc === undefined ? {} : { setPc }) };
|
|
66
|
+
|
|
67
|
+
const { result, attachments } = await assemble(params, tree.contents, localToolContext());
|
|
68
|
+
if (!result.assembled) return { result: "FAIL", assembled: false, diagnostics: result.diagnostics };
|
|
69
|
+
const plain = call.flag("plain");
|
|
70
|
+
const written = writeReported(out, plain ? attachments![0]!.subarray(2) : attachments![0]!, { what: "--out" });
|
|
71
|
+
const range = result.loadRange!;
|
|
72
|
+
const compared = program === undefined ? undefined : compareBuild(attachments![0]!, program);
|
|
73
|
+
// An include file of platform names gives many constants that the program does not use.
|
|
74
|
+
const all = call.flag("all-symbols");
|
|
75
|
+
const listed = result.symbols!.filter((symbol) => all || symbol.kind === "address" || symbol.used);
|
|
76
|
+
return {
|
|
77
|
+
// A build that differs from its reference is a failed check.
|
|
78
|
+
result: compared !== undefined && !compared.identical ? "FAIL" : "PASS",
|
|
79
|
+
assembled: true,
|
|
80
|
+
...written,
|
|
81
|
+
...(plain ? { plain: true } : {}),
|
|
82
|
+
loadRange: { start: formatC64Address(range.start), end: formatC64Address(range.end), bytes: range.bytes },
|
|
83
|
+
symbols: listed.map((symbol) =>
|
|
84
|
+
symbol.kind === "address"
|
|
85
|
+
? { name: symbol.name, kind: "address", address: formatC64Address(symbol.address), used: symbol.used }
|
|
86
|
+
: { name: symbol.name, kind: "constant", value: `$${symbol.value.toString(16).padStart(4, "0")}`, used: symbol.used },
|
|
87
|
+
),
|
|
88
|
+
...(all ? {} : { unusedConstants: result.symbols!.length - listed.length }),
|
|
89
|
+
diagnostics: result.diagnostics,
|
|
90
|
+
...(compared === undefined ? {} : { compare: compared }),
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
@@ -20,23 +20,33 @@ This skill does not run the program and does not change project knowledge.
|
|
|
20
20
|
The script sends the program to the host runtime. The host decodes it with petcat and finds the handoffs.
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
node <skill>/scripts/basic.ts extracted/game.prg
|
|
23
|
+
node <skill>/scripts/basic.ts list extracted/game.prg
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
### Calling the script
|
|
27
|
+
|
|
28
|
+
- Call a script as `node <skill>/scripts/<script>.ts <command> [subjects] [options]`. The command comes first.
|
|
29
|
+
- `<script>.ts --help` lists the commands. `<script>.ts <command> --help` gives the subjects and the options of one command. The help is JSON: `{"text": "..."}`.
|
|
30
|
+
- An address is `$` and 1 to 4 hex digits, for example `'$c000'` or `'$fb'`, or a name. A name is a symbol, else an analyzer name that only one analyzer gives. Quote an address in a shell.
|
|
31
|
+
- A range is `<start>-<end>`, for example `'$c000-$cfff'`. In a JSON file, a range is `start` and `end`.
|
|
32
|
+
- Bytes in a JSON file are hex in a field `data`, for example `"data": "a900"`.
|
|
33
|
+
- The result is one JSON object on one line. A file that the script writes shows as `{"out", "bytes", "sha256"}`. A list shows as `{"count", "listed"}`, and `rest` tells where to get the rest of a long list.
|
|
34
|
+
- An error is `{"error": {"code": "...", "message": "..."}}`. The message tells what is wrong and what to do.
|
|
35
|
+
- The exit status is 0 for done or PASS, 1 for FAIL, 2 for an error and 3 for INCONCLUSIVE.
|
|
27
36
|
|
|
28
37
|
### Project folder
|
|
29
38
|
|
|
30
39
|
- Paths are relative to the project folder. The project folder is the nearest folder at or above the working directory that holds `.c64-re-tools/`.
|
|
31
40
|
- If the working directory is not in a project and has projects below it, the script refuses and lists them. Then give `--project <folder>` to each script.
|
|
32
41
|
- Write each file that you make for your work in `.c64-re-tools/local/` of the project. Git ignores that folder. Examples are results, batches, step files, runs, snapshots and memory dumps.
|
|
33
|
-
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence
|
|
42
|
+
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence test:<file>` or `--evidence file:<file>` of c64-knowledge. These two refuse a file in `.c64-re-tools/local/`.
|
|
43
|
+
- Do not move a coverage map after `coverage` of c64-knowledge imported it. `--evidence coverage:<map>` names the map by its path at the import. The knowledge keeps the coverage and the SHA-256 of the map, so the evidence stays valid.
|
|
34
44
|
- Put a run that is evidence in `runs/<name>/`, with its run file, its results and the snapshot files that it uses. Change the paths in the run file to the new paths. Then git keeps them, and another person can do the run again.
|
|
35
45
|
- A shell redirect (`>`) is relative to the working directory, not to the project folder. From the folder above the project, write `<folder>/.c64-re-tools/local/result.json`.
|
|
36
46
|
|
|
37
47
|
## Workflow
|
|
38
48
|
|
|
39
|
-
1. Run
|
|
49
|
+
1. Run `list` on the program.
|
|
40
50
|
2. Read `listing`. Each line starts with its BASIC line number. Keywords show in lower case. Control characters in strings show in braces, for example `{clr}`.
|
|
41
51
|
3. Read `handoffs`. Each handoff has a `kind` (`sys` or `usr`) and the BASIC `line`.
|
|
42
52
|
- A `sys` handoff with an `address`: BASIC calls the machine code at that address. The script calculates a SYS argument with numbers, π, `+`, `-`, `*`, `/` and brackets. For example, `SYS π*656` calls `$080c`.
|
|
@@ -57,16 +67,14 @@ The entry point of the machine code can be durable knowledge. The c64-reverse-en
|
|
|
57
67
|
|
|
58
68
|
## Result
|
|
59
69
|
|
|
60
|
-
The listing and the handoff addresses.
|
|
61
|
-
|
|
62
|
-
The exit status is 0 for a listing. The exit status is 1 for `decoded: false` and for an error. The exit status is 2 for a wrong argument.
|
|
70
|
+
The listing and the handoff addresses.
|
|
63
71
|
|
|
64
72
|
## Failure and conflicts
|
|
65
73
|
|
|
66
|
-
- `
|
|
74
|
+
- `invalid-input` with "is no BASIC program": the file is not a BASIC program. Usually the file is machine code. Give it to the c64-static-analysis skill. A memory dump also gives this error: its first two bytes are not a load address, and its bytes go past `$ffff`.
|
|
67
75
|
- An empty `handoffs` list: the BASIC does not start machine code with SYS or USR. Do not make up a handoff. Look for a POKE into a vector or a LOAD of another file in the listing.
|
|
68
76
|
- `installation-incomplete`, or `machine-unavailable` with a message that the host runtime does not run: stop the task. Give the message to the user and wait for the user. The message gives the remedy. Do not make a replacement for the tool or the host runtime, for example your own disk parser, disassembler or emulator script.
|
|
69
|
-
- `not-found` or `invalid-input`: the path or an argument is wrong. Read the message and correct the call.
|
|
77
|
+
- `not-found` or another `invalid-input`: the path or an argument is wrong. Read the message and correct the call.
|
|
70
78
|
|
|
71
79
|
## Handoffs
|
|
72
80
|
|
|
@@ -2,45 +2,35 @@
|
|
|
2
2
|
// through the Host Runtime and reports its machine-code handoffs.
|
|
3
3
|
|
|
4
4
|
import { statSync } from "node:fs";
|
|
5
|
-
import { parseArgs } from "node:util";
|
|
6
5
|
|
|
7
6
|
import { formatC64Address } from "#src/c64.ts";
|
|
7
|
+
import { Refusal } from "#src/errors.ts";
|
|
8
8
|
import { decodeBasic } from "#src/host-client/tools.ts";
|
|
9
9
|
import { resolveProjectPath } from "#src/project.ts";
|
|
10
|
-
import { runScript
|
|
10
|
+
import { runScript } from "#src/script.ts";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
12
|
+
await runScript({
|
|
13
|
+
name: "basic",
|
|
14
|
+
summary: "Decodes a C64 BASIC V2 program with petcat and reports its machine-code handoffs: SYS and USR.",
|
|
15
|
+
commands: [
|
|
16
|
+
{
|
|
17
|
+
name: "list",
|
|
18
|
+
summary: "The BASIC lines of a PRG, where the BASIC ends, and the handoffs to machine code.",
|
|
19
|
+
subjects: [{ name: "<program>", help: "a PRG file, relative to the project folder" }],
|
|
20
|
+
run: async (call) => {
|
|
21
|
+
const program = call.subject(0);
|
|
22
|
+
const result = await decodeBasic({ program });
|
|
23
|
+
if (!result.decoded) throw new Refusal("invalid-input", `${program} is no BASIC program: ${result.reason}`);
|
|
24
|
+
const size = statSync(resolveProjectPath(program)).size - 2;
|
|
25
|
+
const end = result.loadAddress + size - 1;
|
|
26
|
+
return {
|
|
27
|
+
loadRange: { start: formatC64Address(result.loadAddress), end: formatC64Address(end) },
|
|
28
|
+
// From here to the end of the load range, the file holds no BASIC (often machine code).
|
|
29
|
+
basicEnd: result.basicEnd <= end ? formatC64Address(result.basicEnd) : null,
|
|
30
|
+
listing: result.listing,
|
|
31
|
+
handoffs: result.handoffs.map((handoff) => ("address" in handoff ? { ...handoff, address: formatC64Address(handoff.address) } : handoff)),
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
34
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
await runScript(
|
|
40
|
-
async (argv) => {
|
|
41
|
-
const { output, failed } = await run(argv);
|
|
42
|
-
if (failed) process.exitCode = 1;
|
|
43
|
-
return output;
|
|
44
|
-
},
|
|
45
|
-
{ usage: USAGE },
|
|
46
|
-
);
|
|
35
|
+
],
|
|
36
|
+
});
|
package/skills/c64-disk/SKILL.md
CHANGED
|
@@ -23,7 +23,7 @@ This skill does not change the disk image and does not change project knowledge.
|
|
|
23
23
|
The script sends a disk image to the host runtime. The host runs c1541 and sends the result back. The script writes an extracted file into the project. It reads T64 and CRT files itself.
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
node <skill>/scripts/disk.ts <
|
|
26
|
+
node <skill>/scripts/disk.ts <command> <image> [name] [--out <file>]
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
| Need | Command |
|
|
@@ -38,16 +38,26 @@ node <skill>/scripts/disk.ts <action> <image> [name] [--out <file>]
|
|
|
38
38
|
| The banks of a cartridge | `directory original/game.crt` |
|
|
39
39
|
| Write each bank of a cartridge as a PRG | `crt original/game.crt --out cart` |
|
|
40
40
|
|
|
41
|
-
The script prints one JSON object.
|
|
42
|
-
|
|
43
41
|
File names show the characters that the C64 shows in a directory list. A byte that has no character shows as `{$xx}`, for example `{$c1}`. Write the name in the same form. The letter case of a name is not important. Put the name in single quotes in a shell, for example `'GAME {$c1}'`. A name that starts with `-` needs `--` before it, after all options, for example `read d.d64 --out x.prg -- '-----'`.
|
|
44
42
|
|
|
43
|
+
### Calling the script
|
|
44
|
+
|
|
45
|
+
- Call a script as `node <skill>/scripts/<script>.ts <command> [subjects] [options]`. The command comes first.
|
|
46
|
+
- `<script>.ts --help` lists the commands. `<script>.ts <command> --help` gives the subjects and the options of one command. The help is JSON: `{"text": "..."}`.
|
|
47
|
+
- An address is `$` and 1 to 4 hex digits, for example `'$c000'` or `'$fb'`, or a name. A name is a symbol, else an analyzer name that only one analyzer gives. Quote an address in a shell.
|
|
48
|
+
- A range is `<start>-<end>`, for example `'$c000-$cfff'`. In a JSON file, a range is `start` and `end`.
|
|
49
|
+
- Bytes in a JSON file are hex in a field `data`, for example `"data": "a900"`.
|
|
50
|
+
- The result is one JSON object on one line. A file that the script writes shows as `{"out", "bytes", "sha256"}`. A list shows as `{"count", "listed"}`, and `rest` tells where to get the rest of a long list.
|
|
51
|
+
- An error is `{"error": {"code": "...", "message": "..."}}`. The message tells what is wrong and what to do.
|
|
52
|
+
- The exit status is 0 for done or PASS, 1 for FAIL, 2 for an error and 3 for INCONCLUSIVE.
|
|
53
|
+
|
|
45
54
|
### Project folder
|
|
46
55
|
|
|
47
56
|
- Paths are relative to the project folder. The project folder is the nearest folder at or above the working directory that holds `.c64-re-tools/`.
|
|
48
57
|
- If the working directory is not in a project and has projects below it, the script refuses and lists them. Then give `--project <folder>` to each script.
|
|
49
58
|
- Write each file that you make for your work in `.c64-re-tools/local/` of the project. Git ignores that folder. Examples are results, batches, step files, runs, snapshots and memory dumps.
|
|
50
|
-
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence
|
|
59
|
+
- Move a file out of `.c64-re-tools/local/` only when it is the evidence for a finding. Move it before you give it to `--evidence test:<file>` or `--evidence file:<file>` of c64-knowledge. These two refuse a file in `.c64-re-tools/local/`.
|
|
60
|
+
- Do not move a coverage map after `coverage` of c64-knowledge imported it. `--evidence coverage:<map>` names the map by its path at the import. The knowledge keeps the coverage and the SHA-256 of the map, so the evidence stays valid.
|
|
51
61
|
- Put a run that is evidence in `runs/<name>/`, with its run file, its results and the snapshot files that it uses. Change the paths in the run file to the new paths. Then git keeps them, and another person can do the run again.
|
|
52
62
|
- A shell redirect (`>`) is relative to the working directory, not to the project folder. From the folder above the project, write `<folder>/.c64-re-tools/local/result.json`.
|
|
53
63
|
|
|
@@ -80,13 +90,13 @@ The script reads T64 and CRT files itself. The host runtime is not necessary for
|
|
|
80
90
|
|
|
81
91
|
## Result
|
|
82
92
|
|
|
83
|
-
The directory, the BAM, an entry or a sector chain as JSON.
|
|
93
|
+
The directory, the BAM, an entry or a sector chain as JSON.
|
|
84
94
|
|
|
85
|
-
|
|
95
|
+
`read` writes the file at the output path. Its result gives the `name` and the file as `{out, bytes, sha256}`. `crt` gives the header, `chips` and `files`: one item for each PRG and one for `crt.json`.
|
|
86
96
|
|
|
87
97
|
## Failure and conflicts
|
|
88
98
|
|
|
89
|
-
- `found
|
|
99
|
+
- `not-found`: the image file is missing, or the image has no file with that name. Read the directory again and compare the names.
|
|
90
100
|
- `media-error`: the image has a defect or is not a disk image. The message tells the problem, for example a sector chain that loops. Tell the user. Do not change the image.
|
|
91
101
|
- `installation-incomplete`, or `machine-unavailable` with a message that the host runtime does not run: stop the task. Give the message to the user and wait for the user. The message gives the remedy. Do not make a replacement for the tool or the host runtime, for example your own disk parser, disassembler or emulator script.
|
|
92
102
|
- `invalid-input`: an argument or a path is wrong. Read the message and correct the call.
|