@henols/c64-re-tools 2.0.0-rc.9 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -116
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +22 -6
- package/skills/c64-assembler/scripts/assemble.ts +42 -14
- package/skills/c64-assembler/scripts/lib/compare.ts +48 -0
- package/skills/c64-basic/SKILL.md +10 -1
- package/skills/c64-basic/scripts/basic.ts +3 -3
- package/skills/c64-disk/SKILL.md +13 -4
- package/skills/c64-disk/scripts/disk.ts +7 -7
- package/skills/c64-disk/scripts/{containers.ts → lib/containers.ts} +2 -2
- 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 +109 -0
- package/skills/c64-docs/scripts/lib/links.ts +167 -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 +239 -0
- package/skills/c64-emulator/SKILL.md +16 -5
- package/skills/c64-knowledge/SKILL.md +140 -18
- package/skills/c64-knowledge/scripts/knowledge.ts +737 -112
- package/skills/c64-knowledge/scripts/lib/listing.ts +162 -0
- package/skills/c64-memory-map/SKILL.md +11 -2
- package/skills/c64-memory-map/scripts/memmap.ts +2 -2
- package/skills/c64-provenance/SKILL.md +66 -11
- package/skills/c64-provenance/scripts/{compare.ts → lib/compare.ts} +3 -3
- package/skills/c64-provenance/scripts/lib/library.ts +584 -0
- package/skills/c64-provenance/scripts/{runtime.ts → lib/runtime.ts} +2 -2
- package/skills/c64-provenance/scripts/{separate.ts → lib/separate.ts} +3 -3
- package/skills/c64-provenance/scripts/provenance.ts +242 -40
- package/skills/c64-reverse-engineering/SKILL.md +51 -26
- package/skills/c64-source/SKILL.md +76 -28
- package/skills/c64-source/scripts/generate.ts +425 -127
- package/skills/c64-source/scripts/{loader.ts → lib/loader.ts} +84 -25
- package/skills/c64-source/scripts/lib/source.ts +1121 -0
- package/skills/c64-static-analysis/SKILL.md +35 -18
- package/skills/c64-static-analysis/scripts/analyze.ts +184 -55
- package/skills/c64-static-analysis/scripts/{coverage.ts → lib/coverage.ts} +19 -25
- package/skills/c64-static-analysis/scripts/{findings.ts → lib/findings.ts} +6 -2
- package/skills/c64-static-analysis/scripts/{idioms.ts → lib/idioms.ts} +21 -12
- package/skills/c64-static-analysis/scripts/{instructions.ts → lib/instructions.ts} +1 -1
- 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 +36 -7
- package/skills/c64-testing/references/scenario-format.md +12 -8
- 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} +45 -34
- package/skills/c64-testing/scripts/run.ts +73 -21
- package/skills/c64-testing/scripts/test.ts +5 -5
- package/skills/c64-unpacker/SKILL.md +23 -9
- package/skills/c64-unpacker/scripts/{evidence.ts → lib/evidence.ts} +1 -1
- package/skills/c64-unpacker/scripts/{loads.ts → lib/loads.ts} +4 -4
- package/skills/c64-unpacker/scripts/{machine.ts → lib/machine.ts} +4 -4
- package/skills/c64-unpacker/scripts/{stages.ts → lib/stages.ts} +17 -5
- package/skills/c64-unpacker/scripts/unpack.ts +41 -24
- package/src/c64.ts +103 -5
- package/src/cli/cleanup.ts +1 -1
- package/src/cli/main.ts +14 -5
- package/src/cli/status.ts +70 -3
- package/src/graphics.ts +158 -0
- package/src/host/main.ts +25 -5
- package/src/host/server.ts +10 -3
- package/src/host/tools/petcat.ts +1 -1
- package/src/host/tools/status.ts +1 -1
- package/src/host/vice/adapter.ts +4 -3
- package/src/host/vice/process.ts +2 -2
- package/src/host/vice/screen.ts +1 -42
- package/src/host/vice/session.ts +196 -23
- package/src/host-client/autostart.ts +1 -1
- package/src/host-client/connect.ts +1 -1
- package/src/host-client/coverage.ts +2 -29
- package/src/host-client/image.ts +66 -0
- package/src/host-client/steps.ts +9 -4
- package/src/host-client/tools.ts +1 -0
- package/src/host-client/transfer.ts +39 -11
- package/src/host-client/vice-session.ts +33 -16
- 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 +70 -22
- package/src/knowledge/import.ts +64 -55
- package/src/knowledge/join.ts +211 -0
- package/src/knowledge/program.ts +266 -0
- package/src/knowledge/read.ts +258 -27
- package/src/knowledge/schema.ts +199 -75
- package/src/knowledge/text.ts +415 -0
- package/src/knowledge/write.ts +740 -57
- package/src/mcp/main.ts +8 -2
- package/src/mcp/output.ts +15 -8
- package/src/mcp/server.ts +3 -1
- package/src/mcp/tools/debug.ts +97 -16
- package/src/mcp/tools/execution.ts +38 -5
- package/src/mcp/tools/machine.ts +11 -2
- package/src/mcp/tools/media.ts +18 -13
- package/src/mcp/tools/memory.ts +17 -8
- package/src/mcp/tools/video.ts +9 -9
- package/src/native/discover.ts +2 -2
- package/src/native/ghidra/analyze.ts +2 -2
- package/src/native/ghidra/index.ts +1 -1
- package/src/native/ghidra/language/c64rt_6510.slaspec +4 -1
- package/src/native/ghidra/scripts/C64Export.java +1 -1
- package/src/native/ghidra/scripts/C64Prepare.java +7 -1
- package/src/native/types.ts +14 -8
- package/{skills/c64-source/scripts → src}/opcodes.ts +75 -3
- package/src/package.ts +13 -3
- package/src/platform.ts +70 -7
- package/src/project.ts +180 -3
- package/src/protocol/messages.ts +1 -1
- package/src/protocol/tools.ts +2 -16
- package/src/protocol/vice.ts +124 -15
- package/src/script.ts +46 -7
- package/skills/c64-assembler/scripts/compare.ts +0 -84
- package/skills/c64-source/scripts/source.ts +0 -595
package/README.md
CHANGED
|
@@ -1,49 +1,59 @@
|
|
|
1
1
|
# c64-re-tools
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
An agent drives a real VICE emulator through an MCP server, and uses skills
|
|
8
|
-
for the rest: disk images, BASIC, static analysis with DXA and Ghidra, a project
|
|
9
|
-
knowledge database, the ACME assembler, unpacking, release comparison and
|
|
10
|
-
functional-equivalence tests.
|
|
11
|
-
|
|
12
|
-
Architecture in one line: one MCP process owns one VICE instance and is
|
|
13
|
-
VICE-only; a Host Runtime on the graphical host owns VICE and the tools that
|
|
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 in
|
|
16
|
-
`.c64-re-tools/knowledge.db`.
|
|
17
|
-
The design is in [`docs/redesign/`](docs/redesign/README.md), and
|
|
18
|
-
[`docs/plan.md`](docs/plan.md) tracks the implementation.
|
|
3
|
+
Let an AI agent work on Commodore 64 programs. The agent drives a real VICE
|
|
4
|
+
emulator and runs ACME, DXA and Ghidra. Use it to build new C64 apps and games,
|
|
5
|
+
and to reverse engineer old ones.
|
|
19
6
|
|
|
20
|
-
|
|
7
|
+
It works with Claude Code, Codex, pi, opencode, Gemini, Copilot, Cursor and
|
|
8
|
+
Windsurf.
|
|
21
9
|
|
|
22
|
-
|
|
10
|
+
## What it can do
|
|
23
11
|
|
|
24
|
-
|
|
12
|
+
**Build new apps and games**
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
14
|
+
- Assemble 6510 source with ACME, with errors by file and line, and symbols.
|
|
15
|
+
- Load and run the program in VICE. Set breakpoints and watchpoints, step, and read the CPU history and the call stack.
|
|
16
|
+
- Read, write, search and compare memory, and disassemble it.
|
|
17
|
+
- Show the VIC-II, sprite, SID and CIA state, and capture the screen.
|
|
18
|
+
- Type on the keyboard and move the joystick.
|
|
19
|
+
- Count cycles, profile the code, and wait for a raster line.
|
|
20
|
+
- Run many steps in one go and keep screens, snapshots and coverage as files.
|
|
21
|
+
- Tell what any address or register value means on the C64.
|
|
22
|
+
|
|
23
|
+
**Reverse engineer existing programs**
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
- Read D64, D71, D81, G64, T64 and CRT images, list BASIC and find the SYS address.
|
|
26
|
+
- Find packers and capture the unpacked program from the emulator.
|
|
27
|
+
- Analyse the code with DXA and Ghidra. The toolkit brings its own NMOS 6510 language for Ghidra, with all 256 opcodes.
|
|
28
|
+
- Keep names, regions and comments as text in the project, ready for git.
|
|
29
|
+
- Compare releases, and find routines that several programs share.
|
|
30
|
+
- Generate ACME source that rebuilds the program byte for byte, and its documentation.
|
|
31
|
+
- Test that a rebuild behaves as the original: PASS, FAIL or INCONCLUSIVE.
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
VICE and the Host Runtime run on your computer, where VICE can open a window.
|
|
36
|
+
The agent runs in your project, preferably in a dev container. It reaches VICE
|
|
37
|
+
through an MCP server, and it runs ACME, DXA and Ghidra through its skills.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
You need Node.js 24 or newer and VICE 3.9 or later (`x64sc`, `c1541`, `petcat`)
|
|
42
|
+
with its ROM files on the host. Use the latest VICE release.
|
|
43
|
+
|
|
44
|
+
Start the Host Runtime on the host, and leave it running:
|
|
33
45
|
|
|
34
46
|
```
|
|
35
|
-
|
|
36
|
-
npx -y @henols/c64-re-tools@latest install --target claude
|
|
37
|
-
claude
|
|
47
|
+
npx -y --package=@henols/c64-re-tools@latest c64-re-tools-host
|
|
38
48
|
```
|
|
39
49
|
|
|
40
|
-
`
|
|
41
|
-
|
|
42
|
-
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).
|
|
50
|
+
`--log-console` prints each call, and `--log-dir <directory>` writes them to a file.
|
|
43
51
|
|
|
44
52
|
### Dev container
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
This is the preferred way. The container gets Node.js 24, ACME, dxa, Java 21 and Ghidra,
|
|
55
|
+
installs the toolkit into the project when it is created, and updates it at
|
|
56
|
+
each start. Add to `.devcontainer/devcontainer.json`:
|
|
47
57
|
|
|
48
58
|
```json
|
|
49
59
|
"features": {
|
|
@@ -52,127 +62,98 @@ The agent runs in the container, VICE and the Host Runtime on the host. In `.dev
|
|
|
52
62
|
"runArgs": ["--add-host=host.docker.internal:host-gateway"]
|
|
53
63
|
```
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
`target` is the harness, or a comma list such as `claude,codex`. The Feature
|
|
66
|
+
needs a Debian or Ubuntu image. `runArgs` is for Docker on Linux.
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
### Without a container
|
|
58
69
|
|
|
59
|
-
|
|
70
|
+
Install ACME, dxa and Ghidra as in [docs/install-tools.md](docs/install-tools.md).
|
|
71
|
+
Then, in your project:
|
|
60
72
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| `C64RT_C1541`, `C64RT_PETCAT` | host | Full path of the tool when it is not on `PATH`. |
|
|
65
|
-
| `C64RT_ACME`, `C64RT_DXA` | skills, CLI | Full path of the tool when it is not on `PATH`. |
|
|
66
|
-
| `C64RT_GHIDRA` | skills, CLI | The Ghidra installation directory, when `analyzeHeadless` is not on `PATH`. |
|
|
67
|
-
| `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. |
|
|
68
|
-
| `C64RT_VIDEO` | MCP | `pal` (default) or `ntsc`, fixed for the life of the MCP process. |
|
|
69
|
-
| `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. |
|
|
73
|
+
```
|
|
74
|
+
npx -y @henols/c64-re-tools@latest install --target claude
|
|
75
|
+
```
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
and settings stay unchanged.
|
|
77
|
+
`--target` is `claude`, `codex`, `pi`, `opencode`, `gemini`, `copilot`, `cursor`
|
|
78
|
+
or `windsurf`. Without it, `install` installs for all of them.
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
Check what works:
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
```
|
|
83
|
+
npx -y @henols/c64-re-tools@latest status
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Use it
|
|
81
87
|
|
|
82
|
-
|
|
88
|
+
Start the agent in the project, for example `claude`, and ask for what you want:
|
|
83
89
|
|
|
84
90
|
```
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
Write a game in ACME where a sprite moves with the joystick and the border
|
|
92
|
+
flashes when it hits the edge. Build it, run it, and show me the screen.
|
|
87
93
|
|
|
88
|
-
|
|
94
|
+
The music stops when the title screen starts. Find out why.
|
|
89
95
|
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
Reverse engineer original/game.d64. Find how it starts, unpack it if it is
|
|
97
|
+
packed, and name the main routines.
|
|
92
98
|
|
|
93
|
-
|
|
99
|
+
Find the routine that draws the score, and explain how it works.
|
|
94
100
|
```
|
|
95
101
|
|
|
96
|
-
The agent keeps what it learns about
|
|
102
|
+
The agent keeps what it learns about a program as text in
|
|
103
|
+
`.c64-re-tools/knowledge/`. Commit that folder with the project. If a command
|
|
104
|
+
reports conflicts after a `git pull`, run `knowledge.ts merge`.
|
|
105
|
+
|
|
106
|
+
Several programs in one repository: install once at the root and give each
|
|
107
|
+
program its own folder. A folder with `.c64-re-tools/` is a project.
|
|
97
108
|
|
|
98
109
|
## Skills
|
|
99
110
|
|
|
100
111
|
| Skill | Use |
|
|
101
112
|
| --- | --- |
|
|
102
|
-
| c64-
|
|
103
|
-
| c64-emulator | Runs and observes the program in VICE
|
|
104
|
-
| c64-
|
|
105
|
-
| c64-
|
|
113
|
+
| c64-assembler | Builds ACME source, with errors and symbols. |
|
|
114
|
+
| c64-emulator | Runs, debugs and observes the program in VICE. |
|
|
115
|
+
| c64-memory-map | What an address or a register value means. |
|
|
116
|
+
| c64-testing | Runs scenarios: original against rebuild, or many steps on one program. |
|
|
106
117
|
| c64-disk | Directory, BAM, entries, sector chains and extraction of disk images. |
|
|
107
118
|
| c64-basic | BASIC V2 listings and the SYS/USR handoff to machine code. |
|
|
108
|
-
| c64-unpacker | Packing evidence, and capture of the unpacked program
|
|
109
|
-
| c64-
|
|
110
|
-
| c64-
|
|
111
|
-
| c64-
|
|
112
|
-
| c64-
|
|
113
|
-
| c64-
|
|
114
|
-
|
|
115
|
-
## Examples
|
|
119
|
+
| c64-unpacker | Packing evidence, and capture of the unpacked program. |
|
|
120
|
+
| c64-static-analysis | Analysis with DXA and Ghidra into the knowledge. |
|
|
121
|
+
| c64-knowledge | Symbols, regions, comments and references, with history. |
|
|
122
|
+
| c64-provenance | Compares releases, and keeps a library of shared routines. |
|
|
123
|
+
| c64-source | ACME source from the knowledge, checked byte for byte. |
|
|
124
|
+
| c64-docs | Documentation of a program from its knowledge and source. |
|
|
125
|
+
| c64-reverse-engineering | Leads an investigation and picks the next skill. |
|
|
116
126
|
|
|
117
|
-
|
|
118
|
-
that the skills run for the agent; `<skills>` is the installed skills
|
|
119
|
-
directory):
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
node <skills>/c64-disk/scripts/disk.ts directory original/game.d64
|
|
123
|
-
node <skills>/c64-disk/scripts/disk.ts read original/game.d64 GAME --out extracted/game.prg
|
|
124
|
-
node <skills>/c64-basic/scripts/basic.ts extracted/game.prg # the SYS address, e.g. $080d
|
|
125
|
-
node <skills>/c64-unpacker/scripts/unpack.ts inspect extracted/game.prg
|
|
126
|
-
node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --analyzer dxa --listing analysis/game.lst
|
|
127
|
-
node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --entry '$080d'
|
|
128
|
-
node <skills>/c64-knowledge/scripts/knowledge.ts rename '$080d' main --kind routine --reason 'entry from BASIC'
|
|
129
|
-
node <skills>/c64-static-analysis/scripts/analyze.ts extracted/game.prg --entry '$080d' --decompile '$080d'
|
|
130
|
-
node <skills>/c64-assembler/scripts/assemble.ts --source-root src --entry game.a --out build/game.prg > build/game.json
|
|
131
|
-
node <skills>/c64-testing/scripts/test.ts tests/counts.json # PASS, FAIL or INCONCLUSIVE
|
|
132
|
-
```
|
|
127
|
+
## Settings
|
|
133
128
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
`
|
|
129
|
+
| Variable | Meaning |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| `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`. |
|
|
133
|
+
| `C64RT_GHIDRA` | The Ghidra installation directory, when `analyzeHeadless` is not on `PATH`. |
|
|
134
|
+
| `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
|
+
| `C64RT_VIDEO` | `pal` (default) or `ntsc`. |
|
|
136
|
+
| `C64RT_TRACE` | A directory for a trace of a run. See [docs/debug-install.md](docs/debug-install.md). |
|
|
137
137
|
|
|
138
138
|
## Developing
|
|
139
139
|
|
|
140
|
-
To work on the toolkit itself
|
|
141
|
-
`corepack enable pnpm`). `packageManager` in `package.json` pins its version.
|
|
140
|
+
To work on the toolkit itself you need Node 24 or newer and pnpm.
|
|
142
141
|
|
|
143
142
|
```
|
|
144
143
|
pnpm install --frozen-lockfile
|
|
145
|
-
pnpm typecheck
|
|
146
|
-
pnpm test
|
|
144
|
+
pnpm typecheck
|
|
145
|
+
pnpm test
|
|
146
|
+
pnpm host # the Host Runtime from this checkout
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
integration and end-to-end tests. Node runs the TypeScript directly (type
|
|
151
|
-
stripping), so relative imports name the `.ts` file. Skill scripts import
|
|
152
|
-
`src/` as `#src/...` (the `imports` map in `package.json`); an installed skill
|
|
153
|
-
gets the `src/` modules it reaches and its own `package.json` with that map.
|
|
154
|
-
|
|
155
|
-
Tests that need real tools are opt-in. Without them they are reported as
|
|
156
|
-
skipped, never as passed:
|
|
149
|
+
Tests that need real tools are skipped unless you turn them on:
|
|
157
150
|
|
|
158
151
|
```
|
|
159
152
|
C64RT_LIVE_VICE=/usr/bin/x64sc C64RT_GHIDRA=/opt/ghidra pnpm test
|
|
160
153
|
```
|
|
161
154
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
To release, push a tag. See [docs/releasing.md](docs/releasing.md).
|
|
165
|
-
|
|
166
|
-
## Layout
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
src/ # mcp, host, host-client, native, knowledge, cli; unit tests beside the source
|
|
170
|
-
skills/ # the skills: SKILL.md, scripts (TypeScript) and references
|
|
171
|
-
distribution/ # the AP SDK plugin definition: skills with the src/ modules they reach
|
|
172
|
-
test/ # fixtures, integration and end-to-end tests
|
|
173
|
-
docs/redesign/ # the design
|
|
174
|
-
```
|
|
155
|
+
To release, see [docs/releasing.md](docs/releasing.md).
|
|
175
156
|
|
|
176
157
|
## License
|
|
177
158
|
|
|
178
|
-
MIT. See [
|
|
159
|
+
MIT. See [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -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,20 +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
|
|
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. |
|
|
35
37
|
|
|
36
38
|
The script copies the full source root. Put all files that the program includes under it. Version control directories stay out.
|
|
37
39
|
|
|
38
|
-
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`.
|
|
39
50
|
|
|
40
51
|
## Workflow
|
|
41
52
|
|
|
42
53
|
1. Find the source root and the entry source. The entry source sets its own start address with `* = $0801` or similar.
|
|
43
54
|
2. Run the script.
|
|
44
|
-
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.
|
|
45
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.
|
|
46
60
|
5. To run the program, give the PRG path to the c64-emulator skill.
|
|
47
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
|
+
|
|
48
64
|
## Knowledge
|
|
49
65
|
|
|
50
66
|
Automatic write:
|
|
@@ -60,7 +76,7 @@ A symbol from the rebuilt source can give the address of a routine in the rebuil
|
|
|
60
76
|
|
|
61
77
|
The PRG in the project at the output path, with its load range and symbols. Or the source diagnostics when the assembly failed.
|
|
62
78
|
|
|
63
|
-
With `--compare`, `compare` gives `identical` and one check for each segment of the
|
|
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.
|
|
64
80
|
|
|
65
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.
|
|
66
82
|
|
|
@@ -12,20 +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 "./compare.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";
|
|
16
19
|
|
|
17
20
|
const USAGE = `assemble.ts --source-root <dir> --entry <file> --out <file.prg> [options]
|
|
18
21
|
|
|
19
|
-
--source-root <dir> project
|
|
22
|
+
--source-root <dir> folder in the project to assemble from; all of it is sent
|
|
20
23
|
--entry <file> the main source file, relative to the source root
|
|
21
24
|
--out <file.prg> where to write the program, relative to the project
|
|
22
25
|
--include <dir> include directory relative to the source root (repeatable)
|
|
23
26
|
--define NAME=VALUE assembler symbol: an integer ($hex allowed) or true/false (repeatable)
|
|
24
27
|
--set-pc <address> start address when the source sets none, for example '$0801'
|
|
25
|
-
--compare
|
|
26
|
-
|
|
28
|
+
--compare check the program byte by byte against the program in the
|
|
29
|
+
knowledge, 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
|
|
27
35
|
|
|
28
|
-
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.`;
|
|
29
37
|
|
|
30
38
|
function parseDefine(text: string): [string, number | boolean] {
|
|
31
39
|
const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.+)$/.exec(text);
|
|
@@ -48,7 +56,10 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
48
56
|
include: { type: "string", multiple: true },
|
|
49
57
|
define: { type: "string", multiple: true },
|
|
50
58
|
"set-pc": { type: "string" },
|
|
51
|
-
compare: { type: "
|
|
59
|
+
compare: { type: "boolean" },
|
|
60
|
+
space: { type: "string" },
|
|
61
|
+
plain: { type: "boolean" },
|
|
62
|
+
"all-symbols": { type: "boolean" },
|
|
52
63
|
help: { type: "boolean" },
|
|
53
64
|
},
|
|
54
65
|
});
|
|
@@ -61,7 +72,20 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
61
72
|
try {
|
|
62
73
|
outPath = resolveProjectPath(out);
|
|
63
74
|
} catch {
|
|
64
|
-
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
|
+
}
|
|
65
89
|
}
|
|
66
90
|
const defines = Object.fromEntries((values.define ?? []).map(parseDefine));
|
|
67
91
|
let setPc: number | undefined;
|
|
@@ -79,19 +103,23 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
79
103
|
const { result, attachments } = await assemble(params, tree.contents, localToolContext());
|
|
80
104
|
if (!result.assembled) return { output: { assembled: false, diagnostics: result.diagnostics }, failed: true };
|
|
81
105
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
82
|
-
writeFileSync(outPath, attachments![0]!);
|
|
106
|
+
writeFileSync(outPath, values.plain ? attachments![0]!.subarray(2) : attachments![0]!);
|
|
83
107
|
const range = result.loadRange!;
|
|
84
|
-
const compared =
|
|
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);
|
|
85
111
|
return {
|
|
86
112
|
output: {
|
|
87
113
|
assembled: true,
|
|
88
114
|
output: out,
|
|
115
|
+
...(values.plain ? { plain: true } : {}),
|
|
89
116
|
loadRange: { start: formatC64Address(range.start), end: formatC64Address(range.end), bytes: range.bytes },
|
|
90
|
-
symbols:
|
|
117
|
+
symbols: listed.map((symbol) =>
|
|
91
118
|
symbol.kind === "address"
|
|
92
119
|
? { name: symbol.name, kind: "address", address: formatC64Address(symbol.address), used: symbol.used }
|
|
93
|
-
: { 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 },
|
|
94
121
|
),
|
|
122
|
+
...(values["all-symbols"] ? {} : { unusedConstants: result.symbols!.length - listed.length }),
|
|
95
123
|
diagnostics: result.diagnostics,
|
|
96
124
|
...(compared === undefined ? {} : { compare: compared }),
|
|
97
125
|
},
|
|
@@ -101,10 +129,10 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
101
129
|
}
|
|
102
130
|
|
|
103
131
|
await runScript(
|
|
104
|
-
async () => {
|
|
105
|
-
const { output, failed } = await run(
|
|
132
|
+
async (argv) => {
|
|
133
|
+
const { output, failed } = await run(argv);
|
|
106
134
|
if (failed) process.exitCode = 1;
|
|
107
135
|
return output;
|
|
108
136
|
},
|
|
109
|
-
{ usage: USAGE },
|
|
137
|
+
{ usage: USAGE, refusals: [KnowledgeError] },
|
|
110
138
|
);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// The byte check of a build: the assembled program against the program
|
|
2
|
+
// in the knowledge, 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,6 +25,15 @@ 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.
|
|
@@ -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 }> {
|
|
@@ -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
|
@@ -13,10 +13,10 @@ 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
|
|
19
|
-
- A T64 tape file or a CRT cartridge file, 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.
|
|
20
20
|
|
|
21
21
|
## Tools / execution path
|
|
22
22
|
|
|
@@ -40,7 +40,16 @@ node <skill>/scripts/disk.ts <action> <image> [name] [--out <file>]
|
|
|
40
40
|
|
|
41
41
|
The script prints one JSON object.
|
|
42
42
|
|
|
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}'`.
|
|
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`.
|
|
44
53
|
|
|
45
54
|
## Workflow
|
|
46
55
|
|