@henols/c64-re-tools 2.0.0-rc.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -115
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +22 -6
- package/skills/c64-assembler/scripts/assemble.ts +42 -14
- package/skills/c64-assembler/scripts/lib/compare.ts +48 -0
- package/skills/c64-basic/SKILL.md +10 -1
- package/skills/c64-basic/scripts/basic.ts +3 -3
- package/skills/c64-disk/SKILL.md +13 -4
- package/skills/c64-disk/scripts/disk.ts +7 -7
- package/skills/c64-disk/scripts/{containers.ts → lib/containers.ts} +2 -2
- package/skills/c64-docs/SKILL.md +138 -0
- package/skills/c64-docs/references/page-types.md +141 -0
- package/skills/c64-docs/references/style.md +43 -0
- package/skills/c64-docs/scripts/generate.ts +109 -0
- package/skills/c64-docs/scripts/lib/links.ts +167 -0
- package/skills/c64-docs/scripts/lib/markdown.ts +79 -0
- package/skills/c64-docs/scripts/lib/reference.ts +406 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +81 -0
- package/skills/c64-docs/scripts/link.ts +239 -0
- package/skills/c64-emulator/SKILL.md +16 -5
- package/skills/c64-knowledge/SKILL.md +140 -18
- package/skills/c64-knowledge/scripts/knowledge.ts +737 -112
- package/skills/c64-knowledge/scripts/lib/listing.ts +162 -0
- package/skills/c64-memory-map/SKILL.md +11 -2
- package/skills/c64-memory-map/scripts/memmap.ts +2 -2
- package/skills/c64-provenance/SKILL.md +66 -11
- package/skills/c64-provenance/scripts/{compare.ts → lib/compare.ts} +3 -3
- package/skills/c64-provenance/scripts/lib/library.ts +584 -0
- package/skills/c64-provenance/scripts/{runtime.ts → lib/runtime.ts} +2 -2
- package/skills/c64-provenance/scripts/{separate.ts → lib/separate.ts} +3 -3
- package/skills/c64-provenance/scripts/provenance.ts +242 -40
- package/skills/c64-reverse-engineering/SKILL.md +51 -26
- package/skills/c64-source/SKILL.md +76 -28
- package/skills/c64-source/scripts/generate.ts +425 -127
- package/skills/c64-source/scripts/{loader.ts → lib/loader.ts} +84 -25
- package/skills/c64-source/scripts/lib/source.ts +1121 -0
- package/skills/c64-static-analysis/SKILL.md +35 -18
- package/skills/c64-static-analysis/scripts/analyze.ts +184 -55
- package/skills/c64-static-analysis/scripts/{coverage.ts → lib/coverage.ts} +19 -25
- package/skills/c64-static-analysis/scripts/{findings.ts → lib/findings.ts} +6 -2
- package/skills/c64-static-analysis/scripts/{idioms.ts → lib/idioms.ts} +21 -12
- package/skills/c64-static-analysis/scripts/{instructions.ts → lib/instructions.ts} +1 -1
- package/skills/c64-static-analysis/scripts/lib/macros.ts +233 -0
- package/skills/c64-static-analysis/scripts/lib/pitfalls.ts +68 -0
- package/skills/c64-static-analysis/scripts/lib/pointers.ts +108 -0
- package/skills/c64-testing/SKILL.md +36 -7
- package/skills/c64-testing/references/scenario-format.md +12 -8
- package/skills/c64-testing/scripts/checklist.ts +3 -3
- package/skills/c64-testing/scripts/diff.ts +167 -0
- package/skills/c64-testing/scripts/lib/diff.ts +70 -0
- package/skills/c64-testing/scripts/{scenario.ts → lib/scenario.ts} +45 -34
- package/skills/c64-testing/scripts/run.ts +73 -21
- package/skills/c64-testing/scripts/test.ts +5 -5
- package/skills/c64-unpacker/SKILL.md +23 -9
- package/skills/c64-unpacker/scripts/{evidence.ts → lib/evidence.ts} +1 -1
- package/skills/c64-unpacker/scripts/{loads.ts → lib/loads.ts} +4 -4
- package/skills/c64-unpacker/scripts/{machine.ts → lib/machine.ts} +4 -4
- package/skills/c64-unpacker/scripts/{stages.ts → lib/stages.ts} +17 -5
- package/skills/c64-unpacker/scripts/unpack.ts +41 -24
- package/src/c64.ts +103 -5
- package/src/cli/cleanup.ts +1 -1
- package/src/cli/main.ts +7 -4
- package/src/cli/status.ts +70 -3
- package/src/graphics.ts +158 -0
- package/src/host/main.ts +43 -8
- package/src/host/server.ts +10 -3
- package/src/host/tools/petcat.ts +1 -1
- package/src/host/tools/status.ts +1 -1
- package/src/host/vice/adapter.ts +4 -3
- package/src/host/vice/process.ts +2 -2
- package/src/host/vice/screen.ts +1 -42
- package/src/host/vice/session.ts +196 -23
- package/src/host-client/autostart.ts +1 -1
- package/src/host-client/connect.ts +1 -1
- package/src/host-client/coverage.ts +2 -29
- package/src/host-client/image.ts +66 -0
- package/src/host-client/steps.ts +9 -4
- package/src/host-client/tools.ts +1 -0
- package/src/host-client/transfer.ts +39 -11
- package/src/host-client/vice-session.ts +33 -16
- package/src/knowledge/conflicts.ts +161 -0
- package/src/knowledge/database.ts +150 -38
- package/src/knowledge/expression.ts +209 -0
- package/src/knowledge/graph.ts +104 -0
- package/src/knowledge/history.ts +70 -22
- package/src/knowledge/import.ts +64 -55
- package/src/knowledge/join.ts +211 -0
- package/src/knowledge/program.ts +266 -0
- package/src/knowledge/read.ts +258 -27
- package/src/knowledge/schema.ts +199 -75
- package/src/knowledge/text.ts +415 -0
- package/src/knowledge/write.ts +740 -57
- package/src/mcp/main.ts +1 -1
- package/src/mcp/output.ts +15 -8
- package/src/mcp/server.ts +3 -1
- package/src/mcp/tools/debug.ts +97 -16
- package/src/mcp/tools/execution.ts +38 -5
- package/src/mcp/tools/machine.ts +11 -2
- package/src/mcp/tools/media.ts +18 -13
- package/src/mcp/tools/memory.ts +17 -8
- package/src/mcp/tools/video.ts +9 -9
- package/src/native/discover.ts +2 -2
- package/src/native/ghidra/analyze.ts +2 -2
- package/src/native/ghidra/index.ts +1 -1
- package/src/native/ghidra/language/c64rt_6510.slaspec +4 -1
- package/src/native/ghidra/scripts/C64Export.java +1 -1
- package/src/native/ghidra/scripts/C64Prepare.java +7 -1
- package/src/native/types.ts +14 -8
- package/{skills/c64-source/scripts → src}/opcodes.ts +75 -3
- package/src/platform.ts +70 -7
- package/src/project.ts +180 -3
- package/src/protocol/messages.ts +1 -1
- package/src/protocol/tools.ts +2 -16
- package/src/protocol/vice.ts +124 -15
- package/src/script.ts +46 -7
- package/src/trace.ts +55 -23
- package/skills/c64-assembler/scripts/compare.ts +0 -84
- package/skills/c64-source/scripts/source.ts +0 -595
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// One listing of the image with its knowledge: the lines of a range as
|
|
2
|
+
// ACME text with names, operand expressions and comments, the access class of
|
|
3
|
+
// the bytes from coverage files, and the references in and out. Nothing here
|
|
4
|
+
// reads files: knowledge.ts gives the lookups.
|
|
5
|
+
|
|
6
|
+
import { COVERAGE } from "#src/c64.ts";
|
|
7
|
+
import { MODE_LENGTH, OPCODES, type Mode } from "#src/opcodes.ts";
|
|
8
|
+
|
|
9
|
+
export interface ListingSource {
|
|
10
|
+
/** The image byte at an address, or undefined outside the image. */
|
|
11
|
+
byteAt: (address: number) => number | undefined;
|
|
12
|
+
/** The name of an address: a symbol, else a platform name. */
|
|
13
|
+
nameOf: (address: number) => string | undefined;
|
|
14
|
+
/** The analyzer names of an address, for reference. */
|
|
15
|
+
secondNames: (address: number) => string[];
|
|
16
|
+
/** The knowledge regions, in address order. */
|
|
17
|
+
regions: ReadonlyArray<{ start: number; end: number; type: string }>;
|
|
18
|
+
/** The line and side comments of an address. */
|
|
19
|
+
comments: (address: number) => { line: string[]; side?: string };
|
|
20
|
+
/** The operand expression of the instruction at an address, as ACME text. */
|
|
21
|
+
operand: (address: number) => string | undefined;
|
|
22
|
+
/** The references to and from an address. */
|
|
23
|
+
references: (address: number) => { in: number; out: number };
|
|
24
|
+
/** Joined coverage files, or undefined without them. */
|
|
25
|
+
coverage?: Uint8Array;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ListingLine {
|
|
29
|
+
address: number;
|
|
30
|
+
/** The bytes as hex, separated by spaces. */
|
|
31
|
+
bytes: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
analyzerNames?: string[];
|
|
34
|
+
text: string;
|
|
35
|
+
/** From the coverage: x executed, r read, w written, - none. */
|
|
36
|
+
access?: string;
|
|
37
|
+
referencesIn: number;
|
|
38
|
+
referencesOut: number;
|
|
39
|
+
lineComments?: string[];
|
|
40
|
+
comment?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const CODE = new Set(["code", "jumps"]);
|
|
44
|
+
const hex2 = (value: number) => `$${value.toString(16).padStart(2, "0")}`;
|
|
45
|
+
const hex4 = (value: number) => `$${value.toString(16).padStart(4, "0")}`;
|
|
46
|
+
const DATA_PER_LINE = 8;
|
|
47
|
+
|
|
48
|
+
/** The access class of bytes from the coverage: x, r and w as they occur, or - for none. */
|
|
49
|
+
function accessOf(coverage: Uint8Array | undefined, start: number, length: number): string | undefined {
|
|
50
|
+
if (coverage === undefined) return undefined;
|
|
51
|
+
let flags = 0;
|
|
52
|
+
for (let offset = 0; offset < length; offset++) flags |= coverage[start + offset]!;
|
|
53
|
+
const text = `${flags & (COVERAGE.ramExecute | COVERAGE.romExecute) ? "x" : ""}${flags & (COVERAGE.ramRead | COVERAGE.romRead) ? "r" : ""}${flags & COVERAGE.ramWrite ? "w" : ""}`;
|
|
54
|
+
return text === "" ? "-" : text;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The operand of an instruction in ACME syntax, with the name of its target where one exists. */
|
|
58
|
+
function operandOf(mode: Mode, address: number, low: number, high: number, nameOf: ListingSource["nameOf"]): string {
|
|
59
|
+
const word = low | (high << 8);
|
|
60
|
+
const named = (target: number, digits: 2 | 4) => nameOf(target) ?? (digits === 2 ? hex2(target) : hex4(target));
|
|
61
|
+
switch (mode) {
|
|
62
|
+
case "imp":
|
|
63
|
+
return "";
|
|
64
|
+
case "imm":
|
|
65
|
+
return `#${hex2(low)}`;
|
|
66
|
+
case "zp":
|
|
67
|
+
return named(low, 2);
|
|
68
|
+
case "zpx":
|
|
69
|
+
return `${named(low, 2)},x`;
|
|
70
|
+
case "zpy":
|
|
71
|
+
return `${named(low, 2)},y`;
|
|
72
|
+
case "abs":
|
|
73
|
+
return named(word, 4);
|
|
74
|
+
case "abx":
|
|
75
|
+
return `${named(word, 4)},x`;
|
|
76
|
+
case "aby":
|
|
77
|
+
return `${named(word, 4)},y`;
|
|
78
|
+
case "ind":
|
|
79
|
+
return `(${named(word, 4)})`;
|
|
80
|
+
case "izx":
|
|
81
|
+
return `(${named(low, 2)},x)`;
|
|
82
|
+
case "izy":
|
|
83
|
+
return `(${named(low, 2)}),y`;
|
|
84
|
+
case "rel":
|
|
85
|
+
return named((address + 2 + (low < 0x80 ? low : low - 0x100)) & 0xffff, 4);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The lines of start..end: instructions in code regions, decoded from the region start, and bytes elsewhere. */
|
|
90
|
+
export function listing(source: ListingSource, start: number, end: number): ListingLine[] {
|
|
91
|
+
const lines: ListingLine[] = [];
|
|
92
|
+
const line = (address: number, length: number, text: string): ListingLine => {
|
|
93
|
+
const bytes = Array.from({ length }, (_, offset) => source.byteAt(address + offset)!.toString(16).padStart(2, "0")).join(" ");
|
|
94
|
+
const label = source.nameOf(address);
|
|
95
|
+
const second = source.secondNames(address).filter((name) => name !== label);
|
|
96
|
+
const { line: lineComments, side } = source.comments(address);
|
|
97
|
+
const references = source.references(address);
|
|
98
|
+
const access = accessOf(source.coverage, address, length);
|
|
99
|
+
return {
|
|
100
|
+
address,
|
|
101
|
+
bytes,
|
|
102
|
+
...(label === undefined ? {} : { label }),
|
|
103
|
+
...(second.length === 0 ? {} : { analyzerNames: second }),
|
|
104
|
+
text,
|
|
105
|
+
...(access === undefined ? {} : { access }),
|
|
106
|
+
referencesIn: references.in,
|
|
107
|
+
referencesOut: references.out,
|
|
108
|
+
...(lineComments.length === 0 ? {} : { lineComments }),
|
|
109
|
+
...(side === undefined ? {} : { comment: side }),
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
const regionAt = (address: number) => source.regions.find((region) => address >= region.start && address <= region.end);
|
|
113
|
+
// Start at the code region that holds the first address, so the instructions keep their boundaries.
|
|
114
|
+
const first = regionAt(start);
|
|
115
|
+
let address = first !== undefined && CODE.has(first.type) ? first.start : start;
|
|
116
|
+
while (address <= end) {
|
|
117
|
+
const byte = source.byteAt(address);
|
|
118
|
+
if (byte === undefined) {
|
|
119
|
+
address++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const region = regionAt(address);
|
|
123
|
+
if (region !== undefined && CODE.has(region.type)) {
|
|
124
|
+
const opcode = OPCODES[byte]!;
|
|
125
|
+
const length = MODE_LENGTH[opcode.mode];
|
|
126
|
+
const bytes = Array.from({ length: length - 1 }, (_, offset) => source.byteAt(address + 1 + offset));
|
|
127
|
+
if (address + length - 1 <= region.end && bytes.every((value) => value !== undefined)) {
|
|
128
|
+
const operand = source.operand(address) ?? operandOf(opcode.mode, address, bytes[0] ?? 0, bytes[1] ?? 0, source.nameOf);
|
|
129
|
+
if (address >= start) lines.push(line(address, length, operand === "" ? opcode.mnemonic : `${opcode.mnemonic} ${operand}`));
|
|
130
|
+
address += length;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Bytes up to the next label, region edge or end of the line.
|
|
135
|
+
const values: number[] = [];
|
|
136
|
+
let at = address;
|
|
137
|
+
while (at <= end && values.length < DATA_PER_LINE && source.byteAt(at) !== undefined && (at === address || (source.nameOf(at) === undefined && regionAt(at) === region))) {
|
|
138
|
+
values.push(source.byteAt(at)!);
|
|
139
|
+
at++;
|
|
140
|
+
}
|
|
141
|
+
if (address >= start) lines.push(line(address, values.length, `!byte ${values.map(hex2).join(",")}`));
|
|
142
|
+
address = at;
|
|
143
|
+
}
|
|
144
|
+
return lines;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A listing as text, one line for each line, with its line comments above it. */
|
|
148
|
+
export function listingText(lines: readonly ListingLine[]): string {
|
|
149
|
+
const out: string[] = [];
|
|
150
|
+
for (const item of lines) {
|
|
151
|
+
for (const comment of item.lineComments ?? []) out.push(` ; ${comment}`);
|
|
152
|
+
const head = `${hex4(item.address)} ${item.bytes.padEnd(9)} ${(item.access ?? "").padEnd(3)}`;
|
|
153
|
+
const body = (item.label === undefined ? "" : `${item.label} `).padEnd(20) + item.text;
|
|
154
|
+
const notes = [
|
|
155
|
+
...(item.comment === undefined ? [] : [item.comment]),
|
|
156
|
+
...(item.referencesIn + item.referencesOut === 0 ? [] : [`refs in ${item.referencesIn} out ${item.referencesOut}`]),
|
|
157
|
+
...(item.analyzerNames === undefined ? [] : [`also ${item.analyzerNames.join(", ")}`]),
|
|
158
|
+
];
|
|
159
|
+
out.push(`${head} ${body.padEnd(44)}${notes.length === 0 ? "" : ` ; ${notes.join(" | ")}`}`.trimEnd());
|
|
160
|
+
}
|
|
161
|
+
return `${out.join("\n")}\n`;
|
|
162
|
+
}
|
|
@@ -30,14 +30,23 @@ node <skill>/scripts/memmap.ts export --out src/hw.asm
|
|
|
30
30
|
| Command | Result |
|
|
31
31
|
|---|---|
|
|
32
32
|
| `at <address> ...` | For each address: `kind` (`platform` or `application`), the area, the name and the meaning. |
|
|
33
|
-
| `decode <register> <value>` | The meaning of the value. Use `--bank` with the value of `$DD00` to get the addresses for `$D018`. |
|
|
33
|
+
| `decode <register> <value>` | The meaning of the value. Use `--bank` with the value of `$DD00` to get the addresses for `$D018`. The decoders are for `$01`, `$D010`, `$D011`, `$D015` to `$D01F`, `$D016`, `$D018`, `$DC0D`, `$DD00` and `$DD0D`. |
|
|
34
34
|
| `list <vic\|sid\|cia1\|cia2\|kernal\|system>` | All entries of that group. |
|
|
35
|
-
| `export --out <file>` | Writes all platform names to a project file as an ACME include: one
|
|
35
|
+
| `export --out <file>` | Writes all platform names to a project file as an ACME include: one line for each name, with its meaning as a comment. A zero-page name is `NAME = $xx`. Other names are `NAME = $xxxx`. |
|
|
36
36
|
|
|
37
37
|
Write an address or a value as `$` and hex digits, as a decimal number, or as `%` and binary digits. Put a `$` value in single quotes in a shell.
|
|
38
38
|
|
|
39
39
|
The script prints one JSON object.
|
|
40
40
|
|
|
41
|
+
### Project folder
|
|
42
|
+
|
|
43
|
+
- 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/`.
|
|
44
|
+
- 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.
|
|
45
|
+
- 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.
|
|
46
|
+
- 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/`.
|
|
47
|
+
- 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
|
+
- 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
|
+
|
|
41
50
|
## Workflow
|
|
42
51
|
|
|
43
52
|
1. Run `at` for the addresses in the code or the question.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The c64-memory-map script: the platform meaning of C64 addresses and of
|
|
2
2
|
// values in platform registers. Local lookup only; nothing is stored. export
|
|
3
|
-
// writes the platform names to a project file as an ACME include
|
|
3
|
+
// writes the platform names to a project file as an ACME include.
|
|
4
4
|
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
|
|
@@ -89,4 +89,4 @@ function run(argv: string[]): unknown {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
await runScript(() => run(
|
|
92
|
+
await runScript((argv) => run(argv), { usage: USAGE });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: c64-provenance
|
|
3
|
-
description: Use this skill to compare two or more releases of a C64 program, for example cracked versions from different groups. It finds the bytes where the releases differ and shows which releases agree there. It can run each release and tell how the program uses each difference.
|
|
3
|
+
description: Use this skill to compare two or more releases of a C64 program, for example cracked versions from different groups. It finds the bytes where the releases differ and shows which releases agree there. It can run each release and tell how the program uses each difference. It also gives evidence for each range of the program in the knowledge. The evidence is the unpack stage, the access, and the code that the entry does not reach. Use the result to separate the content of the application from changes that a release added. It also keeps a library of routines that several programs share. It finds them in the program of the knowledge and writes their names as a batch for c64-knowledge. Do not use it to analyze the code.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# c64-provenance
|
|
@@ -9,15 +9,18 @@ description: Use this skill to compare two or more releases of a C64 program, fo
|
|
|
9
9
|
|
|
10
10
|
Compare releases of a C64 program. Find the bytes where they differ and which releases agree. Use this to find which bytes are probably the original application and which are changes of one release.
|
|
11
11
|
|
|
12
|
+
Keep a library of routines that several programs share, for example a music driver or a decruncher. Find the routines of the library in a program, and get their names for that program.
|
|
13
|
+
|
|
12
14
|
This skill does not change project knowledge.
|
|
13
15
|
|
|
14
16
|
## Inputs
|
|
15
17
|
|
|
16
|
-
- Two or more releases, relative to the project
|
|
18
|
+
- Two or more releases, relative to the project folder. A release is a PRG file or an `image.json` from c64-unpacker capture. Extract releases from disk images with the c64-disk skill. Unpack packed releases with the c64-unpacker skill first.
|
|
17
19
|
- Optional: an address range to compare.
|
|
18
20
|
- Optional: a shift for a release that loads at a different address.
|
|
19
21
|
- Optional: a number of frames to run each release, and a steps file.
|
|
20
|
-
- For one release: the
|
|
22
|
+
- For one release: the program, the coverage and the other knowledge of the project. Put the program and the coverage in with `image` and `coverage` of the c64-knowledge skill.
|
|
23
|
+
- For the library: the program and the other knowledge of its project, and the library folder.
|
|
21
24
|
|
|
22
25
|
## Tools / execution path
|
|
23
26
|
|
|
@@ -26,8 +29,8 @@ The script compares the files locally. Only `--run` uses the host runtime: each
|
|
|
26
29
|
```
|
|
27
30
|
node <skill>/scripts/provenance.ts releases/a.prg releases/b.prg releases/c.prg
|
|
28
31
|
node <skill>/scripts/provenance.ts releases/a.prg releases/b.prg --shift 2:-256 --from '$0900' --to '$09ff'
|
|
29
|
-
node <skill>/scripts/provenance.ts releases/a.prg
|
|
30
|
-
node <skill>/scripts/provenance.ts separate
|
|
32
|
+
node <skill>/scripts/provenance.ts releases/a.prg .c64-re-tools/local/b/image.json --run 600 --steps .c64-re-tools/local/start-game.json
|
|
33
|
+
node <skill>/scripts/provenance.ts separate --out .c64-re-tools/local/separate.json
|
|
31
34
|
```
|
|
32
35
|
|
|
33
36
|
| Option | Meaning |
|
|
@@ -44,6 +47,15 @@ With `--shift`, the addresses of `segments`, `common` and `differences` are the
|
|
|
44
47
|
|
|
45
48
|
With `--run`, a PRG starts with autostart and RUN. An image starts from its bytes: the script writes the segments and the entry state from `entry_state.json` into the emulator, and starts at the entry. Watchpoints on each difference stop the run at its first access.
|
|
46
49
|
|
|
50
|
+
### Project folder
|
|
51
|
+
|
|
52
|
+
- 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/`.
|
|
53
|
+
- 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.
|
|
54
|
+
- 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.
|
|
55
|
+
- 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/`.
|
|
56
|
+
- 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.
|
|
57
|
+
- 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`.
|
|
58
|
+
|
|
47
59
|
## Workflow
|
|
48
60
|
|
|
49
61
|
1. Run the script on the releases.
|
|
@@ -65,15 +77,51 @@ With `--run`, a PRG starts with autostart and RUN. An image starts from its byte
|
|
|
65
77
|
|
|
66
78
|
## One release
|
|
67
79
|
|
|
68
|
-
Use `separate` when you have only one release. It gives evidence, not a decision:
|
|
80
|
+
Use `separate` when you have only one release. It reads the program, its stages and the coverage from the knowledge. It gives evidence, not a decision:
|
|
69
81
|
|
|
70
|
-
- `ranges`: the
|
|
71
|
-
- `access`: from the
|
|
82
|
+
- `ranges`: the program in ranges with the same stage and access. `stage` is the last unpack stage that wrote the range. The last stage is the program. An earlier stage is a depacker, an intro or a loader. Only a capture of c64-unpacker `capture` has stages. For a program from `loads` or a PRG, `noStages` tells this.
|
|
83
|
+
- `access`: from the coverage of the knowledge: `executed`, `read`, `written`, `read-written` or `untouched`. `untouched` bytes did not take part in the runs. A `read` byte right after executed code, or at a return address, can be a dummy read of the 6502 that the program does not use.
|
|
72
84
|
- `codeRegions.total`: the number of code regions in the knowledge.
|
|
73
85
|
- `codeRegions.unreachable`: code regions of the knowledge that no chain of call and jump references reaches from the entry. Run c64-static-analysis first, so that the knowledge has the code regions and the references.
|
|
74
|
-
- `outside`: RAM outside the
|
|
86
|
+
- `outside`: RAM outside the program that the program `read` or `executed` but did not write, and code it `generated`. A loader or an earlier stage put the bytes that the program did not write. A `generated` range can have gaps of two bytes or less: these are operand bytes.
|
|
87
|
+
|
|
88
|
+
Each list has `count` and `listed`. `listed` has the first ranges, at most `--max-listed`. `--out` writes all of them. Run more coverage runs for parts that are `untouched`, and add them with `coverage` of the c64-knowledge skill, before you decide.
|
|
89
|
+
|
|
90
|
+
## Library of routines
|
|
91
|
+
|
|
92
|
+
A library is a folder of routine files. Each file holds one routine: its bytes and its knowledge. Keep one library beside the project folders, for example `library/` at the root of the workspace. A library is not a project folder. Commit it with the projects.
|
|
75
93
|
|
|
76
|
-
|
|
94
|
+
`--library` is relative to the working directory, as `--project`. All other paths are relative to the project folder.
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
node <skill>/scripts/provenance.ts routine --module '$1000' --library library --project game-a
|
|
98
|
+
node <skill>/scripts/provenance.ts routine --start '$1000' --end '$1026' --name play --library library --project game-a
|
|
99
|
+
node <skill>/scripts/provenance.ts match --library library --out .c64-re-tools/local/match.json --project game-b
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
To save a routine:
|
|
103
|
+
|
|
104
|
+
1. Put the program into the knowledge, and name the routine there first. The file keeps the user and LLM names, comments, regions, references and operand expressions in the range. It also keeps the code regions of each origin.
|
|
105
|
+
2. Run `routine` with the start of a module (`--module`), or with `--start` and `--end`. A module ends at the next module or at the end of its segment.
|
|
106
|
+
- `--name` is the name of the routine and of its file. When you do not give it, the script uses the user or LLM name at the start.
|
|
107
|
+
- `--title` is the title of the routine. When you do not give it, the script uses the module title.
|
|
108
|
+
- A routine has at least 16 bytes. Its last instruction must end in the range.
|
|
109
|
+
3. Read `leftOut`. It lists the rows that the file does not keep, and why. For example, the file does not keep an operand expression that uses a name outside the routine.
|
|
110
|
+
|
|
111
|
+
To find the routines in a program:
|
|
112
|
+
|
|
113
|
+
1. Put the program into the knowledge. Then run `match`. The script examines each routine of the library at each address of the program.
|
|
114
|
+
- The opcodes and the other bytes must be equal.
|
|
115
|
+
- An address into the routine must point into the routine at its new address. `shift` is the address in the program minus the address in the library.
|
|
116
|
+
- An operand that addresses something outside the routine can be different. `differing` lists each one with its value in the library and in the program. Examine them. For example, the program can use other variables or another SID voice.
|
|
117
|
+
2. Give `--out` to get a batch for `knowledge.ts apply`. The batch has the names, regions, comments, references and operand expressions of each routine at its addresses in the program. `match` does not change the knowledge.
|
|
118
|
+
3. Read `batch.leftOut` before you apply the batch. The script leaves out a write in these conditions:
|
|
119
|
+
- The program has a user or LLM row at that place.
|
|
120
|
+
- The program uses the name at another address.
|
|
121
|
+
- A removal holds at that place.
|
|
122
|
+
- The routine occurs two times. Only the first copy gets the names.
|
|
123
|
+
- The script leaves out the name that an operand expression uses, or the expression does not give the operand in the program.
|
|
124
|
+
4. Apply the batch with the command in `apply`. Add `--project` when you are not in the project folder. The batch is one revision of the LLM. `batch.known` counts the writes that the program has already.
|
|
77
125
|
|
|
78
126
|
## Knowledge
|
|
79
127
|
|
|
@@ -82,6 +130,8 @@ Automatic write:
|
|
|
82
130
|
|
|
83
131
|
Record a confirmed conclusion as a normal comment, region or symbol with the c64-knowledge skill. Do not record the full comparison.
|
|
84
132
|
|
|
133
|
+
`match --out` writes a batch file and does not change the knowledge. Apply the batch with the c64-knowledge skill.
|
|
134
|
+
|
|
85
135
|
## Result
|
|
86
136
|
|
|
87
137
|
The differing ranges with the bytes of each release and the groups that agree, and the share of identical bytes. `common` gives the ranges where all releases have bytes. `releases` gives the segments and the `shift` of each release. With `--run`, each difference also has `runtime`, and `run` tells if each release started, or why not.
|
|
@@ -99,7 +149,11 @@ The exit status is 0 for a result. The exit status is 1 for an error. The exit s
|
|
|
99
149
|
- `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 emulator script.
|
|
100
150
|
- `moreDifferences: true`: the script shows only the first differences. Use `--from` and `--to` to see the others.
|
|
101
151
|
- `not-found`: the project has no file at that path.
|
|
102
|
-
- `
|
|
152
|
+
- `not-found` from `match`: the library folder does not exist, or it has no routine files. Save a routine with `routine` first.
|
|
153
|
+
- `conflict` from `routine`: the library has a routine with that name. Give another `--name`, or give `--replace` to save over it.
|
|
154
|
+
- `invalid-input` with "is a project folder": the library folder is a project. Give a folder beside the projects.
|
|
155
|
+
- `match` gives `count: 0`: the routine is not in the program. A packed program can hold the routine in a packed form. Unpack the program with c64-unpacker first.
|
|
156
|
+
- `invalid-input`: a file, an address or a shift is wrong. A path that goes out of the project folder is also wrong, also through a link. Read the message.
|
|
103
157
|
|
|
104
158
|
## Handoffs
|
|
105
159
|
|
|
@@ -108,3 +162,4 @@ The exit status is 0 for a result. The exit status is 1 for an error. The exit s
|
|
|
108
162
|
- Run or examine the different code → c64-emulator
|
|
109
163
|
- Analyze the different code → c64-static-analysis
|
|
110
164
|
- Record a conclusion → c64-knowledge
|
|
165
|
+
- Apply the batch of `match` → c64-knowledge
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Byte comparison of releases for the c64-provenance script. Each release
|
|
2
2
|
// has bytes in one or more segments: a PRG at its load address plus an
|
|
3
|
-
// optional shift, or the segments of an unpacker image
|
|
3
|
+
// optional shift, or the segments of an unpacker image. The comparison
|
|
4
4
|
// covers the addresses where every release has bytes, and reports where the
|
|
5
5
|
// releases differ and which releases agree there. It never decides which
|
|
6
6
|
// bytes are original: that needs evidence beyond one comparison.
|
|
@@ -93,7 +93,7 @@ function addressesOf(release: Release): number[] {
|
|
|
93
93
|
return release.segments.flatMap((segment) => Array.from({ length: segment.end - segment.start + 1 }, (_, offset) => segment.start + offset));
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/** Most shifts that suggestShifts gives: data that did not move can outvote code that did
|
|
96
|
+
/** Most shifts that suggestShifts gives: data that did not move can outvote code that did. */
|
|
97
97
|
export const MAX_SUGGESTED_SHIFTS = 3;
|
|
98
98
|
|
|
99
99
|
/**
|
|
@@ -129,7 +129,7 @@ export type ShiftSuggestion = { release: number; shift: number; matchingSequence
|
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
131
|
* The --shift to suggest for release `index` (0 = the first) from `found`,
|
|
132
|
-
* the best shift of that release over the first one
|
|
132
|
+
* the best shift of that release over the first one. The shift is the
|
|
133
133
|
* full value for --shift: `applied` holds the shifts already given. A shift
|
|
134
134
|
* that moves a segment out of $0000-$ffff is not suggested: with two releases,
|
|
135
135
|
* the opposite shift of the first release is suggested when it fits, else the
|