@henols/c64-re-tools 2.0.0-rc.2 → 2.0.0-rc.21
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 +28 -62
- package/distribution/package.ts +1 -16
- package/distribution/plugin.ts +5 -3
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +25 -6
- package/skills/c64-assembler/scripts/assemble.ts +45 -10
- package/skills/c64-assembler/scripts/lib/compare.ts +48 -0
- package/skills/c64-basic/SKILL.md +15 -7
- package/skills/c64-basic/scripts/basic.ts +4 -4
- package/skills/c64-disk/SKILL.md +34 -11
- package/skills/c64-disk/scripts/disk.ts +65 -10
- package/skills/c64-disk/scripts/lib/containers.ts +124 -0
- package/skills/c64-docs/SKILL.md +138 -0
- package/skills/c64-docs/references/page-types.md +141 -0
- package/skills/c64-docs/references/style.md +43 -0
- package/skills/c64-docs/scripts/generate.ts +102 -0
- package/skills/c64-docs/scripts/lib/links.ts +155 -0
- package/skills/c64-docs/scripts/lib/markdown.ts +79 -0
- package/skills/c64-docs/scripts/lib/reference.ts +406 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +81 -0
- package/skills/c64-docs/scripts/link.ts +207 -0
- package/skills/c64-emulator/SKILL.md +29 -8
- package/skills/c64-knowledge/SKILL.md +201 -15
- package/skills/c64-knowledge/scripts/knowledge.ts +927 -78
- package/skills/c64-knowledge/scripts/lib/listing.ts +162 -0
- package/skills/c64-memory-map/SKILL.md +14 -2
- package/skills/c64-memory-map/scripts/memmap.ts +15 -4
- package/skills/c64-provenance/SKILL.md +100 -11
- package/skills/c64-provenance/scripts/lib/compare.ts +152 -0
- package/skills/c64-provenance/scripts/lib/library.ts +584 -0
- package/skills/c64-provenance/scripts/lib/runtime.ts +191 -0
- package/skills/c64-provenance/scripts/lib/separate.ts +140 -0
- package/skills/c64-provenance/scripts/provenance.ts +477 -24
- package/skills/c64-reverse-engineering/SKILL.md +90 -11
- package/skills/c64-source/SKILL.md +174 -0
- package/skills/c64-source/scripts/generate.ts +788 -0
- package/skills/c64-source/scripts/lib/loader.ts +630 -0
- package/skills/c64-source/scripts/lib/source.ts +1121 -0
- package/skills/c64-static-analysis/SKILL.md +53 -13
- package/skills/c64-static-analysis/scripts/analyze.ts +260 -31
- package/skills/c64-static-analysis/scripts/lib/coverage.ts +72 -0
- package/skills/c64-static-analysis/scripts/{findings.ts → lib/findings.ts} +11 -5
- package/skills/c64-static-analysis/scripts/lib/idioms.ts +158 -0
- package/skills/c64-static-analysis/scripts/lib/instructions.ts +105 -0
- package/skills/c64-static-analysis/scripts/lib/macros.ts +233 -0
- package/skills/c64-static-analysis/scripts/lib/pitfalls.ts +68 -0
- package/skills/c64-static-analysis/scripts/lib/pointers.ts +108 -0
- package/skills/c64-testing/SKILL.md +57 -4
- package/skills/c64-testing/references/scenario-format.md +50 -6
- package/skills/c64-testing/scripts/checklist.ts +3 -3
- package/skills/c64-testing/scripts/diff.ts +167 -0
- package/skills/c64-testing/scripts/lib/diff.ts +70 -0
- package/skills/c64-testing/scripts/{scenario.ts → lib/scenario.ts} +183 -152
- package/skills/c64-testing/scripts/run.ts +241 -0
- package/skills/c64-testing/scripts/test.ts +20 -10
- package/skills/c64-unpacker/SKILL.md +88 -21
- package/skills/c64-unpacker/scripts/lib/evidence.ts +207 -0
- package/skills/c64-unpacker/scripts/lib/loads.ts +209 -0
- package/skills/c64-unpacker/scripts/lib/machine.ts +80 -0
- package/skills/c64-unpacker/scripts/lib/stages.ts +142 -0
- package/skills/c64-unpacker/scripts/unpack.ts +545 -61
- package/src/c64.ts +240 -12
- package/src/cli/cleanup.ts +1 -1
- package/src/cli/main.ts +14 -8
- package/src/cli/status.ts +105 -11
- package/src/graphics.ts +158 -0
- package/src/host/main.ts +57 -31
- package/src/host/server.ts +30 -47
- package/src/host/tools/c1541.ts +34 -5
- package/src/host/tools/index.ts +6 -2
- package/src/host/tools/petcat.ts +10 -3
- package/src/host/tools/status.ts +23 -6
- package/src/host/vice/adapter.ts +64 -20
- package/src/host/vice/chips.ts +11 -1
- package/src/host/vice/process.ts +19 -8
- package/src/host/vice/screen.ts +4 -44
- package/src/host/vice/session.ts +343 -60
- package/src/host-client/autostart.ts +55 -0
- package/src/host-client/connect.ts +14 -14
- package/src/host-client/coverage.ts +36 -0
- package/src/host-client/image.ts +63 -0
- package/src/host-client/steps.ts +175 -0
- package/src/host-client/tools.ts +7 -6
- package/src/host-client/transfer.ts +60 -10
- package/src/host-client/vice-session.ts +107 -28
- package/src/knowledge/conflicts.ts +161 -0
- package/src/knowledge/database.ts +150 -38
- package/src/knowledge/expression.ts +209 -0
- package/src/knowledge/graph.ts +104 -0
- package/src/knowledge/history.ts +105 -27
- package/src/knowledge/import.ts +141 -85
- package/src/knowledge/join.ts +211 -0
- package/src/knowledge/program.ts +258 -0
- package/src/knowledge/read.ts +445 -63
- package/src/knowledge/schema.ts +277 -65
- package/src/knowledge/text.ts +415 -0
- package/src/knowledge/write.ts +1145 -150
- package/src/mcp/main.ts +44 -19
- package/src/mcp/output.ts +39 -0
- package/src/mcp/server.ts +15 -4
- package/src/mcp/tools/debug.ts +202 -31
- package/src/mcp/tools/execution.ts +39 -5
- package/src/mcp/tools/input.ts +7 -4
- package/src/mcp/tools/machine.ts +14 -2
- package/src/mcp/tools/media.ts +44 -16
- package/src/mcp/tools/memory.ts +68 -12
- package/src/mcp/tools/video.ts +20 -9
- package/src/native/discover.ts +44 -6
- package/src/native/ghidra/analyze.ts +2 -2
- package/src/native/ghidra/index.ts +19 -3
- package/src/native/ghidra/language/c64rt_6510.slaspec +4 -1
- package/src/native/ghidra/scripts/C64Export.java +33 -1
- package/src/native/ghidra/scripts/C64Prepare.java +7 -1
- package/src/native/status.ts +15 -3
- package/src/native/types.ts +43 -8
- package/src/opcodes.ts +133 -0
- package/src/package.ts +21 -0
- package/{skills/c64-memory-map/scripts → src}/platform.ts +87 -7
- package/src/project.ts +166 -3
- package/src/protocol/messages.ts +17 -10
- package/src/protocol/tools.ts +27 -22
- package/src/protocol/vice.ts +255 -33
- package/src/script.ts +49 -9
- package/src/trace.ts +55 -23
- package/skills/c64-provenance/scripts/compare.ts +0 -92
- package/skills/c64-unpacker/scripts/evidence.ts +0 -135
package/README.md
CHANGED
|
@@ -12,78 +12,51 @@ functional-equivalence tests.
|
|
|
12
12
|
Architecture in one line: one MCP process owns one VICE instance and is
|
|
13
13
|
VICE-only; a Host Runtime on the graphical host owns VICE and the tools that
|
|
14
14
|
come with it (c1541, petcat); skill scripts call the Host Runtime for those,
|
|
15
|
-
run ACME, DXA and Ghidra themselves, and keep project knowledge local
|
|
16
|
-
`.c64-re-tools/knowledge
|
|
15
|
+
run ACME, DXA and Ghidra themselves, and keep project knowledge local as
|
|
16
|
+
text in `.c64-re-tools/knowledge/`.
|
|
17
17
|
The design is in [`docs/redesign/`](docs/redesign/README.md), and
|
|
18
18
|
[`docs/plan.md`](docs/plan.md) tracks the implementation.
|
|
19
19
|
|
|
20
|
-
## Install
|
|
20
|
+
## Install
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
You need Node.js 24 or newer, and VICE 3.9 or later (`x64sc`, `c1541`, `petcat`) with its ROM files; use the latest VICE release. `status` and the host name the known flaws of an older VICE. ACME, DXA and Ghidra are needed only for the skills that use them: [docs/install-tools.md](docs/install-tools.md) has the steps for each system.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
- On the machine with VICE: VICE 3.9 or later (`x64sc`, `c1541`, `petcat`) with its ROM files.
|
|
26
|
-
- Where the agent runs: ACME, DXA and Ghidra, for the skills that use them.
|
|
27
|
-
|
|
28
|
-
Everything runs through npx. Nothing is installed globally.
|
|
29
|
-
|
|
30
|
-
### Release or prerelease
|
|
31
|
-
|
|
32
|
-
| Channel | Version |
|
|
33
|
-
| --- | --- |
|
|
34
|
-
| `latest` | The current release. |
|
|
35
|
-
| `next` | The current prerelease, for example `2.0.0-rc.1`. |
|
|
36
|
-
|
|
37
|
-
Until 2.0.0 is released, `latest` is the old 1.1.0. Use `next` until then. In the commands below, replace `<channel>` with `latest` or `next`. Use the same channel on both machines.
|
|
38
|
-
|
|
39
|
-
### On one machine
|
|
40
|
-
|
|
41
|
-
Start the Host Runtime in its own terminal. Ctrl+C stops it and every emulator:
|
|
24
|
+
Start the Host Runtime and leave it running:
|
|
42
25
|
|
|
43
26
|
```
|
|
44
|
-
npx -y --package=@henols/c64-re-tools
|
|
27
|
+
npx -y --package=@henols/c64-re-tools@latest c64-re-tools-host
|
|
45
28
|
```
|
|
46
29
|
|
|
47
|
-
|
|
30
|
+
The host logs each call from a skill script. Add `--log-console` to print also each MCP call and each c1541 or petcat run, or `--log-dir <directory>` to write them to a file. Both are off by default.
|
|
31
|
+
|
|
32
|
+
Install into your project and start the agent:
|
|
48
33
|
|
|
49
34
|
```
|
|
50
35
|
cd my-c64-project
|
|
51
|
-
npx -y @henols/c64-re-tools
|
|
52
|
-
npx -y @henols/c64-re-tools@<channel> status
|
|
36
|
+
npx -y @henols/c64-re-tools@latest install --target claude
|
|
53
37
|
claude
|
|
54
38
|
```
|
|
55
39
|
|
|
56
|
-
|
|
40
|
+
Several programs in one repository: install once at the root, give each program its own folder, and start the agent at the root. A folder with `.c64-re-tools/` is a project; the agent names one with `--project <folder>`.
|
|
57
41
|
|
|
58
|
-
|
|
42
|
+
`npx -y @henols/c64-re-tools@latest status` shows what works.
|
|
59
43
|
|
|
60
|
-
|
|
61
|
-
export C64RT_HOST_TOKEN=$(openssl rand -hex 16)
|
|
62
|
-
npx -y --package=@henols/c64-re-tools@<channel> c64-re-tools-host --listen 172.17.0.1
|
|
63
|
-
```
|
|
44
|
+
Until 2.0.0 is released, `@latest` is the old 1.1.0. Use the prerelease instead. For the prerelease, a dev container and debugging, see [docs/debug-install.md](docs/debug-install.md).
|
|
64
45
|
|
|
65
|
-
|
|
46
|
+
### Dev container
|
|
66
47
|
|
|
67
|
-
|
|
68
|
-
npx -y @henols/c64-re-tools@<channel> install --target claude
|
|
69
|
-
npx -y @henols/c64-re-tools@<channel> status
|
|
70
|
-
claude
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
The container reaches the host as `host.docker.internal`. `status` must show the Host Runtime as reachable.
|
|
48
|
+
The agent runs in the container, VICE and the Host Runtime on the host. In `.devcontainer/devcontainer.json`:
|
|
74
49
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
50
|
+
```json
|
|
51
|
+
"features": {
|
|
52
|
+
"https://github.com/henols/c64-re-tools/releases/latest/download/devcontainer-feature-c64-re-tools.tgz": { "target": "claude" }
|
|
53
|
+
},
|
|
54
|
+
"runArgs": ["--add-host=host.docker.internal:host-gateway"]
|
|
80
55
|
```
|
|
81
56
|
|
|
82
|
-
|
|
57
|
+
Until 2.0.0 is released, the `latest` URL has no Feature. Use the prerelease: `https://github.com/henols/c64-re-tools/releases/download/v2.0.0-rc.4/devcontainer-feature-c64-re-tools.tgz` with `{ "target": "claude", "channel": "next" }`.
|
|
83
58
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
See [docs/debugging.md](docs/debugging.md) for the trace and the field test.
|
|
59
|
+
The Feature installs ACME, dxa, Java 21 and Ghidra, puts the toolkit into the project when the container is created, and updates it at each start. `target` is a harness or a comma list. `channel` is `latest` (default) or `next`. Debian and Ubuntu images only. `runArgs` is for Docker on Linux. Start the Host Runtime on the host as above.
|
|
87
60
|
|
|
88
61
|
### Settings
|
|
89
62
|
|
|
@@ -94,9 +67,8 @@ See [docs/debugging.md](docs/debugging.md) for the trace and the field test.
|
|
|
94
67
|
| `C64RT_ACME`, `C64RT_DXA` | skills, CLI | Full path of the tool when it is not on `PATH`. |
|
|
95
68
|
| `C64RT_GHIDRA` | skills, CLI | The Ghidra installation directory, when `analyzeHeadless` is not on `PATH`. |
|
|
96
69
|
| `C64RT_HOST` | MCP, skills, CLI | `host:port` of the Host Runtime when it is not on 127.0.0.1, `host.docker.internal` or `host.containers.internal` at 6464. |
|
|
97
|
-
| `C64RT_HOST_TOKEN` | host, MCP, skills, CLI | The shared secret for a Host Runtime that listens beyond loopback. |
|
|
98
70
|
| `C64RT_VIDEO` | MCP | `pal` (default) or `ntsc`, fixed for the life of the MCP process. |
|
|
99
|
-
| `C64RT_TRACE` |
|
|
71
|
+
| `C64RT_TRACE` | MCP, skills, CLI | A directory for a trace of what the programs do, for a person who debugs a run. See [docs/debug-install.md](docs/debug-install.md). Nothing of the trace reaches the agent. The host uses `--log-dir` instead. |
|
|
100
72
|
|
|
101
73
|
Ghidra runs with its own settings directory for each request. The toolkit
|
|
102
74
|
brings its own NMOS 6510 language (all 256 opcodes); your Ghidra installation
|
|
@@ -106,7 +78,7 @@ and settings stay unchanged.
|
|
|
106
78
|
|
|
107
79
|
1. Install for your harness: `--target claude`, `codex`, `pi`, `opencode`, `gemini`, `copilot`, `cursor` or `windsurf`. Without `--target`, `install` installs for all of them.
|
|
108
80
|
2. Start the Host Runtime and leave it running.
|
|
109
|
-
3. Start the harness in the project, for example `claude` or `codex`, or open the project in Cursor or Windsurf. The harness starts the MCP server and finds the skills by itself.
|
|
81
|
+
3. Start the harness in the project (or at the root of a repository with several projects), for example `claude` or `codex`, or open the project in Cursor or Windsurf. The harness starts the MCP server and finds the skills by itself.
|
|
110
82
|
4. Ask for the result you want, in plain words. The agent picks the skills and the `c64_*` tools.
|
|
111
83
|
|
|
112
84
|
Examples:
|
|
@@ -123,7 +95,7 @@ Build it, run it in the emulator, and write a test for it.
|
|
|
123
95
|
Show me the emulator window.
|
|
124
96
|
```
|
|
125
97
|
|
|
126
|
-
The agent keeps what it learns about the program in `.c64-re-tools/knowledge
|
|
98
|
+
The agent keeps what it learns about the program as text in `.c64-re-tools/knowledge/`. Commit that folder with the project. After a `git pull` that joined the work of two computers, run `knowledge.ts merge` when a command reports conflicts.
|
|
127
99
|
|
|
128
100
|
## Skills
|
|
129
101
|
|
|
@@ -137,7 +109,8 @@ The agent keeps what it learns about the program in `.c64-re-tools/knowledge.db`
|
|
|
137
109
|
| c64-basic | BASIC V2 listings and the SYS/USR handoff to machine code. |
|
|
138
110
|
| c64-unpacker | Packing evidence, and capture of the unpacked program from the emulator. |
|
|
139
111
|
| c64-memory-map | Platform meaning of addresses and register values. |
|
|
140
|
-
| c64-provenance | Byte comparison of releases. |
|
|
112
|
+
| c64-provenance | Byte comparison of releases, and a library of routines that several programs share. |
|
|
113
|
+
| c64-source | ACME source from the image and the knowledge, checked byte by byte, and a loader for the entry state. |
|
|
141
114
|
| c64-assembler | ACME builds with diagnostics and symbols. |
|
|
142
115
|
| c64-testing | Original-against-rebuild scenarios with PASS, FAIL or INCONCLUSIVE. |
|
|
143
116
|
|
|
@@ -190,14 +163,7 @@ C64RT_LIVE_VICE=/usr/bin/x64sc C64RT_GHIDRA=/opt/ghidra pnpm test
|
|
|
190
163
|
|
|
191
164
|
ACME, DXA, c1541 and petcat tests run when the tools are on `PATH`.
|
|
192
165
|
|
|
193
|
-
To release, push a tag
|
|
194
|
-
|
|
195
|
-
```
|
|
196
|
-
git tag v2.0.0-rc.2 && git push origin v2.0.0-rc.2 # a prerelease, to npm next
|
|
197
|
-
git tag v2.0.0 && git push origin v2.0.0 # a release, to npm latest
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
CI checks and tests the tagged commit, publishes it to npm and makes a GitHub Release. The repository keeps the version `0.0.0-development`.
|
|
166
|
+
To release, push a tag. See [docs/releasing.md](docs/releasing.md).
|
|
201
167
|
|
|
202
168
|
## Layout
|
|
203
169
|
|
package/distribution/package.ts
CHANGED
|
@@ -2,22 +2,7 @@
|
|
|
2
2
|
// only reads package.json, so a caller that needs the declaration does not
|
|
3
3
|
// build the plugin.
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The npm dist-tag that a version is published under: a prerelease
|
|
10
|
-
* (2.0.0-rc.1) goes to "next", a release to "latest".
|
|
11
|
-
*/
|
|
12
|
-
export function channelOf(version: string): "latest" | "next" {
|
|
13
|
-
return version.includes("-") ? "next" : "latest";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** The name, version and dist-tag of this npm package, from its package.json. */
|
|
17
|
-
export const PACKAGE = (() => {
|
|
18
|
-
const metadata = JSON.parse(readFileSync(join(import.meta.dirname, "..", "package.json"), "utf8")) as { name: string; version: string };
|
|
19
|
-
return { name: metadata.name, version: metadata.version, tag: channelOf(metadata.version) };
|
|
20
|
-
})();
|
|
5
|
+
import { PACKAGE } from "../src/package.ts";
|
|
21
6
|
|
|
22
7
|
/**
|
|
23
8
|
* The agent starts the MCP server through npx with the newest version of the
|
package/distribution/plugin.ts
CHANGED
|
@@ -13,7 +13,8 @@ import { dirname, join, relative, resolve, sep } from "node:path";
|
|
|
13
13
|
|
|
14
14
|
import { definePlugin, defineSkill, type Skill } from "@jalco/ap-sdk";
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { PACKAGE } from "../src/package.ts";
|
|
17
|
+
import { MCP_SERVER } from "./package.ts";
|
|
17
18
|
|
|
18
19
|
const root = resolve(import.meta.dirname, "..");
|
|
19
20
|
const SOURCE = join(root, "src");
|
|
@@ -27,9 +28,10 @@ const SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\bnew URL\(\s*)(["'])((?:\.\.?
|
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* The package.json of an installed skill: ES modules, #src/* resolved inside
|
|
30
|
-
* the skill, and the version of c64-re-tools that the copy comes
|
|
31
|
+
* the skill, and the name and version of c64-re-tools that the copy comes
|
|
32
|
+
* from, which src/package.ts reads.
|
|
31
33
|
*/
|
|
32
|
-
export const SKILL_PACKAGE = `${JSON.stringify({ type: "module", version: PACKAGE.version, imports: { "#src/*": "./src/*" } }, null, 2)}\n`;
|
|
34
|
+
export const SKILL_PACKAGE = `${JSON.stringify({ name: PACKAGE.name, type: "module", version: PACKAGE.version, imports: { "#src/*": "./src/*" } }, null, 2)}\n`;
|
|
33
35
|
|
|
34
36
|
/** The scripts that a SKILL.md runs, by file name. */
|
|
35
37
|
export function scriptsOf(skillMarkdown: string): string[] {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henols/c64-re-tools",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.21",
|
|
4
4
|
"description": "Agent-friendly Commodore 64 engineering toolkit for reverse engineering, debugging, development and testing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Henrik Olsson",
|
|
@@ -13,9 +13,9 @@ This skill does not run the program and does not change project knowledge.
|
|
|
13
13
|
|
|
14
14
|
## Inputs
|
|
15
15
|
|
|
16
|
-
- The source root: the project
|
|
16
|
+
- The source root: the folder in the project that holds all source files of the program.
|
|
17
17
|
- The entry source: the main file, relative to the source root.
|
|
18
|
-
- The output path: where to write the PRG, relative to the project
|
|
18
|
+
- The output path: where to write the PRG, relative to the project folder.
|
|
19
19
|
- Optional include directories, symbol definitions and a start address.
|
|
20
20
|
|
|
21
21
|
## Tools / execution path
|
|
@@ -31,19 +31,36 @@ node <skill>/scripts/assemble.ts --source-root src --entry main.a --out build/ga
|
|
|
31
31
|
| `--include lib` | An include directory, relative to the source root. Use it again for more directories. |
|
|
32
32
|
| `--define DEBUG=1` | An assembler symbol: an integer, also as `$` hex, or `true` or `false`. Use it again for more symbols. |
|
|
33
33
|
| `--set-pc '$0801'` | The start address when the source does not set one. |
|
|
34
|
+
| `--compare` | Check the program byte by byte against the program in the knowledge, each segment. Put it in with `image` of the c64-knowledge skill. `--space drive8` or `--space bank0` compares with the program of that space. |
|
|
35
|
+
| `--plain` | Write only the bytes of the program, without the 2-byte load address of a PRG. `loadRange` still tells where the bytes go. |
|
|
36
|
+
| `--all-symbols` | List all symbols. Without it, the list does not include the constants that the source does not use. `unusedConstants` gives their count. |
|
|
34
37
|
|
|
35
38
|
The script copies the full source root. Put all files that the program includes under it. Version control directories stay out.
|
|
36
39
|
|
|
37
|
-
The script prints one JSON object. The target CPU is the 6510
|
|
40
|
+
The script prints one JSON object. The target CPU is the 6510. The output is a C64 PRG, or the bytes only with `--plain`.
|
|
41
|
+
|
|
42
|
+
### Project folder
|
|
43
|
+
|
|
44
|
+
- 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
|
+
- 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
|
+
- 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` of c64-knowledge. `--evidence` refuses a file in `.c64-re-tools/local/`.
|
|
48
|
+
- 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
|
+
- 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`.
|
|
38
50
|
|
|
39
51
|
## Workflow
|
|
40
52
|
|
|
41
53
|
1. Find the source root and the entry source. The entry source sets its own start address with `* = $0801` or similar.
|
|
42
54
|
2. Run the script.
|
|
43
|
-
3. If `assembled` is `true`, the PRG is at `output`. `loadRange` tells where it loads. `symbols` gives each symbol
|
|
55
|
+
3. If `assembled` is `true`, the PRG is at `output`. `loadRange` tells where it loads. `symbols` gives each symbol:
|
|
56
|
+
- Kind `address` has a value in `loadRange`. It gives the `address`.
|
|
57
|
+
- 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
|
+
- `used` is `false` when the source does not use the symbol.
|
|
44
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.
|
|
45
60
|
5. To run the program, give the PRG path to the c64-emulator skill.
|
|
46
61
|
|
|
62
|
+
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.
|
|
63
|
+
|
|
47
64
|
## Knowledge
|
|
48
65
|
|
|
49
66
|
Automatic write:
|
|
@@ -59,12 +76,14 @@ A symbol from the rebuilt source can give the address of a routine in the rebuil
|
|
|
59
76
|
|
|
60
77
|
The PRG in the project at the output path, with its load range and symbols. Or the source diagnostics when the assembly failed.
|
|
61
78
|
|
|
62
|
-
|
|
79
|
+
With `--compare`, `compare` gives `identical` and one check for each segment of the program in the knowledge. A check is `identical`, `different` with the first different address, or `not covered` when the program does not hold the segment.
|
|
80
|
+
|
|
81
|
+
The exit status is 0 for a PRG. The exit status is 1 for `assembled: false`, for a `--compare` that is not identical, and for an error. The exit status is 2 for a wrong argument.
|
|
63
82
|
|
|
64
83
|
## Failure and conflicts
|
|
65
84
|
|
|
66
85
|
- `assembled: false`: the source has errors. This is a normal result. Correct the source.
|
|
67
|
-
- `installation-incomplete
|
|
86
|
+
- `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.
|
|
68
87
|
- `invalid-input`: an option or a path is wrong. Read the message and correct the call.
|
|
69
88
|
|
|
70
89
|
## Handoffs
|
|
@@ -12,17 +12,28 @@ import { localToolContext } from "#src/native/local.ts";
|
|
|
12
12
|
import type { AcmeParams } from "#src/native/types.ts";
|
|
13
13
|
import { resolveProjectPath } from "#src/project.ts";
|
|
14
14
|
import { runScript, UsageError } from "#src/script.ts";
|
|
15
|
+
import { compareBuild } from "./lib/compare.ts";
|
|
16
|
+
import { KnowledgeError, openForRead } from "#src/knowledge/database.ts";
|
|
17
|
+
import { requireProgram, type ProgramImage } from "#src/knowledge/program.ts";
|
|
18
|
+
import { DEFAULT_SPACE, SPACE } from "#src/knowledge/schema.ts";
|
|
15
19
|
|
|
16
20
|
const USAGE = `assemble.ts --source-root <dir> --entry <file> --out <file.prg> [options]
|
|
17
21
|
|
|
18
|
-
--source-root <dir> project
|
|
22
|
+
--source-root <dir> folder in the project to assemble from; all of it is sent
|
|
19
23
|
--entry <file> the main source file, relative to the source root
|
|
20
24
|
--out <file.prg> where to write the program, relative to the project
|
|
21
25
|
--include <dir> include directory relative to the source root (repeatable)
|
|
22
26
|
--define NAME=VALUE assembler symbol: an integer ($hex allowed) or true/false (repeatable)
|
|
23
27
|
--set-pc <address> start address when the source sets none, for example '$0801'
|
|
28
|
+
--compare check the program byte by byte against the program in the
|
|
29
|
+
knowledge (D83), each segment; put it in with knowledge.ts
|
|
30
|
+
image of the c64-knowledge skill
|
|
31
|
+
--space <space> with --compare: the knowledge space of the program, c64 (the
|
|
32
|
+
default), drive8, or bank0 to bank255
|
|
33
|
+
--plain write the bytes only, without the 2-byte load address of a PRG
|
|
34
|
+
--all-symbols list also the constants that the source does not use
|
|
24
35
|
|
|
25
|
-
The result is one JSON object. Addresses are $ and four hex digits.`;
|
|
36
|
+
The result is one JSON object. Addresses and symbol values are $ and at least four hex digits.`;
|
|
26
37
|
|
|
27
38
|
function parseDefine(text: string): [string, number | boolean] {
|
|
28
39
|
const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.+)$/.exec(text);
|
|
@@ -45,6 +56,10 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
45
56
|
include: { type: "string", multiple: true },
|
|
46
57
|
define: { type: "string", multiple: true },
|
|
47
58
|
"set-pc": { type: "string" },
|
|
59
|
+
compare: { type: "boolean" },
|
|
60
|
+
space: { type: "string" },
|
|
61
|
+
plain: { type: "boolean" },
|
|
62
|
+
"all-symbols": { type: "boolean" },
|
|
48
63
|
help: { type: "boolean" },
|
|
49
64
|
},
|
|
50
65
|
});
|
|
@@ -57,7 +72,20 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
57
72
|
try {
|
|
58
73
|
outPath = resolveProjectPath(out);
|
|
59
74
|
} catch {
|
|
60
|
-
throw new UsageError(`--out must be a path relative to the project
|
|
75
|
+
throw new UsageError(`--out must be a path relative to the project folder that stays inside it, not ${out}`);
|
|
76
|
+
}
|
|
77
|
+
if (values.space !== undefined && !values.compare) throw new UsageError("--space works only with --compare");
|
|
78
|
+
const space = values.space ?? DEFAULT_SPACE;
|
|
79
|
+
if (!SPACE.test(space)) throw new UsageError("--space must be c64, drive8, or bank0 to bank255");
|
|
80
|
+
// Read the program before ACME runs, so a project without it stops at once.
|
|
81
|
+
let program: ProgramImage | undefined;
|
|
82
|
+
if (values.compare) {
|
|
83
|
+
const db = openForRead();
|
|
84
|
+
try {
|
|
85
|
+
program = requireProgram(db, space);
|
|
86
|
+
} finally {
|
|
87
|
+
db?.close();
|
|
88
|
+
}
|
|
61
89
|
}
|
|
62
90
|
const defines = Object.fromEntries((values.define ?? []).map(parseDefine));
|
|
63
91
|
let setPc: number | undefined;
|
|
@@ -75,29 +103,36 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
75
103
|
const { result, attachments } = await assemble(params, tree.contents, localToolContext());
|
|
76
104
|
if (!result.assembled) return { output: { assembled: false, diagnostics: result.diagnostics }, failed: true };
|
|
77
105
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
78
|
-
writeFileSync(outPath, attachments![0]!);
|
|
106
|
+
writeFileSync(outPath, values.plain ? attachments![0]!.subarray(2) : attachments![0]!);
|
|
79
107
|
const range = result.loadRange!;
|
|
108
|
+
const compared = program === undefined ? undefined : compareBuild(attachments![0]!, program);
|
|
109
|
+
// An include file of platform names gives many constants that the program does not use.
|
|
110
|
+
const listed = result.symbols!.filter((symbol) => values["all-symbols"] || symbol.kind === "address" || symbol.used);
|
|
80
111
|
return {
|
|
81
112
|
output: {
|
|
82
113
|
assembled: true,
|
|
83
114
|
output: out,
|
|
115
|
+
...(values.plain ? { plain: true } : {}),
|
|
84
116
|
loadRange: { start: formatC64Address(range.start), end: formatC64Address(range.end), bytes: range.bytes },
|
|
85
|
-
symbols:
|
|
117
|
+
symbols: listed.map((symbol) =>
|
|
86
118
|
symbol.kind === "address"
|
|
87
119
|
? { name: symbol.name, kind: "address", address: formatC64Address(symbol.address), used: symbol.used }
|
|
88
|
-
: { name: symbol.name, kind: "constant", value: symbol.value, used: symbol.used },
|
|
120
|
+
: { name: symbol.name, kind: "constant", value: `$${symbol.value.toString(16).padStart(4, "0")}`, used: symbol.used },
|
|
89
121
|
),
|
|
122
|
+
...(values["all-symbols"] ? {} : { unusedConstants: result.symbols!.length - listed.length }),
|
|
90
123
|
diagnostics: result.diagnostics,
|
|
124
|
+
...(compared === undefined ? {} : { compare: compared }),
|
|
91
125
|
},
|
|
92
|
-
failed
|
|
126
|
+
// A build that differs from its reference is a failed check.
|
|
127
|
+
failed: compared !== undefined && !compared.identical,
|
|
93
128
|
};
|
|
94
129
|
}
|
|
95
130
|
|
|
96
131
|
await runScript(
|
|
97
|
-
async () => {
|
|
98
|
-
const { output, failed } = await run(
|
|
132
|
+
async (argv) => {
|
|
133
|
+
const { output, failed } = await run(argv);
|
|
99
134
|
if (failed) process.exitCode = 1;
|
|
100
135
|
return output;
|
|
101
136
|
},
|
|
102
|
-
{ usage: USAGE },
|
|
137
|
+
{ usage: USAGE, refusals: [KnowledgeError] },
|
|
103
138
|
);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// The byte check of a build (D47): the assembled program against the program
|
|
2
|
+
// in the knowledge (D83), segment by segment.
|
|
3
|
+
|
|
4
|
+
import { formatC64Address, prgRange } from "#src/c64.ts";
|
|
5
|
+
|
|
6
|
+
export interface SegmentCheck {
|
|
7
|
+
start: string;
|
|
8
|
+
end: string;
|
|
9
|
+
result: "identical" | "different" | "not covered";
|
|
10
|
+
/** Bytes of the segment that the build does not hold. */
|
|
11
|
+
uncovered?: number;
|
|
12
|
+
differences?: number;
|
|
13
|
+
firstDifference?: { address: string; built: number; reference: number };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The program that the build is checked against: its segments and its bytes in them. */
|
|
17
|
+
export interface Reference {
|
|
18
|
+
segments: ReadonlyArray<{ start: number; end: number }>;
|
|
19
|
+
byteAt: (address: number) => number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Compares the assembled PRG with the reference, segment by segment. */
|
|
23
|
+
export function compareBuild(prg: Uint8Array, program: Reference): { identical: boolean; segments: SegmentCheck[] } {
|
|
24
|
+
const built = prgRange(prg);
|
|
25
|
+
const checks = program.segments.map((segment): SegmentCheck => {
|
|
26
|
+
const from = Math.max(segment.start, built.start);
|
|
27
|
+
const to = Math.min(segment.end, built.end);
|
|
28
|
+
const shown = { start: formatC64Address(segment.start), end: formatC64Address(segment.end) };
|
|
29
|
+
if (from > to) return { ...shown, result: "not covered", uncovered: segment.end - segment.start + 1 };
|
|
30
|
+
let differences = 0;
|
|
31
|
+
let first: SegmentCheck["firstDifference"];
|
|
32
|
+
for (let address = from; address <= to; address++) {
|
|
33
|
+
const builtByte = built.body[address - built.start]!;
|
|
34
|
+
const referenceByte = program.byteAt(address);
|
|
35
|
+
if (builtByte === referenceByte) continue;
|
|
36
|
+
differences++;
|
|
37
|
+
first ??= { address: formatC64Address(address), built: builtByte, reference: referenceByte };
|
|
38
|
+
}
|
|
39
|
+
const uncovered = segment.end - segment.start + 1 - (to - from + 1);
|
|
40
|
+
return {
|
|
41
|
+
...shown,
|
|
42
|
+
result: differences > 0 ? "different" : uncovered > 0 ? "not covered" : "identical",
|
|
43
|
+
...(uncovered > 0 ? { uncovered } : {}),
|
|
44
|
+
...(differences > 0 ? { differences, firstDifference: first! } : {}),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
return { identical: checks.every((check) => check.result === "identical"), segments: checks };
|
|
48
|
+
}
|
|
@@ -13,7 +13,7 @@ This skill does not run the program and does not change project knowledge.
|
|
|
13
13
|
|
|
14
14
|
## Inputs
|
|
15
15
|
|
|
16
|
-
- The program: a PRG file, relative to the project
|
|
16
|
+
- The program: a PRG file, relative to the project folder.
|
|
17
17
|
|
|
18
18
|
## Tools / execution path
|
|
19
19
|
|
|
@@ -25,14 +25,23 @@ node <skill>/scripts/basic.ts extracted/game.prg
|
|
|
25
25
|
|
|
26
26
|
The script prints one JSON object. Addresses are `$` and four hex digits.
|
|
27
27
|
|
|
28
|
+
### Project folder
|
|
29
|
+
|
|
30
|
+
- 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
|
+
- 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
|
+
- 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` of c64-knowledge. `--evidence` refuses a file in `.c64-re-tools/local/`.
|
|
34
|
+
- 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
|
+
- 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
|
+
|
|
28
37
|
## Workflow
|
|
29
38
|
|
|
30
39
|
1. Run the script on the program.
|
|
31
40
|
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}`.
|
|
32
41
|
3. Read `handoffs`. Each handoff has a `kind` (`sys` or `usr`) and the BASIC `line`.
|
|
33
|
-
- A `sys` handoff with an `address`: BASIC calls the machine code at that address.
|
|
34
|
-
- A handoff with `computed: true`: the address comes from a calculation at run time
|
|
35
|
-
4. Compare the address with `loadRange` and `basicEnd`. From `basicEnd` to the end of `loadRange`, the file holds bytes that are not BASIC. Usually this is the machine code that the handoff calls.
|
|
42
|
+
- 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`.
|
|
43
|
+
- A handoff with `computed: true`: the address comes from a calculation at run time, for example with `PEEK`. A `sys` handoff gives its argument in `expression`. Use the c64-emulator skill to find the address.
|
|
44
|
+
4. Compare the address with `loadRange` and `basicEnd`. From `basicEnd` to the end of `loadRange`, the file holds bytes that are not BASIC. Usually this is the machine code that the handoff calls. Some programs have no end marker (two zero bytes) after the last line. The machine code starts directly after the last line. Then `basicEnd` is the first byte of that machine code.
|
|
36
45
|
|
|
37
46
|
The address ranges are for a load to the load address of the file. A load without `,1` puts a BASIC program at `$0801`. Most loaders have `$0801` as their load address.
|
|
38
47
|
|
|
@@ -54,10 +63,9 @@ The exit status is 0 for a listing. The exit status is 1 for `decoded: false` an
|
|
|
54
63
|
|
|
55
64
|
## Failure and conflicts
|
|
56
65
|
|
|
57
|
-
- `decoded: false`: the file is not a BASIC program. This is a normal result. Usually the file is machine code. Give it to the c64-static-analysis skill.
|
|
66
|
+
- `decoded: false`: the file is not a BASIC program. This is a normal result. Usually the file is machine code. Give it to the c64-static-analysis skill. A memory dump also gives `decoded: false`: its first two bytes are not a load address, and its bytes go past `$ffff`.
|
|
58
67
|
- 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.
|
|
59
|
-
- `installation-incomplete
|
|
60
|
-
- `operation-failed` with a message about the host runtime: the host runtime does not run. Tell the user to start the host runtime with `npx -y --package=@henols/c64-re-tools@latest c64-re-tools-host`.
|
|
68
|
+
- `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.
|
|
61
69
|
- `not-found` or `invalid-input`: the path or an argument is wrong. Read the message and correct the call.
|
|
62
70
|
|
|
63
71
|
## Handoffs
|
|
@@ -11,7 +11,7 @@ import { runScript, UsageError } from "#src/script.ts";
|
|
|
11
11
|
|
|
12
12
|
const USAGE = `basic.ts <program>
|
|
13
13
|
|
|
14
|
-
<program> is a PRG file relative to the project
|
|
14
|
+
<program> is a PRG file relative to the project folder. The result is one
|
|
15
15
|
JSON object. Addresses are $ and four hex digits.`;
|
|
16
16
|
|
|
17
17
|
async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }> {
|
|
@@ -22,7 +22,7 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
22
22
|
const result = await decodeBasic({ program });
|
|
23
23
|
if (!result.decoded) return { output: { decoded: false, reason: result.reason }, failed: true };
|
|
24
24
|
const size = statSync(resolveProjectPath(program)).size - 2;
|
|
25
|
-
const end =
|
|
25
|
+
const end = result.loadAddress + size - 1;
|
|
26
26
|
return {
|
|
27
27
|
output: {
|
|
28
28
|
decoded: true,
|
|
@@ -37,8 +37,8 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
await runScript(
|
|
40
|
-
async () => {
|
|
41
|
-
const { output, failed } = await run(
|
|
40
|
+
async (argv) => {
|
|
41
|
+
const { output, failed } = await run(argv);
|
|
42
42
|
if (failed) process.exitCode = 1;
|
|
43
43
|
return output;
|
|
44
44
|
},
|
package/skills/c64-disk/SKILL.md
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: c64-disk
|
|
3
|
-
description: Use this skill to examine a C64 disk image (D64, D71, D81 or G64)
|
|
3
|
+
description: Use this skill to examine a C64 disk image (D64, D71, D81 or G64), a T64 tape file or a CRT cartridge file. It extracts files into the project. It shows the directory, the BAM, the directory entry of a file and the sector chain of a file. It writes each bank of a cartridge as a PRG. Do not use it to run a program or to decode BASIC. Give an extracted file to the c64-basic, c64-unpacker or c64-static-analysis skill.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# c64-disk
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
9
9
|
|
|
10
|
-
Examine a C64 disk image. Extract a file from
|
|
10
|
+
Examine a C64 disk image, a T64 tape file or a CRT cartridge file. Extract a file from it into the project.
|
|
11
11
|
|
|
12
12
|
This skill does not change the disk image and does not change project knowledge.
|
|
13
13
|
|
|
14
14
|
## Inputs
|
|
15
15
|
|
|
16
|
-
- The disk image: a `.d64`, `.d71`, `.d81` or `.g64` file, relative to the project
|
|
16
|
+
- The disk image: a `.d64`, `.d71`, `.d81` or `.g64` file, relative to the project folder.
|
|
17
17
|
- For one file: the file name as the directory shows it.
|
|
18
|
-
- To extract a file: the output path, relative to the project
|
|
18
|
+
- To extract a file: the output path, relative to the project folder.
|
|
19
|
+
- A T64 tape file or a CRT cartridge file, relative to the project folder.
|
|
19
20
|
|
|
20
21
|
## Tools / execution path
|
|
21
22
|
|
|
22
|
-
The script sends
|
|
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.
|
|
23
24
|
|
|
24
25
|
```
|
|
25
26
|
node <skill>/scripts/disk.ts <action> <image> [name] [--out <file>]
|
|
@@ -28,21 +29,34 @@ node <skill>/scripts/disk.ts <action> <image> [name] [--out <file>]
|
|
|
28
29
|
| Need | Command |
|
|
29
30
|
|---|---|
|
|
30
31
|
| The disk name, the free blocks and the files | `directory original/game.d64` |
|
|
31
|
-
| The free and used sectors of each track | `bam original/game.d64` |
|
|
32
|
+
| The free and used sectors of each track, and the used sectors that no file uses | `bam original/game.d64` |
|
|
32
33
|
| The type, size and first block of one file | `entry original/game.d64 GAME` |
|
|
33
34
|
| The track and sector of each block of one file | `chain original/game.d64 GAME` |
|
|
34
35
|
| Extract one file | `read original/game.d64 GAME --out extracted/game.prg` |
|
|
36
|
+
| The files of a tape | `directory original/game.t64` |
|
|
37
|
+
| Extract one file of a tape | `read original/game.t64 GAME --out extracted/game.prg` |
|
|
38
|
+
| The banks of a cartridge | `directory original/game.crt` |
|
|
39
|
+
| Write each bank of a cartridge as a PRG | `crt original/game.crt --out cart` |
|
|
35
40
|
|
|
36
41
|
The script prints one JSON object.
|
|
37
42
|
|
|
38
|
-
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}'`.
|
|
43
|
+
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
|
+
|
|
45
|
+
### Project folder
|
|
46
|
+
|
|
47
|
+
- 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
|
+
- 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
|
+
- 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` of c64-knowledge. `--evidence` refuses a file in `.c64-re-tools/local/`.
|
|
51
|
+
- 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
|
+
- 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`.
|
|
39
53
|
|
|
40
54
|
## Workflow
|
|
41
55
|
|
|
42
56
|
1. Run `directory`. Find the file in `entries`. Each entry has a `name`, a `type`, a size in `blocks` and the `closed` and `locked` flags.
|
|
43
57
|
2. Run `read` with an output path to extract the file. A PRG file keeps its 2-byte load address at the start.
|
|
44
58
|
3. Run `entry` or `chain` only when the disk structure is important, for example for a loader that reads sectors directly.
|
|
45
|
-
4. Run `bam` only to find used sectors that no file uses.
|
|
59
|
+
4. Run `bam` only to find used sectors that no file uses. `unreferenced` lists them. The list does not include the header, the directory and the BAM blocks. These sectors can hold data that a loader reads directly.
|
|
46
60
|
|
|
47
61
|
## Knowledge
|
|
48
62
|
|
|
@@ -55,6 +69,15 @@ Do not store:
|
|
|
55
69
|
|
|
56
70
|
A downstream skill records the meaning of the extracted content.
|
|
57
71
|
|
|
72
|
+
## Tapes and cartridges
|
|
73
|
+
|
|
74
|
+
The script reads T64 and CRT files itself. The host runtime is not necessary for them.
|
|
75
|
+
|
|
76
|
+
- T64: `directory` and `read`. Many T64 files have a wrong end address. Then the entry has `fixedSize: true`, and the size comes from the position of the next file.
|
|
77
|
+
- CRT: `directory` and `crt`. The script reads hardware types 0 (normal 8K, 16K or Ultimax), 5 (Ocean), 19 (Magic Desk) and 32 (EasyFlash). It refuses other types by name. `crt` writes `crt.json` and one PRG for each CHIP packet, for example `bank3-8000.prg`.
|
|
78
|
+
- A cartridge bank uses the same addresses as other banks. Analyze each bank PRG with c64-static-analysis and `--space bank3`, and record its knowledge in space `bank3`.
|
|
79
|
+
- TAP: a TAP file holds tape signals, and turbo loaders have their own formats. The script does not read it. Start it with the c64-emulator skill, or capture its parts with `loads` of the c64-unpacker skill.
|
|
80
|
+
|
|
58
81
|
## Result
|
|
59
82
|
|
|
60
83
|
The directory, the BAM, an entry or a sector chain as JSON. Or the extracted file in the project at the output path.
|
|
@@ -65,8 +88,7 @@ The exit status is 0 for a result with the file. The exit status is 1 for `found
|
|
|
65
88
|
|
|
66
89
|
- `found: false`: the image has no file with that name. This is a normal result. Read the directory again and compare the names.
|
|
67
90
|
- `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.
|
|
68
|
-
- `installation-incomplete
|
|
69
|
-
- `operation-failed` with a message about the host runtime: the host runtime does not run. Tell the user to start the host runtime with `npx -y --package=@henols/c64-re-tools@latest c64-re-tools-host`.
|
|
91
|
+
- `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.
|
|
70
92
|
- `invalid-input`: an argument or a path is wrong. Read the message and correct the call.
|
|
71
93
|
|
|
72
94
|
A file that is not `closed` was not written fully. Its data can be incomplete.
|
|
@@ -75,5 +97,6 @@ A file that is not `closed` was not written fully. Its data can be incomplete.
|
|
|
75
97
|
|
|
76
98
|
- BASIC program → c64-basic
|
|
77
99
|
- Packed program → c64-unpacker
|
|
78
|
-
- Machine code → c64-static-analysis
|
|
100
|
+
- Machine code, also a cartridge bank → c64-static-analysis
|
|
101
|
+
- A TAP file → c64-unpacker
|
|
79
102
|
- Run the program → c64-emulator
|