@henols/c64-re-tools 2.0.0-rc.18 → 2.0.0-rc.19
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/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +5 -4
- package/skills/c64-assembler/scripts/assemble.ts +25 -5
- package/skills/c64-assembler/scripts/lib/compare.ts +11 -47
- package/skills/c64-basic/SKILL.md +3 -2
- package/skills/c64-disk/SKILL.md +3 -2
- package/skills/c64-docs/SKILL.md +3 -4
- package/skills/c64-docs/scripts/generate.ts +11 -19
- package/skills/c64-docs/scripts/lib/reference.ts +3 -3
- package/skills/c64-docs/scripts/lib/source-map.ts +0 -2
- package/skills/c64-emulator/SKILL.md +2 -2
- package/skills/c64-knowledge/SKILL.md +37 -19
- package/skills/c64-knowledge/scripts/knowledge.ts +138 -55
- package/skills/c64-memory-map/SKILL.md +3 -2
- package/skills/c64-provenance/SKILL.md +20 -19
- package/skills/c64-provenance/scripts/provenance.ts +67 -57
- package/skills/c64-reverse-engineering/SKILL.md +30 -28
- package/skills/c64-source/SKILL.md +33 -29
- package/skills/c64-source/scripts/generate.ts +46 -61
- package/skills/c64-static-analysis/SKILL.md +18 -18
- package/skills/c64-static-analysis/scripts/analyze.ts +33 -43
- package/skills/c64-static-analysis/scripts/lib/coverage.ts +2 -10
- package/skills/c64-static-analysis/scripts/lib/idioms.ts +4 -6
- package/skills/c64-testing/SKILL.md +10 -9
- package/skills/c64-testing/references/scenario-format.md +4 -4
- package/skills/c64-testing/scripts/lib/scenario.ts +13 -11
- package/skills/c64-unpacker/SKILL.md +9 -7
- package/src/host-client/image.ts +3 -0
- package/src/knowledge/program.ts +258 -0
- package/src/knowledge/read.ts +1 -1
- package/src/knowledge/schema.ts +46 -1
- package/src/knowledge/text.ts +25 -3
- package/src/knowledge/write.ts +43 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henols/c64-re-tools",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.19",
|
|
4
4
|
"description": "Agent-friendly Commodore 64 engineering toolkit for reverse engineering, debugging, development and testing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Henrik Olsson",
|
|
@@ -31,7 +31,7 @@ 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
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
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. |
|
|
37
37
|
|
|
@@ -43,8 +43,9 @@ The script prints one JSON object. The target CPU is the 6510. The output is a C
|
|
|
43
43
|
|
|
44
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
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
|
-
-
|
|
47
|
-
-
|
|
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.
|
|
48
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`.
|
|
49
50
|
|
|
50
51
|
## Workflow
|
|
@@ -75,7 +76,7 @@ A symbol from the rebuilt source can give the address of a routine in the rebuil
|
|
|
75
76
|
|
|
76
77
|
The PRG in the project at the output path, with its load range and symbols. Or the source diagnostics when the assembly failed.
|
|
77
78
|
|
|
78
|
-
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.
|
|
79
80
|
|
|
80
81
|
The exit status is 0 for a PRG. The exit status is 1 for `assembled: false`, for a `--compare` that is not identical, and for an error. The exit status is 2 for a wrong argument.
|
|
81
82
|
|
|
@@ -13,6 +13,9 @@ 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
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
|
|
|
@@ -22,8 +25,11 @@ const USAGE = `assemble.ts --source-root <dir> --entry <file> --out <file.prg> [
|
|
|
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 (D83), each segment; put it in with knowledge.ts
|
|
30
|
+
image of the c64-knowledge skill
|
|
31
|
+
--space <space> with --compare: the knowledge space of the program, c64 (the
|
|
32
|
+
default), drive8, or bank0 to bank255
|
|
27
33
|
--plain write the bytes only, without the 2-byte load address of a PRG
|
|
28
34
|
--all-symbols list also the constants that the source does not use
|
|
29
35
|
|
|
@@ -50,7 +56,8 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
50
56
|
include: { type: "string", multiple: true },
|
|
51
57
|
define: { type: "string", multiple: true },
|
|
52
58
|
"set-pc": { type: "string" },
|
|
53
|
-
compare: { type: "
|
|
59
|
+
compare: { type: "boolean" },
|
|
60
|
+
space: { type: "string" },
|
|
54
61
|
plain: { type: "boolean" },
|
|
55
62
|
"all-symbols": { type: "boolean" },
|
|
56
63
|
help: { type: "boolean" },
|
|
@@ -67,6 +74,19 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
67
74
|
} catch {
|
|
68
75
|
throw new UsageError(`--out must be a path relative to the project folder that stays inside it, not ${out}`);
|
|
69
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
|
+
}
|
|
89
|
+
}
|
|
70
90
|
const defines = Object.fromEntries((values.define ?? []).map(parseDefine));
|
|
71
91
|
let setPc: number | undefined;
|
|
72
92
|
if (values["set-pc"] !== undefined) {
|
|
@@ -85,7 +105,7 @@ async function run(argv: string[]): Promise<{ output: unknown; failed: boolean }
|
|
|
85
105
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
86
106
|
writeFileSync(outPath, values.plain ? attachments![0]!.subarray(2) : attachments![0]!);
|
|
87
107
|
const range = result.loadRange!;
|
|
88
|
-
const compared =
|
|
108
|
+
const compared = program === undefined ? undefined : compareBuild(attachments![0]!, program);
|
|
89
109
|
// An include file of platform names gives many constants that the program does not use.
|
|
90
110
|
const listed = result.symbols!.filter((symbol) => values["all-symbols"] || symbol.kind === "address" || symbol.used);
|
|
91
111
|
return {
|
|
@@ -114,5 +134,5 @@ await runScript(
|
|
|
114
134
|
if (failed) process.exitCode = 1;
|
|
115
135
|
return output;
|
|
116
136
|
},
|
|
117
|
-
{ usage: USAGE },
|
|
137
|
+
{ usage: USAGE, refusals: [KnowledgeError] },
|
|
118
138
|
);
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
// The byte check of a build (D47): the assembled program against the
|
|
2
|
-
//
|
|
3
|
-
// memory, segment by segment.
|
|
4
|
-
|
|
5
|
-
import { posix } from "node:path";
|
|
1
|
+
// The byte check of a build (D47): the assembled program against the program
|
|
2
|
+
// in the knowledge (D83), segment by segment.
|
|
6
3
|
|
|
7
4
|
import { formatC64Address, prgRange } from "#src/c64.ts";
|
|
8
|
-
import { readProjectFile } from "#src/host-client/transfer.ts";
|
|
9
|
-
import { UsageError } from "#src/script.ts";
|
|
10
5
|
|
|
11
6
|
export interface SegmentCheck {
|
|
12
7
|
start: string;
|
|
@@ -18,47 +13,16 @@ export interface SegmentCheck {
|
|
|
18
13
|
firstDifference?: { address: string; built: number; reference: number };
|
|
19
14
|
}
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const { bytes } = readProjectFile(path);
|
|
26
|
-
if (path.endsWith(".json")) {
|
|
27
|
-
let manifest: { image?: unknown; segments?: unknown };
|
|
28
|
-
try {
|
|
29
|
-
manifest = JSON.parse(bytes.toString("utf8")) as typeof manifest;
|
|
30
|
-
} catch (error) {
|
|
31
|
-
throw new UsageError(`--compare ${path} is not JSON: ${(error as Error).message}`);
|
|
32
|
-
}
|
|
33
|
-
const segments = manifest.segments;
|
|
34
|
-
if (typeof manifest.image !== "string" || !Array.isArray(segments) || segments.length === 0) throw new UsageError(`--compare ${path} is not an image.json: it needs image and segments`);
|
|
35
|
-
const image = readProjectFile(posix.join(posix.dirname(path), manifest.image)).bytes;
|
|
36
|
-
if (image.length !== FULL) throw new UsageError(`the image of ${path} has ${image.length} bytes, not 65536`);
|
|
37
|
-
return {
|
|
38
|
-
memory: (address) => image[address],
|
|
39
|
-
segments: segments.map((segment: { start?: unknown; end?: unknown }) => {
|
|
40
|
-
const parse = (value: unknown) => (typeof value === "string" && /^\$[0-9a-f]{4}$/i.test(value) ? Number.parseInt(value.slice(1), 16) : NaN);
|
|
41
|
-
const range = { start: parse(segment.start), end: parse(segment.end) };
|
|
42
|
-
if (!(range.start <= range.end)) throw new UsageError(`--compare ${path}: a segment needs start and end as $ addresses`);
|
|
43
|
-
return range;
|
|
44
|
-
}),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
if (bytes.length === FULL && !path.endsWith(".prg")) return { memory: (address) => bytes[address], segments: [built] };
|
|
48
|
-
let range: ReturnType<typeof prgRange>;
|
|
49
|
-
try {
|
|
50
|
-
range = prgRange(bytes);
|
|
51
|
-
} catch (error) {
|
|
52
|
-
throw new UsageError(`--compare ${path}: ${(error as Error).message}`);
|
|
53
|
-
}
|
|
54
|
-
return { memory: (address) => (address >= range.start && address <= range.end ? range.body[address - range.start] : undefined), segments: [{ start: range.start, end: range.end }] };
|
|
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;
|
|
55
20
|
}
|
|
56
21
|
|
|
57
22
|
/** Compares the assembled PRG with the reference, segment by segment. */
|
|
58
|
-
export function compareBuild(prg: Uint8Array,
|
|
23
|
+
export function compareBuild(prg: Uint8Array, program: Reference): { identical: boolean; segments: SegmentCheck[] } {
|
|
59
24
|
const built = prgRange(prg);
|
|
60
|
-
const
|
|
61
|
-
const checks = segments.map((segment): SegmentCheck => {
|
|
25
|
+
const checks = program.segments.map((segment): SegmentCheck => {
|
|
62
26
|
const from = Math.max(segment.start, built.start);
|
|
63
27
|
const to = Math.min(segment.end, built.end);
|
|
64
28
|
const shown = { start: formatC64Address(segment.start), end: formatC64Address(segment.end) };
|
|
@@ -67,8 +31,8 @@ export function compareBuild(prg: Uint8Array, path: string): { against: string;
|
|
|
67
31
|
let first: SegmentCheck["firstDifference"];
|
|
68
32
|
for (let address = from; address <= to; address++) {
|
|
69
33
|
const builtByte = built.body[address - built.start]!;
|
|
70
|
-
const referenceByte =
|
|
71
|
-
if (
|
|
34
|
+
const referenceByte = program.byteAt(address);
|
|
35
|
+
if (builtByte === referenceByte) continue;
|
|
72
36
|
differences++;
|
|
73
37
|
first ??= { address: formatC64Address(address), built: builtByte, reference: referenceByte };
|
|
74
38
|
}
|
|
@@ -80,5 +44,5 @@ export function compareBuild(prg: Uint8Array, path: string): { against: string;
|
|
|
80
44
|
...(differences > 0 ? { differences, firstDifference: first! } : {}),
|
|
81
45
|
};
|
|
82
46
|
});
|
|
83
|
-
return {
|
|
47
|
+
return { identical: checks.every((check) => check.result === "identical"), segments: checks };
|
|
84
48
|
}
|
|
@@ -29,8 +29,9 @@ The script prints one JSON object. Addresses are `$` and four hex digits.
|
|
|
29
29
|
|
|
30
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
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
|
-
-
|
|
33
|
-
-
|
|
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.
|
|
34
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`.
|
|
35
36
|
|
|
36
37
|
## Workflow
|
package/skills/c64-disk/SKILL.md
CHANGED
|
@@ -46,8 +46,9 @@ File names show the characters that the C64 shows in a directory list. A byte th
|
|
|
46
46
|
|
|
47
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
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
|
-
-
|
|
50
|
-
-
|
|
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.
|
|
51
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`.
|
|
52
53
|
|
|
53
54
|
## Workflow
|
package/skills/c64-docs/SKILL.md
CHANGED
|
@@ -19,7 +19,7 @@ This skill does not change the knowledge.
|
|
|
19
19
|
## Inputs
|
|
20
20
|
|
|
21
21
|
- The source directory of c64-source, with its `source-map.json`, for example `src`. The source must be from the current knowledge revision.
|
|
22
|
-
- The
|
|
22
|
+
- The program in the knowledge. Put it in with `image` of the c64-knowledge skill. The reference decodes the tables and shows the graphics from it.
|
|
23
23
|
- The directory of the documentation, for example `docs`. The reference goes into a directory of its own, for example `docs/reference`.
|
|
24
24
|
|
|
25
25
|
## Tools / execution path
|
|
@@ -27,7 +27,7 @@ This skill does not change the knowledge.
|
|
|
27
27
|
The scripts run on this machine. They do not use the emulator or the host runtime.
|
|
28
28
|
|
|
29
29
|
```
|
|
30
|
-
node <skill>/scripts/generate.ts --source src --out docs/reference
|
|
30
|
+
node <skill>/scripts/generate.ts --source src --out docs/reference
|
|
31
31
|
node <skill>/scripts/link.ts docs --source src
|
|
32
32
|
node <skill>/scripts/link.ts docs --source src --check
|
|
33
33
|
```
|
|
@@ -36,7 +36,6 @@ node <skill>/scripts/link.ts docs --source src --check
|
|
|
36
36
|
|---|---|
|
|
37
37
|
| `--source src` | The source directory of c64-source. Both scripts read `src/source-map.json`. |
|
|
38
38
|
| `--out docs/reference` | The directory of the reference. `generate.ts` writes only there. It refuses to start when the directory holds a page that it did not write. |
|
|
39
|
-
| `--image unpacked/image.json` | The image. Without it, the tables are not decoded and the graphics page has no images. |
|
|
40
39
|
| `docs` | The directory of the documentation. `link.ts` reads every `.md` file below it. It changes only the pages that you wrote. It checks the pages of the reference. |
|
|
41
40
|
| `--reference <dir>` | The directory of the reference for `link.ts`. The default is `docs/reference` when it exists. |
|
|
42
41
|
| `--check` | `link.ts` writes nothing. It fails when a link is not up to date or a page has a problem. |
|
|
@@ -109,7 +108,7 @@ Do not store:
|
|
|
109
108
|
|
|
110
109
|
- `out`, `index` and `revision`.
|
|
111
110
|
- `pages`, `entries`, `tables`, `images` and `edges`: the counts of the reference.
|
|
112
|
-
- `
|
|
111
|
+
- `program`: `the knowledge`, or a note when the knowledge has no program. Without the program, the tables are not decoded and the graphics page has no images.
|
|
113
112
|
|
|
114
113
|
`link.ts` gives one JSON object:
|
|
115
114
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// c64-docs generate.ts (D80): the reference pages of a program from the
|
|
2
|
-
// knowledge, the source map of c64-source and the
|
|
2
|
+
// knowledge, the source map of c64-source and the program in the knowledge. The pages are
|
|
3
3
|
// generated and never edited; the pages of a person link to them.
|
|
4
4
|
|
|
5
5
|
import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
6
6
|
import { join, posix } from "node:path";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
8
|
|
|
9
|
-
import { ImageError, readImage } from "#src/host-client/image.ts";
|
|
10
9
|
import { WireFailure } from "#src/host-client/tools.ts";
|
|
11
10
|
import { writeProjectFile } from "#src/host-client/transfer.ts";
|
|
12
11
|
import { conflicts } from "#src/knowledge/conflicts.ts";
|
|
@@ -16,18 +15,18 @@ import { resolveProjectPath } from "#src/project.ts";
|
|
|
16
15
|
import { runScript, UsageError } from "#src/script.ts";
|
|
17
16
|
import { buildReference, GENERATED_MARK } from "./lib/reference.ts";
|
|
18
17
|
import { readSourceMap } from "./lib/source-map.ts";
|
|
18
|
+
import { programImage, type ProgramImage } from "#src/knowledge/program.ts";
|
|
19
19
|
|
|
20
|
-
const USAGE = `generate.ts --source <dir> --out <dir>
|
|
20
|
+
const USAGE = `generate.ts --source <dir> --out <dir>
|
|
21
21
|
|
|
22
22
|
--source <dir> the source directory of c64-source, with its source-map.json,
|
|
23
23
|
relative to the project folder
|
|
24
24
|
--out <dir> the directory for the reference pages, relative to the project
|
|
25
25
|
folder, for example docs/reference; generate.ts writes only
|
|
26
26
|
there and refuses a page in it that it did not write
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
The result is one JSON object.`;
|
|
27
|
+
When the knowledge has the program of the source (knowledge.ts image of the
|
|
28
|
+
c64-knowledge skill, D83), the reference decodes the tables and shows the
|
|
29
|
+
graphics. The result is one JSON object.`;
|
|
31
30
|
|
|
32
31
|
/** The files of an earlier run, or a refusal that names a file of a person. */
|
|
33
32
|
function oldFiles(out: string): string[] {
|
|
@@ -51,7 +50,7 @@ function oldFiles(out: string): string[] {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
function run(argv: string[]): Record<string, unknown> {
|
|
54
|
-
const { values, positionals } = parseArgs({ args: argv, strict: true, allowPositionals: true, options: { source: { type: "string" }, out: { type: "string" },
|
|
53
|
+
const { values, positionals } = parseArgs({ args: argv, strict: true, allowPositionals: true, options: { source: { type: "string" }, out: { type: "string" }, help: { type: "boolean" } } });
|
|
55
54
|
if (values.help) throw new UsageError("");
|
|
56
55
|
if (positionals.length > 0) throw new UsageError(`generate.ts takes no positional arguments, not ${positionals[0]}`);
|
|
57
56
|
if (values.source === undefined) throw new UsageError("give --source <dir>: the source directory of c64-source");
|
|
@@ -64,18 +63,11 @@ function run(argv: string[]): Record<string, unknown> {
|
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
const map = readSourceMap(values.source);
|
|
67
|
-
let image: ReturnType<typeof readImage> | undefined;
|
|
68
|
-
if (values.image !== undefined) {
|
|
69
|
-
try {
|
|
70
|
-
image = readImage(values.image);
|
|
71
|
-
} catch (error) {
|
|
72
|
-
if (error instanceof ImageError) throw new UsageError(error.message);
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
66
|
const db = openForRead();
|
|
77
67
|
let knowledge;
|
|
68
|
+
let image: ProgramImage | undefined;
|
|
78
69
|
try {
|
|
70
|
+
image = programImage(db, map.space);
|
|
79
71
|
const found = conflicts(db).length;
|
|
80
72
|
if (found > 0) throw new KnowledgeError("conflict", `The knowledge has ${found} conflict${found === 1 ? "" : "s"} from a git merge. Nothing was written. Run c64-knowledge's knowledge.ts merge, then generate the source and the reference again.`);
|
|
81
73
|
const revision = currentRevision(db);
|
|
@@ -99,12 +91,12 @@ function run(argv: string[]): Record<string, unknown> {
|
|
|
99
91
|
out: posix.normalize(values.out),
|
|
100
92
|
map,
|
|
101
93
|
...knowledge,
|
|
102
|
-
...(image === undefined ? {} : { image: {
|
|
94
|
+
...(image === undefined ? {} : { image: { byteAt: image.byteAt, inImage: (address: number) => image.segments.some((segment) => address >= segment.start && address <= segment.end) } }),
|
|
103
95
|
});
|
|
104
96
|
const directory = resolveProjectPath(values.out);
|
|
105
97
|
for (const path of old) rmSync(join(directory, path), { force: true });
|
|
106
98
|
for (const file of built.files) writeProjectFile(file.path, file.bytes, { what: "--out" });
|
|
107
|
-
return { out: values.out, revision: knowledge.revision, index: posix.join(posix.normalize(values.out), "index.md"), ...built.counts,
|
|
99
|
+
return { out: values.out, revision: knowledge.revision, index: posix.join(posix.normalize(values.out), "index.md"), ...built.counts, program: image === undefined ? "none: put the program into the knowledge with knowledge.ts image to decode the tables and show the graphics" : "the knowledge" };
|
|
108
100
|
}
|
|
109
101
|
|
|
110
102
|
await runScript(run, { usage: USAGE, refusals: [KnowledgeError] });
|
|
@@ -25,7 +25,7 @@ export interface ReferenceInput {
|
|
|
25
25
|
comments: CommentRow[];
|
|
26
26
|
references: ReferenceRow[];
|
|
27
27
|
modules: ModuleRow[];
|
|
28
|
-
image?: {
|
|
28
|
+
image?: { byteAt: (address: number) => number; inImage: (address: number) => boolean };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface ReferenceFile {
|
|
@@ -299,7 +299,7 @@ export function buildReference(input: ReferenceInput): { files: ReferenceFile[];
|
|
|
299
299
|
{
|
|
300
300
|
const page = posix.join(out, "tables.md");
|
|
301
301
|
const lines = ["# Tables", "", `Each table of addresses, jumps and words, entry by entry. An RTS table holds each target minus 1; the target column gives the target. Index: [the reference](${rel(page, posix.join(out, "index.md"))}).`];
|
|
302
|
-
if (input.image === undefined) lines.push("", "
|
|
302
|
+
if (input.image === undefined) lines.push("", "Put the program into the knowledge with knowledge.ts image, then generate the reference again to decode the tables.");
|
|
303
303
|
for (const table of tables) {
|
|
304
304
|
const name = entryAt.get(table.start);
|
|
305
305
|
lines.push("", `<a id="${tableAnchor(table)}"></a>`, `## ${name === undefined ? `\`${range(table.start, table.end)}\`` : `${codeOfName(page, name.name)} \`${range(table.start, table.end)}\``}`, "", `*${table.type}*`);
|
|
@@ -329,7 +329,7 @@ export function buildReference(input: ReferenceInput): { files: ReferenceFile[];
|
|
|
329
329
|
"",
|
|
330
330
|
`Each sprite, bitmap, charset and screen region of the knowledge. The knowledge gives no display mode and no colours, so a sprite, bitmap or charset shows in hires and in multicolour, in pattern colours: black is pixel value 0, white is 1 in hires; in multicolour light grey is 1, grey is 2 and yellow is 3. Index: [the reference](${rel(page, posix.join(out, "index.md"))}).`,
|
|
331
331
|
];
|
|
332
|
-
if (input.image === undefined && graphics.length > 0) lines.push("", "
|
|
332
|
+
if (input.image === undefined && graphics.length > 0) lines.push("", "Put the program into the knowledge with knowledge.ts image, then generate the reference again to show the graphics.");
|
|
333
333
|
for (const region of graphics) {
|
|
334
334
|
const anchor = entryAt.get(region.start)?.name ?? `${region.type}_${region.start.toString(16).padStart(4, "0")}`;
|
|
335
335
|
const name = entryAt.get(region.start);
|
|
@@ -18,7 +18,6 @@ export interface SourceLine {
|
|
|
18
18
|
export interface SourceMap {
|
|
19
19
|
/** The source directory, relative to the project folder. */
|
|
20
20
|
directory: string;
|
|
21
|
-
image: string;
|
|
22
21
|
space: Space;
|
|
23
22
|
revision: string;
|
|
24
23
|
files: string[];
|
|
@@ -60,7 +59,6 @@ export function readSourceMap(directory: string): SourceMap {
|
|
|
60
59
|
const byName = new Map(labels.map((label) => [label.name, label]));
|
|
61
60
|
return {
|
|
62
61
|
directory,
|
|
63
|
-
image: String(raw.image),
|
|
64
62
|
space: String(raw.space ?? "c64") as Space,
|
|
65
63
|
revision: String(raw.revision),
|
|
66
64
|
files: (raw.files as unknown[]).map(String),
|
|
@@ -51,9 +51,9 @@ Use only the MCP tools. Each MCP session has its own C64. Nothing else can chang
|
|
|
51
51
|
|
|
52
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.
|
|
53
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`.
|
|
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
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
|
|
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
57
|
|
|
58
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
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.
|
|
@@ -1,13 +1,13 @@
|
|
|
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/. 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.
|
|
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 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.
|
|
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
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
|
|
|
@@ -18,6 +18,7 @@ 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
|
|
@@ -37,8 +38,11 @@ A symbol is the name of an address from the user or the LLM. An analyzer name is
|
|
|
37
38
|
| Lists in a range | `symbols`, `regions`, `comments`, `references` with `--start` and `--end` |
|
|
38
39
|
| The names that DXA, Ghidra and the idioms gave | `symbols --analyzer` |
|
|
39
40
|
| Only some origins | add `--origin semantic`, `--origin analyzer` (dxa, ghidra and heuristic), or `--origin user,llm` |
|
|
40
|
-
| All knowledge in one file | `export --out
|
|
41
|
-
|
|
|
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'` |
|
|
42
46
|
| Name the symbol at an address | `rename '$2100' update_player --kind routine` |
|
|
43
47
|
| Remove a symbol | `remove-symbol '$2100'` |
|
|
44
48
|
| Classify memory | `classify '$3000' '$303f' sprite` |
|
|
@@ -50,12 +54,12 @@ A symbol is the name of an address from the user or the LLM. An analyzer name is
|
|
|
50
54
|
| Add or remove a reference | `reference '$2000' '$2100' call` / `unreference ...` |
|
|
51
55
|
| Places that a removal blocks | `suppressions` with `--start` and `--end` |
|
|
52
56
|
| Start a file of generated source | `module '$2000' player.asm --title 'Player movement'` / `unmodule '$2000'` |
|
|
53
|
-
| Write the operand of an instruction as an expression | `operand '$8079' --lo irq_handler
|
|
57
|
+
| Write the operand of an instruction as an expression | `operand '$8079' --lo irq_handler` / `unoperand '$8079'` |
|
|
54
58
|
| Operand expressions | `operands` with `--start` and `--end` |
|
|
55
|
-
| Replace a range with code when a switch is on | `patch '$100e' '$1010' CRACK --code-file crack.asm
|
|
59
|
+
| Replace a range with code when a switch is on | `patch '$100e' '$1010' CRACK --code-file crack.asm` / `unpatch '$100e'` |
|
|
56
60
|
| Patches and switches | `patches` with `--switch`, and `switches` |
|
|
57
61
|
| Write a macro | `macro inc16 --params dst --body-file inc16.asm` / `unmacro inc16` |
|
|
58
|
-
| Write instructions as one use of a macro | `use '$1005' '$100a' inc16 --args ptr
|
|
62
|
+
| Write instructions as one use of a macro | `use '$1005' '$100a' inc16 --args ptr` / `unuse '$1005'` |
|
|
59
63
|
| Macros and their uses | `macros`, and `uses` with `--start`, `--end` and `--macro` |
|
|
60
64
|
| Files of generated source | `modules` |
|
|
61
65
|
| The address spaces that hold knowledge | `spaces` |
|
|
@@ -89,8 +93,9 @@ Region types:
|
|
|
89
93
|
|
|
90
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/`.
|
|
91
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.
|
|
92
|
-
-
|
|
93
|
-
-
|
|
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.
|
|
94
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`.
|
|
95
100
|
|
|
96
101
|
## Workflow
|
|
@@ -104,11 +109,24 @@ Write:
|
|
|
104
109
|
|
|
105
110
|
1. Read the current value with `at`. Note the `revision` in the result.
|
|
106
111
|
2. Decide the change. Keep observed facts apart from your conclusions.
|
|
107
|
-
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:<
|
|
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.
|
|
108
113
|
4. Read the result. A `revision` of `null` means that nothing changed.
|
|
109
114
|
|
|
110
115
|
Each write is one revision. A classification inside a region splits that region, so regions never overlap.
|
|
111
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
|
+
|
|
112
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:
|
|
113
131
|
|
|
114
132
|
```json
|
|
@@ -140,15 +158,15 @@ The ops and their fields:
|
|
|
140
158
|
| `unmacro` | `name` |
|
|
141
159
|
| `use` | `start`, `end`, `macro`, `args` (a list or a comma list) |
|
|
142
160
|
|
|
143
|
-
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.
|
|
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.
|
|
144
162
|
|
|
145
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`.
|
|
146
164
|
|
|
147
165
|
## Listing
|
|
148
166
|
|
|
149
|
-
`listing` reads the
|
|
167
|
+
`listing` reads the program and the other knowledge, and writes no knowledge. Use it in place of your own disassembler.
|
|
150
168
|
|
|
151
|
-
- `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.
|
|
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.
|
|
152
170
|
|
|
153
171
|
## Batches of parallel agents
|
|
154
172
|
|
|
@@ -163,7 +181,7 @@ When more agents name the program at the same time, give each agent one range of
|
|
|
163
181
|
- `choices` lists each place where the batches gave different values, with the value that stays.
|
|
164
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.
|
|
165
183
|
|
|
166
|
-
Then apply the joined batch with the command in `apply`.
|
|
184
|
+
Then apply the joined batch with the command in `apply`.
|
|
167
185
|
|
|
168
186
|
## Modules
|
|
169
187
|
|
|
@@ -185,7 +203,7 @@ Give one selector:
|
|
|
185
203
|
- `--offset` adds a whole number to the symbol. Write a negative offset as `--offset=-1`.
|
|
186
204
|
- A name is a symbol of the knowledge or a platform name of the c64-memory-map skill.
|
|
187
205
|
- `--expr` takes `$`, `%` and decimal numbers, names, brackets, the operators `+ - * / % << >> >>> & | XOR`, and `<`, `>`, `-` and `!` before an operand.
|
|
188
|
-
- The script compares the value of the expression with the operand in the
|
|
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.
|
|
189
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.
|
|
190
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.
|
|
191
209
|
|
|
@@ -193,7 +211,7 @@ Write an operand expression for each immediate that is a byte of an address. Als
|
|
|
193
211
|
|
|
194
212
|
## Code switches
|
|
195
213
|
|
|
196
|
-
A patch is ACME code that replaces a range of the
|
|
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.
|
|
197
215
|
|
|
198
216
|
- A switch name is upper case, for example `CRACK` or `TRAINER`. It cannot be the name of a symbol or a platform name.
|
|
199
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.
|
|
@@ -201,7 +219,7 @@ A patch is ACME code that replaces a range of the image when the build defines i
|
|
|
201
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`.
|
|
202
220
|
- The braces in the code must be pairs, because the source puts the code inside `{ }`.
|
|
203
221
|
- A patch cannot overlap another patch or a macro use.
|
|
204
|
-
-
|
|
222
|
+
- Each byte of the range must be in the program of the knowledge.
|
|
205
223
|
- A `patch` write without `--comment` keeps the comment that the patch has.
|
|
206
224
|
|
|
207
225
|
Build the program with a switch on with the c64-assembler skill: `--define CRACK=1`.
|
|
@@ -212,12 +230,12 @@ A macro writes a run of instructions as one line of the source, for example `+in
|
|
|
212
230
|
|
|
213
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`.
|
|
214
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.
|
|
215
|
-
- The range of a use is complete instructions
|
|
233
|
+
- The range of a use is complete instructions of the program of the knowledge.
|
|
216
234
|
- A `rename` of a symbol changes the name in each arg. The knowledge refuses `remove-symbol` while an arg uses the symbol.
|
|
217
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.
|
|
218
236
|
- A use cannot overlap a patch or another use.
|
|
219
237
|
|
|
220
|
-
The c64-source skill writes a use only when the use gives the bytes of the
|
|
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`.
|
|
221
239
|
|
|
222
240
|
## Removals
|
|
223
241
|
|