@henols/c64-re-tools 2.1.0-rc.1 → 2.1.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/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +24 -9
- package/skills/c64-assembler/scripts/assembler.ts +95 -0
- package/skills/c64-basic/SKILL.md +17 -9
- package/skills/c64-basic/scripts/basic.ts +26 -36
- package/skills/c64-disk/SKILL.md +17 -7
- package/skills/c64-disk/scripts/disk.ts +130 -104
- package/skills/c64-disk/scripts/lib/containers.ts +10 -12
- package/skills/c64-docs/SKILL.md +47 -28
- package/skills/c64-docs/references/style.md +6 -6
- package/skills/c64-docs/scripts/docs.ts +323 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +1 -1
- package/skills/c64-emulator/SKILL.md +11 -7
- package/skills/c64-knowledge/SKILL.md +34 -21
- package/skills/c64-knowledge/scripts/knowledge.ts +597 -718
- package/skills/c64-knowledge/scripts/lib/listing.ts +5 -7
- package/skills/c64-memory-map/SKILL.md +18 -10
- package/skills/c64-memory-map/scripts/memory-map.ts +81 -0
- package/skills/c64-provenance/SKILL.md +33 -24
- package/skills/c64-provenance/scripts/lib/compare.ts +8 -12
- package/skills/c64-provenance/scripts/lib/leftovers.ts +19 -20
- package/skills/c64-provenance/scripts/lib/library.ts +15 -25
- package/skills/c64-provenance/scripts/lib/runtime.ts +12 -37
- package/skills/c64-provenance/scripts/lib/separate.ts +9 -10
- package/skills/c64-provenance/scripts/provenance.ts +177 -279
- package/skills/c64-reverse-engineering/SKILL.md +13 -12
- package/skills/c64-source/SKILL.md +28 -16
- package/skills/c64-source/scripts/lib/loader.ts +52 -77
- package/skills/c64-source/scripts/lib/source.ts +33 -42
- package/skills/c64-source/scripts/{generate.ts → source.ts} +68 -109
- package/skills/c64-static-analysis/SKILL.md +24 -13
- package/skills/c64-static-analysis/scripts/lib/coverage.ts +3 -4
- package/skills/c64-static-analysis/scripts/lib/findings.ts +2 -2
- package/skills/c64-static-analysis/scripts/lib/instructions.ts +1 -1
- package/skills/c64-static-analysis/scripts/lib/macros.ts +6 -8
- package/skills/c64-static-analysis/scripts/{analyze.ts → static-analysis.ts} +58 -118
- package/skills/c64-testing/SKILL.md +48 -32
- package/skills/c64-testing/references/recording-format.md +48 -19
- package/skills/c64-testing/references/scenario-format.md +24 -19
- package/skills/c64-testing/scripts/{diff.ts → lib/diff-files.ts} +18 -69
- package/skills/c64-testing/scripts/lib/diff.ts +5 -3
- package/skills/c64-testing/scripts/lib/play.ts +244 -54
- package/skills/c64-testing/scripts/lib/recording.ts +153 -120
- package/skills/c64-testing/scripts/{replay.ts → lib/replay.ts} +28 -86
- package/skills/c64-testing/scripts/{run.ts → lib/runs.ts} +36 -59
- package/skills/c64-testing/scripts/lib/scenario.ts +73 -81
- package/skills/c64-testing/scripts/testing.ts +189 -0
- package/skills/c64-unpacker/SKILL.md +42 -25
- package/skills/c64-unpacker/scripts/lib/evidence.ts +3 -4
- package/skills/c64-unpacker/scripts/lib/loads.ts +12 -10
- package/skills/c64-unpacker/scripts/lib/machine.ts +0 -2
- package/skills/c64-unpacker/scripts/lib/stages.ts +14 -19
- package/skills/c64-unpacker/scripts/{unpack.ts → unpacker.ts} +109 -211
- package/src/c64.ts +35 -20
- package/src/cli/main.ts +2 -2
- package/src/errors.ts +38 -0
- package/src/graphics.ts +3 -1
- package/src/host/server.ts +3 -2
- package/src/host/vice/session.ts +4 -4
- package/src/host-client/entry-state.ts +106 -0
- package/src/host-client/image.ts +21 -10
- package/src/host-client/steps.ts +285 -108
- package/src/host-client/transfer.ts +21 -15
- package/src/host-client/vice-session.ts +1 -1
- package/src/knowledge/database.ts +3 -23
- package/src/knowledge/expression.ts +8 -11
- package/src/knowledge/history.ts +2 -2
- package/src/knowledge/import.ts +13 -14
- package/src/knowledge/names.ts +50 -0
- package/src/knowledge/program.ts +11 -10
- package/src/knowledge/read.ts +16 -21
- package/src/knowledge/schema.ts +9 -3
- package/src/knowledge/text.ts +5 -5
- package/src/knowledge/write.ts +93 -137
- package/src/mcp/inputs.ts +105 -0
- package/src/mcp/output.ts +2 -13
- package/src/mcp/server.ts +12 -60
- package/src/mcp/tools/debug.ts +17 -12
- package/src/mcp/tools/execution.ts +5 -32
- package/src/mcp/tools/input.ts +7 -32
- package/src/mcp/tools/media.ts +6 -5
- package/src/mcp/tools/memory.ts +11 -38
- package/src/mcp/tools/video.ts +6 -5
- package/src/native/c64run.ts +1 -1
- package/src/native/dxa.ts +7 -6
- package/src/native/ghidra/analyze.ts +2 -1
- package/src/native/ghidra/index.ts +2 -1
- package/src/native/processes.ts +4 -3
- package/src/native/status.ts +2 -2
- package/src/native/types.ts +3 -7
- package/src/opcodes.ts +13 -13
- package/src/platform.ts +9 -9
- package/src/project.ts +16 -26
- package/src/protocol/messages.ts +3 -21
- package/src/protocol/vice.ts +7 -7
- package/src/script.ts +491 -49
- package/skills/c64-assembler/scripts/assemble.ts +0 -138
- package/skills/c64-docs/scripts/generate.ts +0 -109
- package/skills/c64-docs/scripts/link.ts +0 -239
- package/skills/c64-memory-map/scripts/memmap.ts +0 -92
- package/skills/c64-testing/scripts/checklist.ts +0 -51
- package/skills/c64-testing/scripts/record.ts +0 -96
- package/skills/c64-testing/scripts/test.ts +0 -65
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// the bytes from coverage files, and the references in and out. Nothing here
|
|
4
4
|
// reads files: knowledge.ts gives the lookups.
|
|
5
5
|
|
|
6
|
-
import { COVERAGE } from "#src/c64.ts";
|
|
6
|
+
import { COVERAGE, formatC64Address, formatC64Byte } from "#src/c64.ts";
|
|
7
7
|
import { MODE_LENGTH, OPCODES, type Mode } from "#src/opcodes.ts";
|
|
8
8
|
|
|
9
9
|
export interface ListingSource {
|
|
@@ -41,8 +41,6 @@ export interface ListingLine {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
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
44
|
const DATA_PER_LINE = 8;
|
|
47
45
|
|
|
48
46
|
/** The access class of bytes from the coverage: x, r and w as they occur, or - for none. */
|
|
@@ -57,12 +55,12 @@ function accessOf(coverage: Uint8Array | undefined, start: number, length: numbe
|
|
|
57
55
|
/** The operand of an instruction in ACME syntax, with the name of its target where one exists. */
|
|
58
56
|
function operandOf(mode: Mode, address: number, low: number, high: number, nameOf: ListingSource["nameOf"]): string {
|
|
59
57
|
const word = low | (high << 8);
|
|
60
|
-
const named = (target: number, digits: 2 | 4) => nameOf(target) ?? (digits === 2 ?
|
|
58
|
+
const named = (target: number, digits: 2 | 4) => nameOf(target) ?? (digits === 2 ? formatC64Byte(target) : formatC64Address(target));
|
|
61
59
|
switch (mode) {
|
|
62
60
|
case "imp":
|
|
63
61
|
return "";
|
|
64
62
|
case "imm":
|
|
65
|
-
return `#${
|
|
63
|
+
return `#${formatC64Byte(low)}`;
|
|
66
64
|
case "zp":
|
|
67
65
|
return named(low, 2);
|
|
68
66
|
case "zpx":
|
|
@@ -138,7 +136,7 @@ export function listing(source: ListingSource, start: number, end: number): List
|
|
|
138
136
|
values.push(source.byteAt(at)!);
|
|
139
137
|
at++;
|
|
140
138
|
}
|
|
141
|
-
if (address >= start) lines.push(line(address, values.length, `!byte ${values.map(
|
|
139
|
+
if (address >= start) lines.push(line(address, values.length, `!byte ${values.map(formatC64Byte).join(",")}`));
|
|
142
140
|
address = at;
|
|
143
141
|
}
|
|
144
142
|
return lines;
|
|
@@ -149,7 +147,7 @@ export function listingText(lines: readonly ListingLine[]): string {
|
|
|
149
147
|
const out: string[] = [];
|
|
150
148
|
for (const item of lines) {
|
|
151
149
|
for (const comment of item.lineComments ?? []) out.push(` ; ${comment}`);
|
|
152
|
-
const head = `${
|
|
150
|
+
const head = `${formatC64Address(item.address)} ${item.bytes.padEnd(9)} ${(item.access ?? "").padEnd(3)}`;
|
|
153
151
|
const body = (item.label === undefined ? "" : `${item.label} `).padEnd(20) + item.text;
|
|
154
152
|
const notes = [
|
|
155
153
|
...(item.comment === undefined ? [] : [item.comment]),
|
|
@@ -21,10 +21,10 @@ This skill does not read the emulator and does not change project knowledge.
|
|
|
21
21
|
The script looks up local reference data. It does not use the host runtime.
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
node <skill>/scripts/
|
|
25
|
-
node <skill>/scripts/
|
|
26
|
-
node <skill>/scripts/
|
|
27
|
-
node <skill>/scripts/
|
|
24
|
+
node <skill>/scripts/memory-map.ts at '$d020' '$0314' '$ffd2'
|
|
25
|
+
node <skill>/scripts/memory-map.ts decode '$d018' '$15' --bank '$97'
|
|
26
|
+
node <skill>/scripts/memory-map.ts list vic
|
|
27
|
+
node <skill>/scripts/memory-map.ts export --out src/hw.asm
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
| Command | Result |
|
|
@@ -34,16 +34,26 @@ node <skill>/scripts/memmap.ts export --out src/hw.asm
|
|
|
34
34
|
| `list <vic\|sid\|cia1\|cia2\|kernal\|system>` | All entries of that group. |
|
|
35
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
|
-
Write
|
|
37
|
+
Write a value for `decode` and `--bank` 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
|
+
### Calling the script
|
|
40
|
+
|
|
41
|
+
- Call a script as `node <skill>/scripts/<script>.ts <command> [subjects] [options]`. The command comes first.
|
|
42
|
+
- `<script>.ts --help` lists the commands. `<script>.ts <command> --help` gives the subjects and the options of one command. The help is JSON: `{"text": "..."}`.
|
|
43
|
+
- An address is `$` and 1 to 4 hex digits, for example `'$c000'` or `'$fb'`, or a name. A name is a symbol, else an analyzer name that only one analyzer gives. Quote an address in a shell.
|
|
44
|
+
- A range is `<start>-<end>`, for example `'$c000-$cfff'`. In a JSON file, a range is `start` and `end`.
|
|
45
|
+
- Bytes in a JSON file are hex in a field `data`, for example `"data": "a900"`.
|
|
46
|
+
- The result is one JSON object on one line. A file that the script writes shows as `{"out", "bytes", "sha256"}`. A list shows as `{"count", "listed"}`, and `rest` tells where to get the rest of a long list.
|
|
47
|
+
- An error is `{"error": {"code": "...", "message": "..."}}`. The message tells what is wrong and what to do.
|
|
48
|
+
- The exit status is 0 for done or PASS, 1 for FAIL, 2 for an error and 3 for INCONCLUSIVE.
|
|
40
49
|
|
|
41
50
|
### Project folder
|
|
42
51
|
|
|
43
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/`.
|
|
44
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.
|
|
45
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.
|
|
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
|
|
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 test:<file>` or `--evidence file:<file>` of c64-knowledge. These two refuse a file in `.c64-re-tools/local/`.
|
|
56
|
+
- Do not move a coverage map after `coverage` of c64-knowledge imported it. `--evidence coverage:<map>` names the map by its path at the import. The knowledge keeps the coverage and the SHA-256 of the map, so the evidence stays valid.
|
|
47
57
|
- 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
58
|
- 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
59
|
|
|
@@ -71,9 +81,7 @@ A conclusion about the application can be project knowledge. For example: "the p
|
|
|
71
81
|
|
|
72
82
|
## Result
|
|
73
83
|
|
|
74
|
-
The platform meaning of each address, or the decoded meaning of a register value. For `export`: the
|
|
75
|
-
|
|
76
|
-
The exit status is 0 for a result. The exit status is 2 for a wrong argument.
|
|
84
|
+
The platform meaning of each address, or the decoded meaning of a register value. For `export`: the count of names in `names`, and the file as `{out, bytes, sha256}`.
|
|
77
85
|
|
|
78
86
|
## Failure and conflicts
|
|
79
87
|
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// The c64-memory-map script: the platform meaning of C64 addresses and of
|
|
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.
|
|
4
|
+
|
|
5
|
+
import { formatC64Address, formatC64Byte } from "#src/c64.ts";
|
|
6
|
+
import { writeReported } from "#src/host-client/transfer.ts";
|
|
7
|
+
import { knowledgeNames } from "#src/knowledge/names.ts";
|
|
8
|
+
import { decode, IO, lookup, platformNames, ROM, SYSTEM, type Entry } from "#src/platform.ts";
|
|
9
|
+
import { OPTIONS, runScript, UsageError } from "#src/script.ts";
|
|
10
|
+
|
|
11
|
+
const show = (entry: Entry) => ({ start: formatC64Address(entry.start), ...(entry.end !== entry.start ? { end: formatC64Address(entry.end) } : {}), name: entry.name, meaning: entry.meaning });
|
|
12
|
+
|
|
13
|
+
const LISTS: Record<string, () => Entry[]> = {
|
|
14
|
+
vic: () => IO[0]!.registers,
|
|
15
|
+
sid: () => IO[1]!.registers,
|
|
16
|
+
cia1: () => IO[2]!.registers,
|
|
17
|
+
cia2: () => IO[3]!.registers,
|
|
18
|
+
kernal: () => ROM,
|
|
19
|
+
system: () => SYSTEM,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
await runScript({
|
|
23
|
+
name: "memory-map",
|
|
24
|
+
summary: "The platform meaning of C64 addresses and of values in platform registers.",
|
|
25
|
+
names: knowledgeNames,
|
|
26
|
+
commands: [
|
|
27
|
+
{
|
|
28
|
+
name: "at",
|
|
29
|
+
summary: "The platform meaning of each address: its name, area and meaning.",
|
|
30
|
+
subjects: [{ name: "<address>", help: "an address or a name of the knowledge", list: true }],
|
|
31
|
+
run: (call) => ({
|
|
32
|
+
addresses: call.subjects.map((text) => {
|
|
33
|
+
const found = lookup(call.parseAddress(text, "<address>"));
|
|
34
|
+
// The chip registers, color RAM and the cartridge I/O areas show only while $01 maps I/O in.
|
|
35
|
+
const io = found.address >= 0xd000 && found.address <= 0xdfff;
|
|
36
|
+
return {
|
|
37
|
+
...found,
|
|
38
|
+
address: formatC64Address(found.address),
|
|
39
|
+
...(found.mirrorOf === undefined ? {} : { mirrorOf: formatC64Address(found.mirrorOf) }),
|
|
40
|
+
...(found.entryStart === undefined ? {} : { entryStart: formatC64Address(found.entryStart) }),
|
|
41
|
+
...(io ? { visible: "when $01 shows I/O at $D000 (the default)" } : {}),
|
|
42
|
+
};
|
|
43
|
+
}),
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "decode",
|
|
48
|
+
summary: "The meaning of a value in a platform register.",
|
|
49
|
+
subjects: [
|
|
50
|
+
{ name: "<register>", help: "the register: $0001, $d011, $d016, $d018, $d010, $d015-$d01f, $dc0d, $dd00 or $dd0d" },
|
|
51
|
+
{ name: "<value>", help: "the value of the register: $ and hex, decimal, or % and binary, 0 to 255" },
|
|
52
|
+
],
|
|
53
|
+
options: { bank: { kind: "number", value: "<value>", help: "the value of $dd00, for the VIC-II bank of a $d018 value", max: 0xff } },
|
|
54
|
+
run: (call) => {
|
|
55
|
+
const register = call.parseAddress(call.subject(0), "<register>");
|
|
56
|
+
const value = call.parseNumber(call.subject(1), "<value>", 0, 0xff);
|
|
57
|
+
return { register: formatC64Address(register), value: formatC64Byte(value), name: lookup(register).name, ...decode(register, value, call.count("bank")) };
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "list",
|
|
62
|
+
summary: "The registers of a chip, the KERNAL jump table, or the system areas.",
|
|
63
|
+
subjects: [{ name: "<list>", help: `one of ${Object.keys(LISTS).join(", ")}` }],
|
|
64
|
+
run: (call) => {
|
|
65
|
+
const list = LISTS[call.subject(0)];
|
|
66
|
+
if (list === undefined) throw new UsageError(`<list> must be one of ${Object.keys(LISTS).join(", ")}, not ${call.subject(0)}`);
|
|
67
|
+
return { entries: list().map(show) };
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "export",
|
|
72
|
+
summary: "Writes the platform names as an ACME include: NAME = $xxxx.",
|
|
73
|
+
options: { out: { ...OPTIONS.out, value: "<file>", help: "the include file, relative to the project folder", required: true } },
|
|
74
|
+
run: (call) => {
|
|
75
|
+
const names = platformNames();
|
|
76
|
+
const lines = ["; C64 platform names from the c64-memory-map skill, for ACME.", ...names.map((item) => `${item.name} = ${item.address < 0x100 ? formatC64Byte(item.address) : formatC64Address(item.address)}`.padEnd(24) + `; ${item.meaning}`)];
|
|
77
|
+
return { names: names.length, ...writeReported(call.string("out")!, `${lines.join("\n")}\n`, { what: "--out" }) };
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
});
|
|
@@ -17,7 +17,7 @@ This skill does not change project knowledge.
|
|
|
17
17
|
|
|
18
18
|
## Inputs
|
|
19
19
|
|
|
20
|
-
- 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.
|
|
20
|
+
- Two or more releases, relative to the project folder. A release is `knowledge` (the program in the knowledge), a PRG file, or an `image.json` from c64-unpacker capture with its `entry_state.json` beside it. Extract releases from disk images with the c64-disk skill. Unpack packed releases with the c64-unpacker skill first.
|
|
21
21
|
- Optional: an address range to compare.
|
|
22
22
|
- Optional: a shift for a release that loads at a different address.
|
|
23
23
|
- Optional: a number of frames to run each release, and a steps file.
|
|
@@ -26,12 +26,12 @@ This skill does not change project knowledge.
|
|
|
26
26
|
|
|
27
27
|
## Tools / execution path
|
|
28
28
|
|
|
29
|
-
The script compares the
|
|
29
|
+
The script compares the releases locally. Only `--frames` uses the host runtime: each release runs in its own emulator.
|
|
30
30
|
|
|
31
31
|
```
|
|
32
|
-
node <skill>/scripts/provenance.ts releases/a.prg releases/b.prg releases/c.prg
|
|
33
|
-
node <skill>/scripts/provenance.ts releases/a.prg releases/b.prg --shift 2:-256 --
|
|
34
|
-
node <skill>/scripts/provenance.ts
|
|
32
|
+
node <skill>/scripts/provenance.ts compare releases/a.prg releases/b.prg releases/c.prg
|
|
33
|
+
node <skill>/scripts/provenance.ts compare releases/a.prg releases/b.prg --shift 2:-256 --range '$0900-$09ff'
|
|
34
|
+
node <skill>/scripts/provenance.ts compare knowledge .c64-re-tools/local/b/image.json --frames 600 --steps .c64-re-tools/local/start-game.json
|
|
35
35
|
node <skill>/scripts/provenance.ts separate --out .c64-re-tools/local/separate.json
|
|
36
36
|
node <skill>/scripts/provenance.ts leftovers --out .c64-re-tools/local/leftovers.json
|
|
37
37
|
```
|
|
@@ -39,23 +39,34 @@ node <skill>/scripts/provenance.ts leftovers --out .c64-re-tools/local/leftovers
|
|
|
39
39
|
| Option | Meaning |
|
|
40
40
|
|---|---|
|
|
41
41
|
| `--shift 2:-256` | Move release 2 (the first release is 1) by -256 bytes. Use it again for more releases. |
|
|
42
|
-
| `--
|
|
43
|
-
| `--
|
|
44
|
-
| `--
|
|
45
|
-
| `--steps steps/start-game.json` | With `--run`: the action steps of the c64-unpacker steps file to run before the frames, for example a key to start the game. |
|
|
42
|
+
| `--range '$0900-$09ff'` | Compare only the addresses in this range. |
|
|
43
|
+
| `--frames 600` | Run each release for this many frames, and give each listed difference its runtime evidence. |
|
|
44
|
+
| `--steps .c64-re-tools/local/start-game.json` | With `--frames`: a JSON list of action steps to run before the frames, for example a key to start the game. The steps are the steps of a c64-unpacker steps file. |
|
|
46
45
|
|
|
47
|
-
The script puts each PRG at its load address. An image has bytes only in its segments. The script compares the addresses where all releases have bytes.
|
|
46
|
+
The script puts each PRG at its load address. An image has bytes only in its segments. The script compares the addresses where all releases have bytes.
|
|
48
47
|
|
|
49
48
|
With `--shift`, the addresses of `segments`, `common` and `differences` are the release addresses plus the `shift` of the release. The `start` and `end` of `runtime` are the addresses in the release itself, without the shift.
|
|
50
49
|
|
|
51
|
-
With `--
|
|
50
|
+
With `--frames`, 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.
|
|
51
|
+
|
|
52
|
+
### Calling the script
|
|
53
|
+
|
|
54
|
+
- Call a script as `node <skill>/scripts/<script>.ts <command> [subjects] [options]`. The command comes first.
|
|
55
|
+
- `<script>.ts --help` lists the commands. `<script>.ts <command> --help` gives the subjects and the options of one command. The help is JSON: `{"text": "..."}`.
|
|
56
|
+
- An address is `$` and 1 to 4 hex digits, for example `'$c000'` or `'$fb'`, or a name. A name is a symbol, else an analyzer name that only one analyzer gives. Quote an address in a shell.
|
|
57
|
+
- A range is `<start>-<end>`, for example `'$c000-$cfff'`. In a JSON file, a range is `start` and `end`.
|
|
58
|
+
- Bytes in a JSON file are hex in a field `data`, for example `"data": "a900"`.
|
|
59
|
+
- The result is one JSON object on one line. A file that the script writes shows as `{"out", "bytes", "sha256"}`. A list shows as `{"count", "listed"}`, and `rest` tells where to get the rest of a long list.
|
|
60
|
+
- An error is `{"error": {"code": "...", "message": "..."}}`. The message tells what is wrong and what to do.
|
|
61
|
+
- The exit status is 0 for done or PASS, 1 for FAIL, 2 for an error and 3 for INCONCLUSIVE.
|
|
52
62
|
|
|
53
63
|
### Project folder
|
|
54
64
|
|
|
55
65
|
- 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/`.
|
|
56
66
|
- 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.
|
|
57
67
|
- 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.
|
|
58
|
-
- 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
|
|
68
|
+
- 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 test:<file>` or `--evidence file:<file>` of c64-knowledge. These two refuse a file in `.c64-re-tools/local/`.
|
|
69
|
+
- Do not move a coverage map after `coverage` of c64-knowledge imported it. `--evidence coverage:<map>` names the map by its path at the import. The knowledge keeps the coverage and the SHA-256 of the map, so the evidence stays valid.
|
|
59
70
|
- 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.
|
|
60
71
|
- 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`.
|
|
61
72
|
|
|
@@ -66,7 +77,7 @@ With `--run`, a PRG starts with autostart and RUN. An image starts from its byte
|
|
|
66
77
|
|
|
67
78
|
`suggestedShifts` has at most 3 shifts for each release. The shift with the most `matchingSequences` is first. Data that did not move and code that moved give different shifts. Try each shift. `current: true` marks the shift of this run.
|
|
68
79
|
3. Read `differences`. Each one has an address range, the bytes of each release, and `groups`: the releases that have the same bytes there.
|
|
69
|
-
4. To know how the program uses the differences, run the script again with `--
|
|
80
|
+
4. To know how the program uses the differences, run the script again with `--frames` and the frames that get the program to the part you examine. Read `runtime` of each difference:
|
|
70
81
|
- `first`: the first access in that release: `read`, `write`, `execute` or `none`. An instruction that reads and then writes the same byte, for example INC, counts as `read`. The operand bytes of an executed instruction count as `execute`, for example the value of `LDA #$03`.
|
|
71
82
|
- `changed`: `true` when the bytes at the end of the run are not the bytes of the release.
|
|
72
83
|
- A range that each release writes before it reads is probably runtime state, not a difference of the releases.
|
|
@@ -88,7 +99,7 @@ Use `separate` when you have only one release. It reads the program, its stages
|
|
|
88
99
|
- `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.
|
|
89
100
|
- `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.
|
|
90
101
|
|
|
91
|
-
Each list has `count` and `listed`. `listed` has the first ranges, at most `--max-listed
|
|
102
|
+
Each list has `count` and `listed`. `listed` has the first ranges, at most `--max-listed` (default 50). `--out` writes all of them and the result shows the file as `{out, bytes, sha256}`. Run more coverage runs for parts that are `untouched`, and add them with `coverage` of the c64-knowledge skill, before you decide.
|
|
92
103
|
|
|
93
104
|
## Leftovers
|
|
94
105
|
|
|
@@ -136,18 +147,18 @@ Read the findings with care:
|
|
|
136
147
|
|
|
137
148
|
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.
|
|
138
149
|
|
|
139
|
-
`--library` is relative to the
|
|
150
|
+
`--library` is relative to the project folder, as all other paths. For example, `../library` is the folder `library/` beside the project.
|
|
140
151
|
|
|
141
152
|
```
|
|
142
|
-
node <skill>/scripts/provenance.ts routine
|
|
143
|
-
node <skill>/scripts/provenance.ts routine
|
|
144
|
-
node <skill>/scripts/provenance.ts match --library library --out .c64-re-tools/local/match.json --project game-b
|
|
153
|
+
node <skill>/scripts/provenance.ts routine '$1000' --library ../library --project game-a
|
|
154
|
+
node <skill>/scripts/provenance.ts routine '$1000-$1026' --name play --library ../library --project game-a
|
|
155
|
+
node <skill>/scripts/provenance.ts match --library ../library --out .c64-re-tools/local/match.json --project game-b
|
|
145
156
|
```
|
|
146
157
|
|
|
147
158
|
To save a routine:
|
|
148
159
|
|
|
149
160
|
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.
|
|
150
|
-
2. Run `routine` with the start of a module
|
|
161
|
+
2. Run `routine` with the start address of a module, or with a range `<start>-<end>`. A module ends at the next module or at the end of its segment.
|
|
151
162
|
- `--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.
|
|
152
163
|
- `--title` is the title of the routine. When you do not give it, the script uses the module title.
|
|
153
164
|
- A routine has at least 16 bytes. Its last instruction must end in the range.
|
|
@@ -159,7 +170,7 @@ To find the routines in a program:
|
|
|
159
170
|
- The opcodes and the other bytes must be equal.
|
|
160
171
|
- 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.
|
|
161
172
|
- 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.
|
|
162
|
-
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.
|
|
173
|
+
2. Give `--out` to get a batch for `knowledge.ts apply`. `batch` shows the file as `{out, bytes, sha256}`. 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.
|
|
163
174
|
3. Read `batch.leftOut` before you apply the batch. The script leaves out a write in these conditions:
|
|
164
175
|
- The program has a user or LLM row at that place.
|
|
165
176
|
- The program uses the name at another address.
|
|
@@ -179,14 +190,12 @@ Record a confirmed conclusion as a normal comment, region or symbol with the c64
|
|
|
179
190
|
|
|
180
191
|
## Result
|
|
181
192
|
|
|
182
|
-
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 `--
|
|
193
|
+
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 `--frames`, each difference also has `runtime`, and `run` tells if each release started, or why not.
|
|
183
194
|
|
|
184
195
|
For `leftovers`: the lists of findings, with `entry` and the number of `stages`. `noEntry`, `noStages` and `noCoverage` tell which knowledge is missing.
|
|
185
196
|
|
|
186
197
|
The runtime evidence and the leftovers are evidence only. The script does not decide which bytes are original.
|
|
187
198
|
|
|
188
|
-
The exit status is 0 for a result. The exit status is 1 for an error. The exit status is 2 for a wrong argument.
|
|
189
|
-
|
|
190
199
|
## Failure and conflicts
|
|
191
200
|
|
|
192
201
|
- Do not call releases independent when you do not know their relation. Two cracks can come from the same source.
|
|
@@ -194,7 +203,7 @@ The exit status is 0 for a result. The exit status is 1 for an error. The exit s
|
|
|
194
203
|
- `common` is empty: the releases do not cover the same addresses. Use `--shift`, or compare a range that all releases cover.
|
|
195
204
|
- `run.releases` shows `started: false`: a PRG without a BASIC start, or an image without `entry_state.json`. The reason tells which. The other releases still give evidence.
|
|
196
205
|
- `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.
|
|
197
|
-
- `moreDifferences: true`: the script shows only the first differences. Use `--
|
|
206
|
+
- `moreDifferences: true`: the script shows only the first differences. Use `--range` to see the others.
|
|
198
207
|
- `not-found`: the project has no file at that path.
|
|
199
208
|
- `not-found` from `match`: the library folder does not exist, or it has no routine files. Save a routine with `routine` first.
|
|
200
209
|
- `conflict` from `routine`: the library has a routine with that name. Give another `--name`, or give `--replace` to save over it.
|
|
@@ -5,15 +5,12 @@
|
|
|
5
5
|
// releases differ and which releases agree there. It never decides which
|
|
6
6
|
// bytes are original: that needs evidence beyond one comparison.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
start: number;
|
|
10
|
-
end: number;
|
|
11
|
-
}
|
|
8
|
+
import { type AddressRange, formatC64Address } from "#src/c64.ts";
|
|
12
9
|
|
|
13
10
|
export interface Release {
|
|
14
11
|
name: string;
|
|
15
12
|
/** Where the release has bytes, after the shift, in address order. */
|
|
16
|
-
segments:
|
|
13
|
+
segments: AddressRange[];
|
|
17
14
|
/** The byte at an address inside a segment. */
|
|
18
15
|
byteAt: (address: number) => number;
|
|
19
16
|
}
|
|
@@ -29,7 +26,7 @@ export interface DifferingRange {
|
|
|
29
26
|
|
|
30
27
|
export interface Comparison {
|
|
31
28
|
/** The addresses where every release has bytes. */
|
|
32
|
-
common:
|
|
29
|
+
common: AddressRange[];
|
|
33
30
|
identicalBytes: number;
|
|
34
31
|
differingBytes: number;
|
|
35
32
|
differences: DifferingRange[];
|
|
@@ -43,8 +40,8 @@ const JOIN_GAP = 4;
|
|
|
43
40
|
export const covers = (release: Release, address: number) => release.segments.some((segment) => address >= segment.start && address <= segment.end);
|
|
44
41
|
|
|
45
42
|
/** The ranges where every release has bytes, inside from..to. */
|
|
46
|
-
export function commonRanges(releases: Release[], from = 0x0000, to = 0xffff):
|
|
47
|
-
const ranges:
|
|
43
|
+
export function commonRanges(releases: Release[], from = 0x0000, to = 0xffff): AddressRange[] {
|
|
44
|
+
const ranges: AddressRange[] = [];
|
|
48
45
|
for (let address = from; address <= to; address++) {
|
|
49
46
|
if (!releases.every((release) => covers(release, address))) continue;
|
|
50
47
|
const last = ranges.at(-1);
|
|
@@ -67,7 +64,7 @@ export function compareReleases(releases: Release[], options: { from?: number; t
|
|
|
67
64
|
}
|
|
68
65
|
// A joined range stays inside one common range, so every release has all of its bytes.
|
|
69
66
|
const inSameCommon = (a: number, b: number) => common.some((range) => a >= range.start && b <= range.end);
|
|
70
|
-
const ranges:
|
|
67
|
+
const ranges: AddressRange[] = [];
|
|
71
68
|
for (const address of differing) {
|
|
72
69
|
const last = ranges.at(-1);
|
|
73
70
|
if (last !== undefined && address - last.end <= JOIN_GAP && inSameCommon(last.start, address)) last.end = address;
|
|
@@ -137,9 +134,8 @@ export type ShiftSuggestion = { release: number; shift: number; matchingSequence
|
|
|
137
134
|
*/
|
|
138
135
|
export function shiftSuggestion(releases: Release[], applied: number[], index: number, found: { shift: number; matches: number }): ShiftSuggestion {
|
|
139
136
|
const outside = (release: Release, by: number) => release.segments.find((segment) => segment.start + by < 0 || segment.end + by > 0xffff);
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
`release ${number} moved by ${by} puts ${hex(segment.start)}-${hex(segment.end)} ${segment.start + by < 0 ? "below $0000" : "past $ffff"}`;
|
|
137
|
+
const why = (number: number, by: number, segment: AddressRange) =>
|
|
138
|
+
`release ${number} moved by ${by} puts ${formatC64Address(segment.start)}-${formatC64Address(segment.end)} ${segment.start + by < 0 ? "below $0000" : "past $ffff"}`;
|
|
143
139
|
const own = outside(releases[index]!, found.shift);
|
|
144
140
|
if (own === undefined) return { release: index + 1, shift: applied[index]! + found.shift, matchingSequences: found.matches };
|
|
145
141
|
const reasons = [why(index + 1, found.shift, own)];
|
|
@@ -6,13 +6,12 @@
|
|
|
6
6
|
// its bytes from the separation. Evidence only: nothing here decides what a
|
|
7
7
|
// leftover is or who made it.
|
|
8
8
|
|
|
9
|
-
import { basicLineText } from "#src/c64.ts";
|
|
9
|
+
import { type AddressRange, basicLineText } from "#src/c64.ts";
|
|
10
10
|
import { MNEMONICS } from "#src/opcodes.ts";
|
|
11
|
-
import type { Range } from "./compare.ts";
|
|
12
11
|
import type { Access, Separation } from "./separate.ts";
|
|
13
12
|
|
|
14
13
|
export interface LeftoversInput {
|
|
15
|
-
segments:
|
|
14
|
+
segments: AddressRange[];
|
|
16
15
|
/** 64 KiB with the program bytes at their addresses. */
|
|
17
16
|
memory: Uint8Array;
|
|
18
17
|
separation: Separation;
|
|
@@ -33,7 +32,7 @@ export interface Placement {
|
|
|
33
32
|
/** high-bit is text with bit 7 set: ASCII as the Apple II keeps it, or C64 shifted letters with $a0 spaces. Its spaces can also be $20. */
|
|
34
33
|
export type TextEncoding = "petscii" | "screen" | "ascii" | "high-bit";
|
|
35
34
|
|
|
36
|
-
export interface TextFinding extends
|
|
35
|
+
export interface TextFinding extends AddressRange, Placement {
|
|
37
36
|
encoding: TextEncoding;
|
|
38
37
|
/** Screen codes with bit 7 set: the characters show reversed. */
|
|
39
38
|
reversed?: boolean;
|
|
@@ -45,26 +44,26 @@ export interface TextFinding extends Range, Placement {
|
|
|
45
44
|
mnemonics?: number;
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
export interface BasicFinding extends
|
|
47
|
+
export interface BasicFinding extends AddressRange, Placement {
|
|
49
48
|
/** The address of the first line where the links put it: where the lines were when BASIC linked them. */
|
|
50
49
|
linkedAt: number;
|
|
51
50
|
lines: string[];
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
export interface FillFinding extends
|
|
53
|
+
export interface FillFinding extends AddressRange, Placement {
|
|
55
54
|
pattern: number[];
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
/** Untouched bytes that are no text, BASIC, fill or code region. */
|
|
59
|
-
export type ResidueFinding =
|
|
58
|
+
export type ResidueFinding = AddressRange & Placement;
|
|
60
59
|
|
|
61
|
-
export interface CopyFinding extends
|
|
60
|
+
export interface CopyFinding extends AddressRange, Placement {
|
|
62
61
|
/** The first address of the same bytes earlier in the program. */
|
|
63
62
|
copyOf: number;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
/** A code region that no call or jump reaches from the entry, and that no run executed. */
|
|
67
|
-
export interface DeadCodeFinding extends
|
|
66
|
+
export interface DeadCodeFinding extends AddressRange, Placement {
|
|
68
67
|
type: string;
|
|
69
68
|
/** false when the coverage shows that it did not run; absent without coverage. */
|
|
70
69
|
executed?: false;
|
|
@@ -228,7 +227,7 @@ function ownEncoding(encoding: TextEncoding, bytes: number[], characters: Charac
|
|
|
228
227
|
}
|
|
229
228
|
|
|
230
229
|
/** The runs of one encoding in a segment that read as text. `reversed` reads the bytes with bit 7 set, without that bit. */
|
|
231
|
-
function textRuns(memory: Uint8Array, segment:
|
|
230
|
+
function textRuns(memory: Uint8Array, segment: AddressRange, encoding: TextEncoding, reversed: boolean, min: number): TextFinding[] {
|
|
232
231
|
const decode = { petscii: petsciiOrLineEnd, screen: screenCharacter, ascii: asciiCharacter, "high-bit": highAsciiCharacter }[encoding];
|
|
233
232
|
const found: TextFinding[] = [];
|
|
234
233
|
let run: Array<{ address: number; character: Character }> = [];
|
|
@@ -276,10 +275,10 @@ function textRuns(memory: Uint8Array, segment: Range, encoding: TextEncoding, re
|
|
|
276
275
|
return found;
|
|
277
276
|
}
|
|
278
277
|
|
|
279
|
-
export function findText(memory: Uint8Array, segments:
|
|
278
|
+
export function findText(memory: Uint8Array, segments: AddressRange[], min = MIN_TEXT): TextFinding[] {
|
|
280
279
|
const scan = (encoding: TextEncoding, reversed = false) => segments.flatMap((segment) => textRuns(memory, segment, encoding, reversed, min));
|
|
281
280
|
// A run that is mostly a run of an earlier list is the same text, read in another encoding, with stray bytes at its edges.
|
|
282
|
-
const overlap = (a:
|
|
281
|
+
const overlap = (a: AddressRange, b: AddressRange) => Math.max(0, Math.min(a.end, b.end) - Math.max(a.start, b.start) + 1);
|
|
283
282
|
const kept: TextFinding[] = [];
|
|
284
283
|
for (const list of [[...scan("high-bit"), ...scan("ascii")], scan("petscii"), [...scan("screen"), ...scan("screen", true)]]) {
|
|
285
284
|
kept.push(...list.filter((run) => !kept.some((other) => overlap(run, other) * 2 > run.end - run.start + 1)));
|
|
@@ -303,7 +302,7 @@ function tokensFit(tokens: Uint8Array): boolean {
|
|
|
303
302
|
* a finding. The links give the address where BASIC put the lines, which can
|
|
304
303
|
* be another address than where they are now.
|
|
305
304
|
*/
|
|
306
|
-
export function findBasic(memory: Uint8Array, segments:
|
|
305
|
+
export function findBasic(memory: Uint8Array, segments: AddressRange[], basicStart?: number): BasicFinding[] {
|
|
307
306
|
const found: BasicFinding[] = [];
|
|
308
307
|
for (const segment of segments) {
|
|
309
308
|
const word = (at: number) => memory[at]! | (memory[at + 1]! << 8);
|
|
@@ -348,7 +347,7 @@ export function findBasic(memory: Uint8Array, segments: Range[], basicStart?: nu
|
|
|
348
347
|
}
|
|
349
348
|
|
|
350
349
|
/** Runs of one byte of at least MIN_FILL bytes, and runs of a pattern of two to four bytes of at least twice that. */
|
|
351
|
-
export function findFills(memory: Uint8Array, segments:
|
|
350
|
+
export function findFills(memory: Uint8Array, segments: AddressRange[]): FillFinding[] {
|
|
352
351
|
const found: FillFinding[] = [];
|
|
353
352
|
for (const segment of segments) {
|
|
354
353
|
for (let at = segment.start; at <= segment.end; ) {
|
|
@@ -374,7 +373,7 @@ export function findFills(memory: Uint8Array, segments: Range[]): FillFinding[]
|
|
|
374
373
|
}
|
|
375
374
|
|
|
376
375
|
/** Exact copies of at least MIN_COPY bytes of an earlier range, with enough different byte values to be no fill. */
|
|
377
|
-
export function findCopies(memory: Uint8Array, segments:
|
|
376
|
+
export function findCopies(memory: Uint8Array, segments: AddressRange[]): CopyFinding[] {
|
|
378
377
|
const found: CopyFinding[] = [];
|
|
379
378
|
const seen = new Map<string, number>();
|
|
380
379
|
const inside = (address: number) => segments.some((segment) => address >= segment.start && address <= segment.end);
|
|
@@ -399,8 +398,8 @@ export function findCopies(memory: Uint8Array, segments: Range[]): CopyFinding[]
|
|
|
399
398
|
}
|
|
400
399
|
|
|
401
400
|
/** Splits `range` at the parts that `taken` covers, and keeps the pieces of at least `min` bytes. */
|
|
402
|
-
function without(range:
|
|
403
|
-
const pieces:
|
|
401
|
+
function without(range: AddressRange, taken: AddressRange[], min: number): AddressRange[] {
|
|
402
|
+
const pieces: AddressRange[] = [];
|
|
404
403
|
let start = range.start;
|
|
405
404
|
for (const part of taken.filter((item) => item.start <= range.end && item.end >= range.start).sort((a, b) => a.start - b.start)) {
|
|
406
405
|
if (part.start > start) pieces.push({ start, end: part.start - 1 });
|
|
@@ -413,19 +412,19 @@ function without(range: Range, taken: Range[], min: number): Range[] {
|
|
|
413
412
|
export function findLeftovers(input: LeftoversInput): Leftovers {
|
|
414
413
|
const { memory, segments, separation } = input;
|
|
415
414
|
const ranges = separation.ranges;
|
|
416
|
-
const placement = (range:
|
|
415
|
+
const placement = (range: AddressRange): Placement => {
|
|
417
416
|
const parts = ranges.filter((part) => part.start <= range.end && part.end >= range.start);
|
|
418
417
|
const stages = [...new Set(parts.flatMap((part) => (part.stage === undefined ? [] : [part.stage])))].sort((a, b) => a - b);
|
|
419
418
|
const access = [...new Set(parts.flatMap((part) => (part.access === undefined ? [] : [part.access])))];
|
|
420
419
|
return { ...(stages.length === 0 ? {} : { stages }), ...(access.length === 0 ? {} : { access }) };
|
|
421
420
|
};
|
|
422
|
-
const placed = <T extends
|
|
421
|
+
const placed = <T extends AddressRange>(items: T[]) => items.map((item) => ({ ...item, ...placement(item) }));
|
|
423
422
|
|
|
424
423
|
const text = findText(memory, segments, input.minText);
|
|
425
424
|
const basic = findBasic(memory, segments, input.basicStart);
|
|
426
425
|
const fill = findFills(memory, segments);
|
|
427
426
|
// Untouched bytes that no other finding explains.
|
|
428
|
-
const untouched:
|
|
427
|
+
const untouched: AddressRange[] = [];
|
|
429
428
|
for (const part of ranges) {
|
|
430
429
|
if (part.access !== "untouched") continue;
|
|
431
430
|
const last = untouched.at(-1);
|