@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.
Files changed (126) hide show
  1. package/README.md +28 -62
  2. package/distribution/package.ts +1 -16
  3. package/distribution/plugin.ts +5 -3
  4. package/package.json +1 -1
  5. package/skills/c64-assembler/SKILL.md +25 -6
  6. package/skills/c64-assembler/scripts/assemble.ts +45 -10
  7. package/skills/c64-assembler/scripts/lib/compare.ts +48 -0
  8. package/skills/c64-basic/SKILL.md +15 -7
  9. package/skills/c64-basic/scripts/basic.ts +4 -4
  10. package/skills/c64-disk/SKILL.md +34 -11
  11. package/skills/c64-disk/scripts/disk.ts +65 -10
  12. package/skills/c64-disk/scripts/lib/containers.ts +124 -0
  13. package/skills/c64-docs/SKILL.md +138 -0
  14. package/skills/c64-docs/references/page-types.md +141 -0
  15. package/skills/c64-docs/references/style.md +43 -0
  16. package/skills/c64-docs/scripts/generate.ts +102 -0
  17. package/skills/c64-docs/scripts/lib/links.ts +155 -0
  18. package/skills/c64-docs/scripts/lib/markdown.ts +79 -0
  19. package/skills/c64-docs/scripts/lib/reference.ts +406 -0
  20. package/skills/c64-docs/scripts/lib/source-map.ts +81 -0
  21. package/skills/c64-docs/scripts/link.ts +207 -0
  22. package/skills/c64-emulator/SKILL.md +29 -8
  23. package/skills/c64-knowledge/SKILL.md +201 -15
  24. package/skills/c64-knowledge/scripts/knowledge.ts +927 -78
  25. package/skills/c64-knowledge/scripts/lib/listing.ts +162 -0
  26. package/skills/c64-memory-map/SKILL.md +14 -2
  27. package/skills/c64-memory-map/scripts/memmap.ts +15 -4
  28. package/skills/c64-provenance/SKILL.md +100 -11
  29. package/skills/c64-provenance/scripts/lib/compare.ts +152 -0
  30. package/skills/c64-provenance/scripts/lib/library.ts +584 -0
  31. package/skills/c64-provenance/scripts/lib/runtime.ts +191 -0
  32. package/skills/c64-provenance/scripts/lib/separate.ts +140 -0
  33. package/skills/c64-provenance/scripts/provenance.ts +477 -24
  34. package/skills/c64-reverse-engineering/SKILL.md +90 -11
  35. package/skills/c64-source/SKILL.md +174 -0
  36. package/skills/c64-source/scripts/generate.ts +788 -0
  37. package/skills/c64-source/scripts/lib/loader.ts +630 -0
  38. package/skills/c64-source/scripts/lib/source.ts +1121 -0
  39. package/skills/c64-static-analysis/SKILL.md +53 -13
  40. package/skills/c64-static-analysis/scripts/analyze.ts +260 -31
  41. package/skills/c64-static-analysis/scripts/lib/coverage.ts +72 -0
  42. package/skills/c64-static-analysis/scripts/{findings.ts → lib/findings.ts} +11 -5
  43. package/skills/c64-static-analysis/scripts/lib/idioms.ts +158 -0
  44. package/skills/c64-static-analysis/scripts/lib/instructions.ts +105 -0
  45. package/skills/c64-static-analysis/scripts/lib/macros.ts +233 -0
  46. package/skills/c64-static-analysis/scripts/lib/pitfalls.ts +68 -0
  47. package/skills/c64-static-analysis/scripts/lib/pointers.ts +108 -0
  48. package/skills/c64-testing/SKILL.md +57 -4
  49. package/skills/c64-testing/references/scenario-format.md +50 -6
  50. package/skills/c64-testing/scripts/checklist.ts +3 -3
  51. package/skills/c64-testing/scripts/diff.ts +167 -0
  52. package/skills/c64-testing/scripts/lib/diff.ts +70 -0
  53. package/skills/c64-testing/scripts/{scenario.ts → lib/scenario.ts} +183 -152
  54. package/skills/c64-testing/scripts/run.ts +241 -0
  55. package/skills/c64-testing/scripts/test.ts +20 -10
  56. package/skills/c64-unpacker/SKILL.md +88 -21
  57. package/skills/c64-unpacker/scripts/lib/evidence.ts +207 -0
  58. package/skills/c64-unpacker/scripts/lib/loads.ts +209 -0
  59. package/skills/c64-unpacker/scripts/lib/machine.ts +80 -0
  60. package/skills/c64-unpacker/scripts/lib/stages.ts +142 -0
  61. package/skills/c64-unpacker/scripts/unpack.ts +545 -61
  62. package/src/c64.ts +240 -12
  63. package/src/cli/cleanup.ts +1 -1
  64. package/src/cli/main.ts +14 -8
  65. package/src/cli/status.ts +105 -11
  66. package/src/graphics.ts +158 -0
  67. package/src/host/main.ts +57 -31
  68. package/src/host/server.ts +30 -47
  69. package/src/host/tools/c1541.ts +34 -5
  70. package/src/host/tools/index.ts +6 -2
  71. package/src/host/tools/petcat.ts +10 -3
  72. package/src/host/tools/status.ts +23 -6
  73. package/src/host/vice/adapter.ts +64 -20
  74. package/src/host/vice/chips.ts +11 -1
  75. package/src/host/vice/process.ts +19 -8
  76. package/src/host/vice/screen.ts +4 -44
  77. package/src/host/vice/session.ts +343 -60
  78. package/src/host-client/autostart.ts +55 -0
  79. package/src/host-client/connect.ts +14 -14
  80. package/src/host-client/coverage.ts +36 -0
  81. package/src/host-client/image.ts +63 -0
  82. package/src/host-client/steps.ts +175 -0
  83. package/src/host-client/tools.ts +7 -6
  84. package/src/host-client/transfer.ts +60 -10
  85. package/src/host-client/vice-session.ts +107 -28
  86. package/src/knowledge/conflicts.ts +161 -0
  87. package/src/knowledge/database.ts +150 -38
  88. package/src/knowledge/expression.ts +209 -0
  89. package/src/knowledge/graph.ts +104 -0
  90. package/src/knowledge/history.ts +105 -27
  91. package/src/knowledge/import.ts +141 -85
  92. package/src/knowledge/join.ts +211 -0
  93. package/src/knowledge/program.ts +258 -0
  94. package/src/knowledge/read.ts +445 -63
  95. package/src/knowledge/schema.ts +277 -65
  96. package/src/knowledge/text.ts +415 -0
  97. package/src/knowledge/write.ts +1145 -150
  98. package/src/mcp/main.ts +44 -19
  99. package/src/mcp/output.ts +39 -0
  100. package/src/mcp/server.ts +15 -4
  101. package/src/mcp/tools/debug.ts +202 -31
  102. package/src/mcp/tools/execution.ts +39 -5
  103. package/src/mcp/tools/input.ts +7 -4
  104. package/src/mcp/tools/machine.ts +14 -2
  105. package/src/mcp/tools/media.ts +44 -16
  106. package/src/mcp/tools/memory.ts +68 -12
  107. package/src/mcp/tools/video.ts +20 -9
  108. package/src/native/discover.ts +44 -6
  109. package/src/native/ghidra/analyze.ts +2 -2
  110. package/src/native/ghidra/index.ts +19 -3
  111. package/src/native/ghidra/language/c64rt_6510.slaspec +4 -1
  112. package/src/native/ghidra/scripts/C64Export.java +33 -1
  113. package/src/native/ghidra/scripts/C64Prepare.java +7 -1
  114. package/src/native/status.ts +15 -3
  115. package/src/native/types.ts +43 -8
  116. package/src/opcodes.ts +133 -0
  117. package/src/package.ts +21 -0
  118. package/{skills/c64-memory-map/scripts → src}/platform.ts +87 -7
  119. package/src/project.ts +166 -3
  120. package/src/protocol/messages.ts +17 -10
  121. package/src/protocol/tools.ts +27 -22
  122. package/src/protocol/vice.ts +255 -33
  123. package/src/script.ts +49 -9
  124. package/src/trace.ts +55 -23
  125. package/skills/c64-provenance/scripts/compare.ts +0 -92
  126. package/skills/c64-unpacker/scripts/evidence.ts +0 -135
@@ -0,0 +1,207 @@
1
+ // c64-docs link.ts (D80): links the name mentions of the documentation to the
2
+ // source and the reference, and checks every link and image of the pages.
3
+ // With --check it writes nothing and fails when a page is not up to date.
4
+
5
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
6
+ import { join, posix } from "node:path";
7
+ import { parseArgs } from "node:util";
8
+
9
+ import { WireFailure } from "#src/host-client/tools.ts";
10
+ import { writeProjectFile } from "#src/host-client/transfer.ts";
11
+ import { KnowledgeError, openForRead } from "#src/knowledge/database.ts";
12
+ import { currentRevision, listSymbols } from "#src/knowledge/read.ts";
13
+ import { resolveProjectFile, resolveProjectPath } from "#src/project.ts";
14
+ import { runScript, UsageError } from "#src/script.ts";
15
+ import { checkLinks, linkPage, type Files, type Problem } from "./lib/links.ts";
16
+ import { GENERATED_MARK } from "./lib/reference.ts";
17
+ import { readSourceMap } from "./lib/source-map.ts";
18
+
19
+ const USAGE = `link.ts <pages>... --source <dir> [--reference <dir>] [--check]
20
+
21
+ <pages>... .md files and directories of the documentation, relative to
22
+ the project folder, for example README.md docs; link.ts reads
23
+ each file and every .md file below each directory
24
+ --source <dir> the source directory of c64-source, with its source-map.json
25
+ --reference <dir> the reference pages of generate.ts; the default is the
26
+ reference/ folder of the first directory that has one
27
+ --check write nothing; fail when a link is not up to date or a page
28
+ has a problem
29
+
30
+ A name in backticks gets a link to its line in the source. An address in
31
+ backticks after the name gets a link to the entry of the name in the reference.
32
+ The result is one JSON object.`;
33
+
34
+ const MAX_LISTED = 50;
35
+ /** A name outside the image covers the addresses up to the next name, at most this many. */
36
+ const MAX_BODY = 256;
37
+
38
+ const isDirectory = (path: string) => {
39
+ const real = resolveProjectPath(path);
40
+ return existsSync(real) && statSync(real).isDirectory();
41
+ };
42
+
43
+ function markdownFiles(directory: string): string[] {
44
+ const root = resolveProjectPath(directory);
45
+ if (!isDirectory(directory)) throw new WireFailure("not-found", `There is no directory ${directory} in the project folder.`);
46
+ const found: string[] = [];
47
+ const walk = (relative: string) => {
48
+ for (const entry of readdirSync(join(root, relative), { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
49
+ const path = posix.join(relative, entry.name);
50
+ if (entry.isDirectory()) walk(path);
51
+ else if (entry.name.endsWith(".md")) found.push(posix.join(directory, path));
52
+ }
53
+ };
54
+ walk("");
55
+ return found;
56
+ }
57
+
58
+ const files: Files = {
59
+ inside: (path) => {
60
+ try {
61
+ resolveProjectPath(path);
62
+ return true;
63
+ } catch {
64
+ return false;
65
+ }
66
+ },
67
+ exists: (path) => {
68
+ try {
69
+ resolveProjectFile(path);
70
+ return true;
71
+ } catch {
72
+ return false;
73
+ }
74
+ },
75
+ text: (path) => {
76
+ try {
77
+ const real = resolveProjectFile(path);
78
+ return statSync(real).isFile() ? readFileSync(real, "utf8") : undefined;
79
+ } catch {
80
+ return undefined;
81
+ }
82
+ },
83
+ };
84
+
85
+ function run(argv: string[]): { output: Record<string, unknown>; failed: boolean } {
86
+ const { values, positionals } = parseArgs({ args: argv, strict: true, allowPositionals: true, options: { source: { type: "string" }, reference: { type: "string" }, check: { type: "boolean" }, help: { type: "boolean" } } });
87
+ if (values.help) throw new UsageError("");
88
+ if (positionals.length === 0) throw new UsageError("give the pages: .md files and directories of the documentation, for example README.md docs");
89
+ if (values.source === undefined) throw new UsageError("give --source <dir>: the source directory of c64-source");
90
+ const given = [...new Set(positionals.map((path) => posix.normalize(path)))];
91
+ for (const [option, path] of [...given.map((path) => ["<pages>", path] as const), ["--source", values.source], ["--reference", values.reference]] as const) {
92
+ if (path === undefined) continue;
93
+ if (!files.inside(path)) throw new UsageError(`${option} must be a path relative to the project folder that stays inside it, not ${path}`);
94
+ }
95
+ for (const path of given) {
96
+ if (!path.endsWith(".md") && files.exists(path) && !isDirectory(path)) throw new UsageError(`<pages> takes .md files and directories, not ${path}`);
97
+ }
98
+ const directories = given.filter((path) => !path.endsWith(".md") || isDirectory(path));
99
+ const map = readSourceMap(values.source);
100
+ const reference = values.reference === undefined ? directories.map((path) => posix.join(path, "reference")).find((path) => isDirectory(path)) : posix.normalize(values.reference);
101
+ const problems: Problem[] = [];
102
+ const db = openForRead();
103
+ let symbols;
104
+ let revision;
105
+ try {
106
+ revision = currentRevision(db);
107
+ symbols = listSymbols(db, undefined, undefined, undefined, map.space);
108
+ } finally {
109
+ db?.close();
110
+ }
111
+ if (map.revision !== revision) {
112
+ problems.push({ page: map.pathOf("source-map.json"), line: 1, kind: "stale-reference", message: `The source is from knowledge revision ${map.revision}, and the knowledge is at ${revision}. Generate the source and the reference again, then run link.ts.` });
113
+ }
114
+ const addresses = new Map<string, number>();
115
+ for (const label of map.labels) addresses.set(label.name, label.address);
116
+ for (const symbol of symbols) addresses.set(symbol.name, symbol.address);
117
+ // A routine runs to the next routine; any other name to the next name of the knowledge, in its file, and outside the image at most MAX_BODY bytes.
118
+ const routines = symbols.filter((symbol) => symbol.kind === "routine").map((symbol) => symbol.address).sort((a, b) => a - b);
119
+ const named = [...new Set(symbols.map((symbol) => symbol.address))].sort((a, b) => a - b);
120
+ const kinds = new Map(symbols.map((symbol) => [symbol.name, symbol.kind]));
121
+ const bodyEnd = (name: string) => {
122
+ const address = addresses.get(name);
123
+ if (address === undefined) return undefined;
124
+ const next = ((kinds.get(name) === "routine" ? routines : named).find((other) => other > address) ?? 0x10000) - 1;
125
+ const file = map.lineOf(address)?.file;
126
+ if (file === undefined) return Math.min(next, address + MAX_BODY - 1);
127
+ let end = address;
128
+ while (end < next && map.lineOf(end + 1)?.file === file) end++;
129
+ return end;
130
+ };
131
+ const nameAt = new Map(symbols.map((symbol) => [symbol.address, symbol.name]));
132
+ /** The routine, else the name, whose body holds an address. */
133
+ const placeOf = (address: number) => {
134
+ for (const list of [routines, named]) {
135
+ let start: number | undefined;
136
+ for (const other of list) if (other <= address) start = other;
137
+ const name = start === undefined ? undefined : nameAt.get(start);
138
+ if (name !== undefined && (bodyEnd(name) ?? start!) >= address) return name;
139
+ }
140
+ return undefined;
141
+ };
142
+
143
+ // The entries of the reference: each <a id> that is a name.
144
+ const entries = new Map<string, string>();
145
+ const referencePages = reference === undefined ? [] : markdownFiles(reference);
146
+ for (const page of referencePages) {
147
+ const text = files.text(page)!;
148
+ const stamp = /^<!-- Generated by c64-docs from knowledge revision (\S+) /.exec(text);
149
+ if (text.startsWith(GENERATED_MARK) && stamp !== null && stamp[1] !== map.revision) {
150
+ problems.push({ page, line: 1, kind: "stale-reference", message: `${page} is from knowledge revision ${stamp[1]}, and the source from ${map.revision}. Run generate.ts again.` });
151
+ }
152
+ for (const match of text.matchAll(/<a id="([^"]+)"><\/a>/g)) if (addresses.has(match[1]!) && !entries.has(match[1]!)) entries.set(match[1]!, `${page}#${match[1]}`);
153
+ }
154
+ const inside = (path: string, directory: string | undefined) => directory !== undefined && (path === directory || path.startsWith(`${directory}/`));
155
+ const context = {
156
+ addressOf: (name: string) => addresses.get(name),
157
+ codeOf: (name: string) => {
158
+ const label = map.labelOf(name);
159
+ return label === undefined ? undefined : `${map.pathOf(label.file)}#L${label.line}`;
160
+ },
161
+ entryOf: (name: string) => entries.get(name),
162
+ bodyEnd,
163
+ placeOf,
164
+ lineOf: (address: number) => {
165
+ const line = map.lineOf(address);
166
+ return line === undefined ? undefined : `${map.pathOf(line.file)}#L${line.line}`;
167
+ },
168
+ managed: (path: string) => inside(path, posix.normalize(map.directory)) || inside(path, reference),
169
+ };
170
+
171
+ const pages = [...new Set(given.flatMap((path) => {
172
+ if (directories.includes(path)) return markdownFiles(path);
173
+ if (files.text(path) === undefined) throw new WireFailure("not-found", `There is no file ${path} in the project folder.`);
174
+ return [path];
175
+ }))];
176
+ const changedPages: string[] = [];
177
+ let links = 0;
178
+ for (const page of pages) {
179
+ const text = files.text(page)!;
180
+ // A generated page is right by its making: it is only checked.
181
+ const generated = text.startsWith(GENERATED_MARK);
182
+ const linked = generated ? { text, changed: 0, problems: [] } : linkPage(text, { page, ...context });
183
+ problems.push(...linked.problems, ...checkLinks(page, linked.text, files));
184
+ if (linked.changed === 0) continue;
185
+ links += linked.changed;
186
+ changedPages.push(page);
187
+ if (!values.check) writeProjectFile(page, Buffer.from(linked.text, "utf8"));
188
+ }
189
+ const byKind: Record<string, number> = {};
190
+ for (const problem of problems) byKind[problem.kind] = (byKind[problem.kind] ?? 0) + 1;
191
+ const output: Record<string, unknown> = {
192
+ pages: pages.length,
193
+ ...(reference === undefined ? { reference: "none: run generate.ts for links to the entries" } : { reference }),
194
+ ...(values.check ? { notUpToDate: { links, pages: changedPages } } : { linked: { links, pages: changedPages } }),
195
+ problems: { count: problems.length, byKind, listed: problems.slice(0, MAX_LISTED).map((problem) => ({ at: `${problem.page}:${problem.line}`, kind: problem.kind, message: problem.message })) },
196
+ };
197
+ return { output, failed: problems.length > 0 || (values.check === true && links > 0) };
198
+ }
199
+
200
+ await runScript(
201
+ (argv) => {
202
+ const { output, failed } = run(argv);
203
+ if (failed) process.exitCode = 1;
204
+ return output;
205
+ },
206
+ { usage: USAGE, refusals: [KnowledgeError] },
207
+ );
@@ -25,18 +25,21 @@ Use only the MCP tools. Each MCP session has its own C64. Nothing else can chang
25
25
 
26
26
  | Need | Tool |
27
27
  |---|---|
28
- | Run state, video standard, warp mode, window | `c64_status` |
28
+ | Run state, video standard, warp mode, window, version of the MCP server | `c64_status` |
29
29
  | Reset, power cycle | `c64_reset` |
30
30
  | Fast emulation | `c64_warp` |
31
31
  | Show the C64 to the user in a window, hide it again | `c64_window` |
32
32
  | Stop, start, step, step over, step out, exact frames | `c64_execution` |
33
33
  | Run until an address, a memory value or a raster position | `c64_run_until` |
34
+ | Run to a place and read the machine there in one call | `c64_run_until` with `observe` and `history` |
35
+ | Each write, read or routine entry over many frames, with the instruction and the raster line | `c64_trace` |
34
36
  | Stop on an instruction address | `c64_breakpoint` |
35
37
  | Stop on a memory read or write | `c64_watchpoint` |
36
38
  | Memory | `c64_memory_read`, `c64_memory_write`, `c64_memory_search`, `c64_memory_compare` |
37
39
  | CPU registers | `c64_registers` |
38
40
  | Code as it is in memory now | `c64_disassemble` |
39
41
  | Executed instructions, call chain | `c64_cpu_history`, `c64_backtrace` |
42
+ | Which instruction reads or writes an address | `c64_trace` with a `read` or `write` point |
40
43
  | Where the CPU spends cycles, which memory it uses | `c64_profile`, `c64_memmap` |
41
44
  | Cycle count between two points | `c64_timing` |
42
45
  | Video chip, sprites, CIAs, sound chip | `c64_vicii`, `c64_sprite`, `c64_cia`, `c64_sid` |
@@ -44,27 +47,46 @@ Use only the MCP tools. Each MCP session has its own C64. Nothing else can chang
44
47
  | Several of these from one moment | `c64_observe` |
45
48
  | Keys, joystick | `c64_keyboard`, `c64_joystick` |
46
49
  | Program and disk media | `c64_autostart`, `c64_program_load`, `c64_disk_attach` |
47
- | Machine state to go back to | `c64_snapshot` |
50
+ | Machine state to go back to, also in a later session | `c64_snapshot` (with `out` and `file`) |
48
51
 
49
52
  Each memory and register tool has a `space`. Use `c64` for the computer. Use `drive8` for the CPU of the 1541 disk drive. The memory `view` is `cpu` or `ram`. With `cpu`, you see what the CPU sees now, with ROM and I/O where the memory configuration puts them. With `ram`, you see the RAM under them.
50
53
 
54
+ For a large result, give `out` with a path in the project. `c64_memory_read`, `c64_disassemble`, `c64_cpu_history` and `c64_screen` then write the result to that file. The answer gives only the file, the number of bytes and a SHA-256. Use it for a memory dump of more than a few hundred bytes, a long listing, or a screen that the user wants to keep. `c64_memmap` action `read` and `c64_snapshot` action `save` also take `out`. Write a file that is only for your work in `.c64-re-tools/local/` of the project. Git ignores that folder.
55
+
56
+ `c64_memmap` keeps RAM, ROM and I/O apart. Use action `summary` to see which code ran. Use action `read` with `out` to keep a coverage run as a file, for example `.c64-re-tools/local/attract.bin`. Then add it to the knowledge with `coverage` of the c64-knowledge skill. Clear the map before each run that you want to keep apart.
57
+
58
+ - Only the first byte of an instruction gets execute. Thus `read` without `out` gives a new range at each instruction of code. For code, use `summary`, or `read` with `out`.
59
+ - A read on a byte right after executed code, or at a return address, can be a dummy read of the 6502. The CPU reads such a byte but does not use it.
60
+
61
+ `c64_profile` gives the routines that used the most cycles since the session started, the last `clear` or the last `c64_window`. Use action `clear` before the part of the program that you want to measure. The profiler gives the cycles to the routine that a JSR called last. Code that the CPU enters with JMP or a branch counts for that routine. After `c64_window` or a snapshot restore, the profiler of VICE keeps the routines that ran before the restore. Then `c64_profile` can give the cycles of the program to such a routine, for example `$fd50`. Do not trust the routine addresses of `c64_profile` after a restore.
62
+
63
+ ### Project folder
64
+
65
+ Paths (`path`, `file` and `out`) are relative to the project folder. A project folder holds `.c64-re-tools/`. The tools use the nearest project folder at or above the folder where the agent started. If that folder is not in a project and has projects below it, a tool with a path refuses and lists them. Then give `project` with the project folder, for example `"project": "game"`. Give the same folder as the `--project` of the skill scripts. A tool without a path does not need `project`.
66
+
51
67
  ## Workflow
52
68
 
53
69
  1. Read the project knowledge for the addresses in the question. Use the c64-knowledge skill.
54
70
  2. Get the machine state with `c64_status`.
55
71
  3. Load the subject if it is not loaded. Use `c64_autostart` to start it as a user does. Use `c64_program_load` to put a PRG in memory with no start.
56
72
  4. Set the stop condition. Use a breakpoint, a watchpoint or a `c64_run_until` target. Add a typed condition when the stop must occur only in one state. The `list` result shows the condition of each breakpoint and each watchpoint.
57
- 5. Run, step or give input. Prefer `c64_run_until` and `advance-frames` to waits. They stop at the same emulated time on every run.
73
+ 5. Run, step or give input. Prefer `c64_run_until` and `advance-frames` to waits. They stop at the same emulated time on every run. To wait for a breakpoint or a watchpoint, use `advance-frames` with a large count. `stopReason` and `pointId` in the result tell what stopped the CPU. `c64_status` also gives them.
58
74
  6. Read only the state that answers the question. To read several things from one moment, use `c64_observe`.
59
75
  7. Interpret the evidence. Keep the observed facts apart from your conclusions.
60
76
 
61
77
  Write operations need a stopped CPU. Stop the CPU with `c64_execution` action `pause` first.
62
78
 
63
- `c64_reset` with `run` false stops the CPU at the first instruction of the reset routine. The result gives that address in `pc`.
79
+ `c64_reset` with `run` false stops the CPU at the first instruction of the reset routine. The result gives that address in `pc`. A reset and `c64_autostart` set `c64_timing` to zero.
80
+
81
+ A cartridge from `c64_autostart` stays in the C64 through every reset, as on a real C64. `c64_status` gives `cartridge: true` while the C64 has it, and `cartridge: false` when it does not. `c64_autostart` of a file that is not a cartridge removes the cartridge first. `c64_autostart` also sets both joysticks to the center first. `joysticksCentred` gives the ports that it changed.
64
82
 
65
83
  Step, next and until-return work only in space `c64`. The emulator cannot stop the disk drive CPU after one instruction. To find when the drive executes an address, use `c64_run_until` with an address target in space `drive8`, or a breakpoint in space `drive8`. The drive can execute a few more instructions before it stops. The computer completes its current instruction. The `pc` in the result is the drive address where it stopped.
66
84
 
67
- `c64_keyboard` puts keys in the keyboard queue of the KERNAL. A program that reads the keyboard hardware directly does not see them. Many games do this. For such a program, use `c64_joystick`, or open the window and ask the user to press the key.
85
+ `c64_keyboard` puts keys in the keyboard queue of the KERNAL. A program that reads the keyboard hardware directly does not see them. Many games do this. The emulator has no command that presses one key of the keyboard matrix. For such a program:
86
+
87
+ - VICE 3.10 and older have a known flaw: bits 5-7 of `$DC00` and `$DC01` are always 0. Then `emulatorFlaw` in the result of `c64_keyboard` and `c64_joystick` gives the flaw. With this flaw, a wait for any key that reads all keyboard columns at one time ends without a key. A wait for one specific key, or a compare of the full byte of a joystick port, never ends. Tell the user about the flaw. The next VICE release and the VICE nightly builds do not have it. Joystick port 1 uses the same lines as the keyboard.
88
+ - For one specific key, for example F1 or RUN/STOP, open the window and ask the user to press the key.
89
+ - Do not change the program to make it accept a key.
68
90
 
69
91
  Before a risky experiment, save the state with `c64_snapshot`. Restore it to try again from the same point.
70
92
 
@@ -106,11 +128,10 @@ If you cannot get the machine into the necessary state, say which state you coul
106
128
 
107
129
  - `machine-state-lost` and the message says that the VICE window closed: the window closed before `c64_window` action `close`. The emulator has no window again. The machine state is the same as at the time when the window opened. The breakpoints and the watchpoints stay. Do not use results from the window time. Read the machine state again, then continue.
108
130
  - `machine-unavailable` and the message says that the VICE window closed: the session starts the emulator again. Wait a few seconds. Then do the operation again.
109
- - `machine-state-lost` with a different message: the emulator stopped and its state is gone. Do not continue with results from before. Tell the user to restart the MCP server.
131
+ - `machine-state-lost` with a different message: the connection to the host runtime was lost, and the machine state is gone. Do not continue with results from before. The next call starts a new machine when the host runtime runs. Load the program again, and set the breakpoints and the watchpoints again. If the next call gives `machine-unavailable` with a message that the host runtime does not run, obey the rule for that message.
110
132
  - `machine-unavailable` and the message names a pause or a dialog in the emulator window: this occurs only while the window is open. Do not send more operations. Tell the user to resume the emulator (Pause, Alt+P) or to close the dialog in the VICE window. Then do the operation again. The machine state does not change.
111
- - `machine-unavailable` with a different message: the host runtime does not run, or it does not answer. Tell the user to start the host runtime with `npx -y --package=@henols/c64-re-tools@latest c64-re-tools-host`.
112
133
  - `machine-running`: stop the CPU first, then try again.
113
- - `installation-incomplete`: tell the user the message. Do not try to repair the installation.
134
+ - `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. Do not try to repair the installation.
114
135
  - `c64_run_until` with `stopReason` `timeout`: the target did not occur in the frame limit. This is evidence too. Report it.
115
136
  - `c64_run_until` with `stopReason` `jam`: the CPU executed a JAM opcode and stopped. Report the address.
116
137
 
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: c64-knowledge
3
- description: Use this skill to read, write, correct and review the durable knowledge of a C64 project in .c64-re-tools/knowledge.db. It gives the knowledge at an address, and it names symbols, classifies memory regions, sets comments and records references. It also shows the history of each change, and it can revert to an earlier revision. Do not use it for live emulator state or for static analysis.
3
+ description: Use this skill to read, write, correct and review the durable knowledge of a C64 project in .c64-re-tools/knowledge/. The knowledge holds the program, its entry state and the coverage of the runs. It gives the knowledge at an address, and it names symbols, classifies memory regions, sets comments and records references. It records patches and macros for the generated source. It also shows the history of each change, and it can revert to an earlier revision. Do not use it for live emulator state or for static analysis.
4
4
  ---
5
5
 
6
6
  # c64-knowledge
7
7
 
8
8
  ## Purpose
9
9
 
10
- Read, write, correct and review the durable understanding of a C64 project. The knowledge is in `.c64-re-tools/knowledge.db` in the project directory. Git can version that file.
10
+ Read, write, correct and review the durable understanding of a C64 project. The knowledge also holds the program: its bytes, its entry state and the stages of its unpacker. It holds the coverage of the runs too. The other skills read the program and the coverage from the knowledge. The knowledge is text in `.c64-re-tools/knowledge/` in the project folder: one JSON line for each row. Commit that folder with git. The script keeps an index of the text in `.c64-re-tools/local/knowledge.db` and makes the index again when the text changes. Do not commit the index.
11
11
 
12
- This skill is the only write path for conclusions of the user and the LLM. It does not run an emulator or an analyzer.
12
+ This skill is the only write path for conclusions of the user and the LLM. It does not run an emulator or an analyzer. Write the knowledge only with the script. The script checks each write. Do not write or edit the files in `.c64-re-tools/knowledge/` or the index yourself.
13
13
 
14
14
  ## Inputs
15
15
 
@@ -18,50 +18,233 @@ One of these:
18
18
  - an address, a range or a symbol name to look up
19
19
  - text to search for
20
20
  - a change to a symbol, a region, a comment or a reference, with a short reason
21
+ - a capture of the c64-unpacker skill, a PRG, or coverage maps to put into the knowledge
21
22
  - a request to see the history or a revision.
22
23
 
23
24
  ## Tools / execution path
24
25
 
25
- Run the script in the project directory. The project directory is the directory that the harness started in.
26
-
27
26
  ```
28
27
  node <skill>/scripts/knowledge.ts <command> [arguments] [options]
29
28
  ```
30
29
 
31
- The script prints one JSON object. An error prints `{"error": {"code": ..., "message": ...}}`. Addresses are `$` and four hex digits. Quote them in a shell, for example `'$2100'`.
30
+ The script prints one JSON object. An error prints `{"error": {"code": ..., "message": ...}}`. Addresses are `$` and four hex digits. Quote them in a shell, for example `'$2100'`. A name also gives an address, for example `at update_player`. The script tries the symbols first and then the analyzer names.
31
+
32
+ A symbol is the name of an address from the user or the LLM. An analyzer name is a second name from DXA, Ghidra or an idiom, for example `FUN_17e2`. Each analyzer keeps its own name at an address. `at`, `search` and `export` show the analyzer names. The generated source uses only the symbols, unless c64-source gets `--analyzer-names`. To use an analyzer name in the source, `rename` the address with it.
32
33
 
33
34
  | Need | Command |
34
35
  |---|---|
35
36
  | Everything known at one address | `at '$2100'` |
36
37
  | Find names and comment text | `search player` |
37
38
  | Lists in a range | `symbols`, `regions`, `comments`, `references` with `--start` and `--end` |
39
+ | The names that DXA, Ghidra and the idioms gave | `symbols --analyzer` |
40
+ | Only some origins | add `--origin semantic`, `--origin analyzer` (dxa, ghidra and heuristic), or `--origin user,llm` |
41
+ | All knowledge in one file | `export --out .c64-re-tools/local/knowledge.json` |
42
+ | Put the program into the knowledge | `image .c64-re-tools/local/capture/image.json` or `image original/game.prg` |
43
+ | Add the coverage of runs | `coverage .c64-re-tools/local/runs/rooms/room0.bin ...` |
44
+ | What the knowledge holds of the program | `program` |
45
+ | The code of a range with names, comments, access and references | `listing '$0400' '$04ff'` |
38
46
  | Name the symbol at an address | `rename '$2100' update_player --kind routine` |
39
47
  | Remove a symbol | `remove-symbol '$2100'` |
40
48
  | Classify memory | `classify '$3000' '$303f' sprite` |
41
- | Make memory unknown again | `unclassify '$3000' '$303f'` |
49
+ | Classify a table of records | `classify '$3000' '$303b' bytes --element 6` |
50
+ | Many writes as one revision | `apply -` with the JSON on standard input, or `apply <file>` |
51
+ | One batch from the batches of parallel agents | `join a.json b.json --out .c64-re-tools/local/joined.json` |
52
+ | Make memory unknown, and keep the analyzers out of it | `unclassify '$3000' '$303f'` |
42
53
  | Set or remove a comment | `comment '$2100' line Reads joystick 2.` / `uncomment '$2100' line` |
43
54
  | Add or remove a reference | `reference '$2000' '$2100' call` / `unreference ...` |
44
- | History | `history '$2100'`, `history --name update_player`, `revisions`, `revision 12` |
45
- | Go back to an earlier state | `revert 12` |
46
-
47
- Symbol kinds are `label`, `routine`, `variable`, `data` and `vector`. Region types are `code`, `bytes`, `words`, `addresses`, `addresses-split`, `petscii`, `screen`, `bitmap` and `sprite`. Reference kinds are `call`, `jump`, `read`, `write` and `reference`.
55
+ | Places that a removal blocks | `suppressions` with `--start` and `--end` |
56
+ | Start a file of generated source | `module '$2000' player.asm --title 'Player movement'` / `unmodule '$2000'` |
57
+ | Write the operand of an instruction as an expression | `operand '$8079' --lo irq_handler` / `unoperand '$8079'` |
58
+ | Operand expressions | `operands` with `--start` and `--end` |
59
+ | Replace a range with code when a switch is on | `patch '$100e' '$1010' CRACK --code-file crack.asm` / `unpatch '$100e'` |
60
+ | Patches and switches | `patches` with `--switch`, and `switches` |
61
+ | Write a macro | `macro inc16 --params dst --body-file inc16.asm` / `unmacro inc16` |
62
+ | Write instructions as one use of a macro | `use '$1005' '$100a' inc16 --args ptr` / `unuse '$1005'` |
63
+ | Macros and their uses | `macros`, and `uses` with `--start`, `--end` and `--macro` |
64
+ | Files of generated source | `modules` |
65
+ | The address spaces that hold knowledge | `spaces` |
66
+ | History | `history '$2100'`, `history --name update_player`, `history --macro inc16`, `revisions`, `revision 20261015T2145` |
67
+ | Go back to an earlier state | `revert 20261015T2145` |
68
+ | Decide the conflicts after a git merge | `merge --reason 'merge of two computers'` |
69
+
70
+ A revision has an id such as `20261015T214501.123-k3f9qz`. The id starts with the date and time. A command takes the full id, or a start of the id that only one revision has. The revision `0` is the knowledge before the first revision.
71
+
72
+ Every command takes `--space`: `c64` (the default), `drive8` for code in the 1541 drive, or `bank0` to `bank255` for cartridge banks. Drive code and banks use the same addresses as the C64, so each space has its own knowledge. A name is unique inside one space. In a batch, a write can name its own `space`.
73
+
74
+ Symbol kinds are `label`, `routine`, `variable`, `data` and `vector`. Reference kinds are `call`, `jump`, `read`, `write`, `reference` and `pointer`. Only an operand expression writes a `pointer` reference.
75
+
76
+ Region types:
77
+
78
+ | Type | Meaning |
79
+ |---|---|
80
+ | `code` | Instructions. |
81
+ | `bytes`, `words`, `petscii`, `screen`, `bitmap`, `sprite` | Data of that form. |
82
+ | `charset` | Characters of a character set, 8 bytes for each character. |
83
+ | `addresses`, `addresses-split` | A table of addresses: one table of 16-bit values, or a low-byte table and then a high-byte table. |
84
+ | `rts-addresses`, `rts-addresses-split` | The same, with each value the target minus 1, for a PHA, PHA, RTS dispatch. |
85
+ | `jumps` | A table of `JMP abs` instructions. Each 3-byte entry is an entry point. It is code. |
86
+ | `workspace` | RAM that the program writes at run time. Its bytes in the image do not matter. |
87
+ | `unused` | Bytes that nothing executes or reads. |
88
+ | `dual-use` | Code at one time and data or a vector at another time. No analyzer finding conflicts with it. |
89
+
90
+ `--element` gives the bytes of one element of a table. The length of the region must be a multiple of it. The result shows `element` and `count`.
91
+
92
+ ### Project folder
93
+
94
+ - 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/`.
95
+ - 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.
96
+ - 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.
97
+ - 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/`.
98
+ - 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.
99
+ - 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`.
48
100
 
49
101
  ## Workflow
50
102
 
51
103
  Read:
52
104
 
53
105
  1. Use `at` for the addresses in the question before other work.
54
- 2. Use `history` when the current knowledge is surprising or disputed.
106
+ 2. Use `history` when the current knowledge is surprising or disputed. Each row gives the `reason` and the `evidence` of the revision that wrote it.
55
107
 
56
108
  Write:
57
109
 
58
110
  1. Read the current value with `at`. Note the `revision` in the result.
59
111
  2. Decide the change. Keep observed facts apart from your conclusions.
60
- 3. Write it with `--reason` and `--expect-revision` set to the revision you read.
112
+ 3. Write it with `--reason` and `--expect-revision` set to the revision you read. Add `--evidence` for each thing that the change rests on: `coverage:<map>`, `test:<file>`, `file:<file>` or `note:<text>`. `<map>` is a coverage map that `coverage` put into the knowledge, by its path at that time. The script keeps the SHA-256 of each file and map.
61
113
  4. Read the result. A `revision` of `null` means that nothing changed.
62
114
 
63
115
  Each write is one revision. A classification inside a region splits that region, so regions never overlap.
64
116
 
117
+ ## The program and the coverage
118
+
119
+ The knowledge holds the program, so a project needs no image or coverage files. The other skills read them from the knowledge and take no path to them.
120
+
121
+ 1. Capture a packed program with the c64-unpacker skill into `.c64-re-tools/local/`. Then put it in with `image .c64-re-tools/local/capture/image.json`. The script reads `image.bin`, `entry_state.json` and the stages next to it.
122
+ 2. For a program without a packer, drive code or a cartridge bank, put the PRG in with `image <file.prg>`. Give `--space drive8` or `--space bank0` for drive code or a bank.
123
+ 3. Write each coverage map of a run into `.c64-re-tools/local/`. Then add it with `coverage <map.bin> ...`.
124
+ 4. Read what the knowledge holds with `program`: the segments, the entry, and the coverage maps that it has.
125
+
126
+ - `image` writes only the bytes that changed. The same image again writes nothing. A new image replaces the program of the space. The history keeps the old bytes.
127
+ - `coverage` joins the maps. It keeps each address that a run executed, read or wrote. It also keeps each address that one run wrote and executed: self-modified code. A map that the knowledge has is not added again.
128
+ - The capture and the maps are not necessary after the import. Keep them in `.c64-re-tools/local/`.
129
+
130
+ For more than a few writes, use `apply -` and give the writes on standard input as one JSON list. Each write has `op` and the arguments of the command with the same name, for example:
131
+
132
+ ```json
133
+ [
134
+ { "op": "rename", "address": "$2100", "name": "update_player", "kind": "routine" },
135
+ { "op": "classify", "start": "$3000", "end": "$303b", "type": "bytes", "element": 6 },
136
+ { "op": "comment", "address": "$2100", "placement": "line", "text": "Reads joystick 2." }
137
+ ]
138
+ ```
139
+
140
+ The ops and their fields:
141
+
142
+ | op | Fields |
143
+ |---|---|
144
+ | `rename` | `address`, `name`, `kind` (optional) |
145
+ | `remove-symbol` | `address` |
146
+ | `classify` | `start`, `end`, `type`, `element` (optional) |
147
+ | `unclassify` | `start`, `end` |
148
+ | `comment` | `address`, `placement` (`line` or `side`), `text` |
149
+ | `uncomment` | `address`, `placement` |
150
+ | `reference`, `unreference` | `from`, `to`, `kind` |
151
+ | `module` | `start`, `file`, `title` (optional) |
152
+ | `unmodule` | `start` |
153
+ | `operand` | `address`, `select` (`lo`, `hi`, `value` or `expr`), `symbol` and `offset` (optional), or `expression` for `expr` |
154
+ | `unoperand` | `address` |
155
+ | `patch` | `start`, `end`, `switch`, `code` (empty for NOP only), `comment` (optional) |
156
+ | `unpatch`, `unuse` | `start` |
157
+ | `macro` | `name`, `params` (a list or a comma list), `body`, `comment` (optional) |
158
+ | `unmacro` | `name` |
159
+ | `use` | `start`, `end`, `macro`, `args` (a list or a comma list) |
160
+
161
+ A `line` comment is a line of its own above the address. A `side` comment is at the end of the line of the address. The script checks an `operand`, `patch` or `use` write against the program of the knowledge in the space of `--space`. All writes become one revision. If the knowledge refuses one write, the script writes nothing, and the error names the write.
162
+
163
+ The c64-provenance skill writes a batch with `match --out`. The batch holds the names of library routines at their addresses in this program. Read the `batch.leftOut` of its result first. Then apply the batch with the command in its `apply`.
164
+
165
+ ## Listing
166
+
167
+ `listing` reads the program and the other knowledge, and writes no knowledge. Use it in place of your own disassembler.
168
+
169
+ - `listing` gives one line for each instruction of a code region, and one `!byte` line for other bytes. A line has the name of the address, the instruction with the names of its target, and the comments. An operand expression of the knowledge replaces the operand. `analyzerNames` gives the second names. `referencesIn` and `referencesOut` count the references. When the knowledge has coverage, `access` tells if a run executed (`x`), read (`r`) or wrote (`w`) the bytes of the line, or `-` for none. `--out` writes the listing as text to a project file.
170
+
171
+ ## Batches of parallel agents
172
+
173
+ When more agents name the program at the same time, give each agent one range of addresses. Each agent writes one batch file and does not apply it. A batch of an agent has the form `{"owner": "$1000-$1fff", "ops": [...]}`. `ops` holds the writes, as for `apply`. The owner ranges must not overlap.
174
+
175
+ `join` makes one batch from the batch files. It writes no knowledge.
176
+
177
+ - At an address in the owner range of a batch, the name, the comment, the operand and the module of that batch stay. At an address that no batch owns, the first batch in the command stays.
178
+ - A batch can give an address a name that `join` replaces. Then `join` also replaces that name in the operand expressions and macro args of that batch.
179
+ - The regions of the owner come after the regions of the other batches. Thus the regions of the owner stay where they overlap.
180
+ - A name from a batch stays when another batch removes the symbol at that address.
181
+ - `choices` lists each place where the batches gave different values, with the value that stays.
182
+ - When one name or one module file is at two addresses, `join` writes nothing. `clashes` lists the addresses and the batches. Correct the batches, then do `join` again.
183
+
184
+ Then apply the joined batch with the command in `apply`.
185
+
186
+ ## Modules
187
+
188
+ A module tells the c64-source skill where a file of the generated source starts. The file runs to the next module or to the end of its segment. Each address has one module, and each file name has one module. A file name has letters, digits, underscores or hyphens, and ends in `.asm`. The c64-source skill writes `main.asm`, `loader.asm`, `constants.asm` and `macros.asm` itself. Thus a module cannot have one of these names, in upper or lower case. The title becomes the first line of the file. A `module` write without a title keeps the title that the module has. Modules have a history, and `revert` restores them.
189
+
190
+ ## Operand expressions
191
+
192
+ An operand expression tells the c64-source skill how to write the operand of one instruction. The source then has a name, not a number. When code or data changes its length, the operand follows the address that moved.
193
+
194
+ Give one selector:
195
+
196
+ | Option | Operand | Source |
197
+ |---|---|---|
198
+ | `--lo <symbol>` | The low byte of the symbol plus the offset | `lda #<irq_handler` |
199
+ | `--hi <symbol>` | The high byte of the symbol plus the offset | `lda #>(sprite_bank+$1000)` |
200
+ | `--value <symbol>` | The symbol plus the offset | `lda bar_colours-1,x` |
201
+ | `--expr <text>` | ACME expression text with names and numbers | `lda #(sprite_bank + 5*64) / 64` |
202
+
203
+ - `--offset` adds a whole number to the symbol. Write a negative offset as `--offset=-1`.
204
+ - A name is a symbol of the knowledge or a platform name of the c64-memory-map skill.
205
+ - `--expr` takes `$`, `%` and decimal numbers, names, brackets, the operators `+ - * / % << >> >>> & | XOR`, and `<`, `>`, `-` and `!` before an operand.
206
+ - The script compares the value of the expression with the operand in the program of the knowledge. If they are not equal, the script refuses the write.
207
+ - Each name in the expression gets a `pointer` reference from the instruction. Thus `at` on the named address shows the code that uses the address.
208
+ - A `rename` of a symbol changes the name in each expression. The knowledge refuses `remove-symbol` while an expression uses the symbol. Use `unoperand` first.
209
+
210
+ Write an operand expression for each immediate that is a byte of an address. Also write one for each table read through the byte before the table. The pointer candidates of the c64-static-analysis skill show many of these places.
211
+
212
+ ## Code switches
213
+
214
+ A patch is ACME code that replaces a range of the program when the build defines its switch as 1. Use patches to change the program, for example for a crack, a trainer or a debug hook. The default build does not define the switch. Thus the c64-source skill still makes a source that is identical to the program.
215
+
216
+ - A switch name is upper case, for example `CRACK` or `TRAINER`. It cannot be the name of a symbol or a platform name.
217
+ - Many patches can have the same switch. The c64-source skill builds each switch on its own. It does not build two switches together.
218
+ - The code must fit in the range. NOP ($EA) fills the bytes after the code. `--nop` gives a range of NOP only.
219
+ - `--code` gives the code as text. `--code-file` reads the code from a project file. The code can use the labels and the macros of the source, and local labels with a dot, for example `.skip`.
220
+ - The braces in the code must be pairs, because the source puts the code inside `{ }`.
221
+ - A patch cannot overlap another patch or a macro use.
222
+ - Each byte of the range must be in the program of the knowledge.
223
+ - A `patch` write without `--comment` keeps the comment that the patch has.
224
+
225
+ Build the program with a switch on with the c64-assembler skill: `--define CRACK=1`.
226
+
227
+ ## Macros
228
+
229
+ A macro writes a run of instructions as one line of the source, for example `+inc16 ptr`. Use macros to make the source shorter and easier to read.
230
+
231
+ - `macro` writes the ACME macro: its name, its params and its body. `--params` gives the params as a comma list, without a dot. The body uses each param with a dot, for example `.dst`.
232
+ - `use` writes the instructions from the start to the end as one use of a macro. `--args` gives one expression for each param, as a comma list. Each name in an arg is a symbol of the knowledge or a platform name.
233
+ - The range of a use is complete instructions of the program of the knowledge.
234
+ - A `rename` of a symbol changes the name in each arg. The knowledge refuses `remove-symbol` while an arg uses the symbol.
235
+ - The knowledge refuses `unmacro` while the macro has uses. It also refuses a change of the number of params while the macro has uses.
236
+ - A use cannot overlap a patch or another use.
237
+
238
+ The c64-source skill writes a use only when the use gives the bytes of the program. If not, it writes the instructions and gives the reason. The c64-static-analysis skill finds uses of known 6502 idioms and writes them to a batch with `--macro-batch`.
239
+
240
+ ## Removals
241
+
242
+ `unclassify` and `unreference` block the place for DXA and Ghidra. An analysis does not add a region in that range or that reference again. This applies to every removed fact, also to a fact from the LLM. `remove-symbol` does not block the address, because an analyzer name never becomes a symbol.
243
+
244
+ - `suppressions` lists the blocked places with the reason of each removal. `at` and `history` also show them.
245
+ - A `rename`, `classify` or `reference` at a blocked place ends the block there, because your own fact is at the place then.
246
+ - A `revert` to a revision before the removal also ends the block.
247
+
65
248
  ## Knowledge
66
249
 
67
250
  Write after interpretation:
@@ -83,7 +266,7 @@ The script records the origin `llm`. Use `--origin user` only when the user deci
83
266
 
84
267
  For a read: the current or historical knowledge in compact JSON.
85
268
 
86
- For a write: the new revision and the current value after the write. If the write replaces a symbol or a region of a different origin, `previous` gives that row as it was before the write. For example, an `llm` write over a `user` name gives `previous`. Read `previous` and tell the user about the change.
269
+ For a write: the new revision and the current value after the write. If the write replaces a symbol, a region or an operand expression of a different origin, `previous` gives that row as it was before the write. For example, an `llm` write over a `user` name gives `previous`. Read `previous` and tell the user about the change. For `apply`, `replaced` lists each write that replaced a row. Each item has the number of the write, its `op` and its `previous`.
87
270
 
88
271
  The exit status is 0 for a result. The exit status is 1 for an error from the knowledge, for example `conflict`. The exit status is 2 for a wrong argument.
89
272
 
@@ -92,7 +275,8 @@ The exit status is 0 for a result. The exit status is 1 for an error from the kn
92
275
  - `conflict`: the change contradicts current knowledge. For example, the name belongs to another address. Read that address and its history, then decide.
93
276
  - `stale-revision`: the knowledge changed after you read it. Read it again, then decide again.
94
277
  - `not-found`: there is nothing to remove or no such revision.
95
- - `invalid-database` or `unsupported-schema`: tell the user the message. Do not edit the file.
278
+ - `conflict` with "from a git merge": git joined the knowledge of two computers, and two rows are current where the knowledge permits one row. For example, two names are at one address. Every read shows these rows in `conflicts`. Writes stop until you run `merge`. The `merge` command keeps the newest row of each conflict and closes the other rows, in one revision. Its result lists each choice. Tell the user the choices.
279
+ - `invalid-database` or `unsupported-schema`: tell the user the message. Do not edit the knowledge files.
96
280
 
97
281
  A revert makes a new revision. The history stays complete.
98
282
 
@@ -100,3 +284,5 @@ A revert makes a new revision. The history stays complete.
100
284
 
101
285
  - Runtime evidence → c64-emulator
102
286
  - Static evidence → c64-static-analysis
287
+ - Source from the knowledge → c64-source
288
+ - Names of routines that other programs share → c64-provenance