@henols/c64-re-tools 2.0.0-rc.9 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -116
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +22 -6
- package/skills/c64-assembler/scripts/assemble.ts +42 -14
- package/skills/c64-assembler/scripts/lib/compare.ts +48 -0
- package/skills/c64-basic/SKILL.md +10 -1
- package/skills/c64-basic/scripts/basic.ts +3 -3
- package/skills/c64-disk/SKILL.md +13 -4
- package/skills/c64-disk/scripts/disk.ts +7 -7
- package/skills/c64-disk/scripts/{containers.ts → lib/containers.ts} +2 -2
- package/skills/c64-docs/SKILL.md +138 -0
- package/skills/c64-docs/references/page-types.md +141 -0
- package/skills/c64-docs/references/style.md +43 -0
- package/skills/c64-docs/scripts/generate.ts +109 -0
- package/skills/c64-docs/scripts/lib/links.ts +167 -0
- package/skills/c64-docs/scripts/lib/markdown.ts +79 -0
- package/skills/c64-docs/scripts/lib/reference.ts +406 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +81 -0
- package/skills/c64-docs/scripts/link.ts +239 -0
- package/skills/c64-emulator/SKILL.md +16 -5
- package/skills/c64-knowledge/SKILL.md +140 -18
- package/skills/c64-knowledge/scripts/knowledge.ts +737 -112
- package/skills/c64-knowledge/scripts/lib/listing.ts +162 -0
- package/skills/c64-memory-map/SKILL.md +11 -2
- package/skills/c64-memory-map/scripts/memmap.ts +2 -2
- package/skills/c64-provenance/SKILL.md +66 -11
- package/skills/c64-provenance/scripts/{compare.ts → lib/compare.ts} +3 -3
- package/skills/c64-provenance/scripts/lib/library.ts +584 -0
- package/skills/c64-provenance/scripts/{runtime.ts → lib/runtime.ts} +2 -2
- package/skills/c64-provenance/scripts/{separate.ts → lib/separate.ts} +3 -3
- package/skills/c64-provenance/scripts/provenance.ts +242 -40
- package/skills/c64-reverse-engineering/SKILL.md +51 -26
- package/skills/c64-source/SKILL.md +76 -28
- package/skills/c64-source/scripts/generate.ts +425 -127
- package/skills/c64-source/scripts/{loader.ts → lib/loader.ts} +84 -25
- package/skills/c64-source/scripts/lib/source.ts +1121 -0
- package/skills/c64-static-analysis/SKILL.md +35 -18
- package/skills/c64-static-analysis/scripts/analyze.ts +184 -55
- package/skills/c64-static-analysis/scripts/{coverage.ts → lib/coverage.ts} +19 -25
- package/skills/c64-static-analysis/scripts/{findings.ts → lib/findings.ts} +6 -2
- package/skills/c64-static-analysis/scripts/{idioms.ts → lib/idioms.ts} +21 -12
- package/skills/c64-static-analysis/scripts/{instructions.ts → lib/instructions.ts} +1 -1
- package/skills/c64-static-analysis/scripts/lib/macros.ts +233 -0
- package/skills/c64-static-analysis/scripts/lib/pitfalls.ts +68 -0
- package/skills/c64-static-analysis/scripts/lib/pointers.ts +108 -0
- package/skills/c64-testing/SKILL.md +36 -7
- package/skills/c64-testing/references/scenario-format.md +12 -8
- package/skills/c64-testing/scripts/checklist.ts +3 -3
- package/skills/c64-testing/scripts/diff.ts +167 -0
- package/skills/c64-testing/scripts/lib/diff.ts +70 -0
- package/skills/c64-testing/scripts/{scenario.ts → lib/scenario.ts} +45 -34
- package/skills/c64-testing/scripts/run.ts +73 -21
- package/skills/c64-testing/scripts/test.ts +5 -5
- package/skills/c64-unpacker/SKILL.md +23 -9
- package/skills/c64-unpacker/scripts/{evidence.ts → lib/evidence.ts} +1 -1
- package/skills/c64-unpacker/scripts/{loads.ts → lib/loads.ts} +4 -4
- package/skills/c64-unpacker/scripts/{machine.ts → lib/machine.ts} +4 -4
- package/skills/c64-unpacker/scripts/{stages.ts → lib/stages.ts} +17 -5
- package/skills/c64-unpacker/scripts/unpack.ts +41 -24
- package/src/c64.ts +103 -5
- package/src/cli/cleanup.ts +1 -1
- package/src/cli/main.ts +14 -5
- package/src/cli/status.ts +70 -3
- package/src/graphics.ts +158 -0
- package/src/host/main.ts +25 -5
- package/src/host/server.ts +10 -3
- package/src/host/tools/petcat.ts +1 -1
- package/src/host/tools/status.ts +1 -1
- package/src/host/vice/adapter.ts +4 -3
- package/src/host/vice/process.ts +2 -2
- package/src/host/vice/screen.ts +1 -42
- package/src/host/vice/session.ts +196 -23
- package/src/host-client/autostart.ts +1 -1
- package/src/host-client/connect.ts +1 -1
- package/src/host-client/coverage.ts +2 -29
- package/src/host-client/image.ts +66 -0
- package/src/host-client/steps.ts +9 -4
- package/src/host-client/tools.ts +1 -0
- package/src/host-client/transfer.ts +39 -11
- package/src/host-client/vice-session.ts +33 -16
- package/src/knowledge/conflicts.ts +161 -0
- package/src/knowledge/database.ts +150 -38
- package/src/knowledge/expression.ts +209 -0
- package/src/knowledge/graph.ts +104 -0
- package/src/knowledge/history.ts +70 -22
- package/src/knowledge/import.ts +64 -55
- package/src/knowledge/join.ts +211 -0
- package/src/knowledge/program.ts +266 -0
- package/src/knowledge/read.ts +258 -27
- package/src/knowledge/schema.ts +199 -75
- package/src/knowledge/text.ts +415 -0
- package/src/knowledge/write.ts +740 -57
- package/src/mcp/main.ts +8 -2
- package/src/mcp/output.ts +15 -8
- package/src/mcp/server.ts +3 -1
- package/src/mcp/tools/debug.ts +97 -16
- package/src/mcp/tools/execution.ts +38 -5
- package/src/mcp/tools/machine.ts +11 -2
- package/src/mcp/tools/media.ts +18 -13
- package/src/mcp/tools/memory.ts +17 -8
- package/src/mcp/tools/video.ts +9 -9
- package/src/native/discover.ts +2 -2
- package/src/native/ghidra/analyze.ts +2 -2
- package/src/native/ghidra/index.ts +1 -1
- package/src/native/ghidra/language/c64rt_6510.slaspec +4 -1
- package/src/native/ghidra/scripts/C64Export.java +1 -1
- package/src/native/ghidra/scripts/C64Prepare.java +7 -1
- package/src/native/types.ts +14 -8
- package/{skills/c64-source/scripts → src}/opcodes.ts +75 -3
- package/src/package.ts +13 -3
- package/src/platform.ts +70 -7
- package/src/project.ts +180 -3
- package/src/protocol/messages.ts +1 -1
- package/src/protocol/tools.ts +2 -16
- package/src/protocol/vice.ts +124 -15
- package/src/script.ts +46 -7
- package/skills/c64-assembler/scripts/compare.ts +0 -84
- package/skills/c64-source/scripts/source.ts +0 -595
package/src/knowledge/schema.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
// The
|
|
1
|
+
// The schema of the knowledge index, .c64-re-tools/local/knowledge.db.
|
|
2
|
+
// The index is made from the text in .c64-re-tools/knowledge/; an index with
|
|
3
|
+
// another version is emptied and made again.
|
|
2
4
|
|
|
3
5
|
import type { DatabaseSync } from "node:sqlite";
|
|
4
6
|
|
|
5
7
|
import { REFERENCE_KINDS, type ReferenceKind } from "../c64.ts";
|
|
6
|
-
import {
|
|
8
|
+
import { OPERAND_SELECTS } from "./expression.ts";
|
|
7
9
|
|
|
8
|
-
/** heuristic: 6502 idioms that the static analysis finds by pattern
|
|
10
|
+
/** heuristic: 6502 idioms that the static analysis finds by pattern; an analyzer origin. */
|
|
9
11
|
export const ORIGINS = ["user", "llm", "dxa", "ghidra", "heuristic"] as const;
|
|
10
12
|
export type Origin = (typeof ORIGINS)[number];
|
|
11
13
|
export const SYMBOL_KINDS = ["label", "routine", "variable", "data", "vector"] as const;
|
|
@@ -23,30 +25,50 @@ export const REGION_TYPES = [
|
|
|
23
25
|
"screen",
|
|
24
26
|
"bitmap",
|
|
25
27
|
"sprite",
|
|
28
|
+
"charset",
|
|
26
29
|
"workspace",
|
|
27
30
|
"unused",
|
|
28
31
|
"dual-use",
|
|
29
32
|
] as const;
|
|
30
33
|
export type RegionType = (typeof REGION_TYPES)[number];
|
|
31
|
-
/** Region types that are code: a JMP table is code too. dual-use is neither code nor data
|
|
34
|
+
/** Region types that are code: a JMP table is code too. dual-use is neither code nor data. */
|
|
32
35
|
export const CODE_REGION_TYPES: readonly RegionType[] = ["code", "jumps"];
|
|
33
36
|
/** Region types that take no element size: their elements are instructions. */
|
|
34
37
|
export const NO_ELEMENT_TYPES: readonly RegionType[] = ["code", "jumps"];
|
|
35
38
|
export const MAX_ELEMENT = 256;
|
|
36
|
-
/** What a revision can name as its evidence
|
|
39
|
+
/** What a revision can name as its evidence: a coverage run, a test, any project file, or a note. */
|
|
37
40
|
export const EVIDENCE_KINDS = ["coverage", "test", "file", "note"] as const;
|
|
38
41
|
export type EvidenceKind = (typeof EVIDENCE_KINDS)[number];
|
|
39
42
|
export const COMMENT_PLACEMENTS = ["line", "side"] as const;
|
|
40
43
|
export type CommentPlacement = (typeof COMMENT_PLACEMENTS)[number];
|
|
41
44
|
export { REFERENCE_KINDS, type ReferenceKind };
|
|
42
|
-
/**
|
|
45
|
+
/** The reference kind that only operand expressions write: code that builds the address of a symbol. */
|
|
46
|
+
export const POINTER_KIND: ReferenceKind = "pointer";
|
|
47
|
+
export { OPERAND_SELECTS, type OperandSelect } from "./expression.ts";
|
|
48
|
+
/** What a suppression blocks: a symbol address, a region range or one reference. */
|
|
43
49
|
export const SUPPRESSION_ENTITIES = ["symbol", "region", "reference"] as const;
|
|
44
50
|
export type SuppressionEntity = (typeof SUPPRESSION_ENTITIES)[number];
|
|
45
51
|
|
|
46
|
-
/** A module file of the generated source
|
|
52
|
+
/** A module file of the generated source: a plain name that ends in .asm, no directory. */
|
|
47
53
|
export const MODULE_FILE = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,59}\.asm$/;
|
|
54
|
+
/** A switch of the generated source: upper case, so it reads as a build option and not as a label. */
|
|
55
|
+
export const SWITCH_NAME = /^[A-Z][A-Z0-9_]{1,31}$/;
|
|
56
|
+
/** A macro name and a macro parameter. The body names a parameter with a dot in front: .dst. */
|
|
57
|
+
export const MACRO_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,31}$/;
|
|
58
|
+
export const MACRO_PARAM = /^[a-z_][a-z0-9_]{0,31}$/;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The kinds of a coverage range, one for each flag of a coverage map
|
|
62
|
+
* (COVERAGE in c64.ts), and wx: written and executed in the same run, so
|
|
63
|
+
* self-modified code still shows after the runs are joined.
|
|
64
|
+
*/
|
|
65
|
+
export const COVERAGE_KINDS = ["r", "w", "x", "rom-r", "rom-x", "io-r", "io-w", "io-x", "wx"] as const;
|
|
66
|
+
export type CoverageKind = (typeof COVERAGE_KINDS)[number];
|
|
67
|
+
/** The most bytes of one memory row: a changed byte changes one short line of memory.jsonl. */
|
|
68
|
+
export const MEMORY_ROW = 64;
|
|
69
|
+
|
|
48
70
|
/** Files that c64-source writes itself: no module can have these names. */
|
|
49
|
-
export const GENERATED_FILES = ["main.asm", "loader.asm", "constants.asm"] as const;
|
|
71
|
+
export const GENERATED_FILES = ["main.asm", "loader.asm", "constants.asm", "macros.asm"] as const;
|
|
50
72
|
|
|
51
73
|
/** True for a generated file name in any letter case: the file systems of macOS and Windows ignore case. */
|
|
52
74
|
export function isGeneratedFile(file: string): boolean {
|
|
@@ -54,7 +76,7 @@ export function isGeneratedFile(file: string): boolean {
|
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
/**
|
|
57
|
-
* The address space of a knowledge row
|
|
79
|
+
* The address space of a knowledge row: the C64, the 1541 drive, or a
|
|
58
80
|
* cartridge bank. Drive code and banks reuse C64 addresses.
|
|
59
81
|
*/
|
|
60
82
|
export const SPACE = /^(c64|drive8|bank(0|[1-9][0-9]?|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$/;
|
|
@@ -64,13 +86,13 @@ const SPACE_CHECK = "space IN ('c64', 'drive8') OR space GLOB 'bank[0-9]' OR spa
|
|
|
64
86
|
|
|
65
87
|
const list = (values: readonly string[]) => values.map((value) => `'${value}'`).join(", ");
|
|
66
88
|
|
|
67
|
-
/** The schema version this build reads and writes. */
|
|
68
|
-
export const SCHEMA_VERSION = 6;
|
|
69
|
-
|
|
70
89
|
/**
|
|
71
90
|
* Creates the schema in an empty database. Knowledge rows are temporal: a
|
|
72
91
|
* change closes the current row (valid_to_revision) and inserts a new one in
|
|
73
|
-
* the same revision.
|
|
92
|
+
* the same revision. Revision and row ids are the text ids of the knowledge
|
|
93
|
+
* files; they sort in time order. The rules for current rows (one symbol at
|
|
94
|
+
* an address, ...) are not unique indexes, because a merged text can break
|
|
95
|
+
* them: the conflict check finds those rows (conflicts.ts).
|
|
74
96
|
*/
|
|
75
97
|
function createSchema(db: DatabaseSync): void {
|
|
76
98
|
db.exec(`
|
|
@@ -79,7 +101,7 @@ function createSchema(db: DatabaseSync): void {
|
|
|
79
101
|
value TEXT NOT NULL
|
|
80
102
|
);
|
|
81
103
|
CREATE TABLE revisions (
|
|
82
|
-
id
|
|
104
|
+
id TEXT PRIMARY KEY,
|
|
83
105
|
created_at TEXT NOT NULL,
|
|
84
106
|
origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
|
|
85
107
|
operation TEXT NOT NULL,
|
|
@@ -88,65 +110,78 @@ function createSchema(db: DatabaseSync): void {
|
|
|
88
110
|
description TEXT
|
|
89
111
|
);
|
|
90
112
|
CREATE TABLE symbols (
|
|
91
|
-
id
|
|
113
|
+
id TEXT PRIMARY KEY,
|
|
92
114
|
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
93
115
|
address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
|
|
94
116
|
name TEXT NOT NULL CHECK (length(name) > 0),
|
|
95
117
|
kind TEXT NOT NULL CHECK (kind IN (${list(SYMBOL_KINDS)})),
|
|
96
118
|
origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
|
|
97
|
-
valid_from_revision
|
|
98
|
-
valid_to_revision
|
|
119
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
120
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
99
121
|
);
|
|
100
|
-
CREATE
|
|
101
|
-
CREATE
|
|
122
|
+
CREATE INDEX symbols_current_address ON symbols (space, address) WHERE valid_to_revision IS NULL;
|
|
123
|
+
CREATE INDEX symbols_current_name ON symbols (space, name) WHERE valid_to_revision IS NULL;
|
|
102
124
|
CREATE INDEX symbols_address ON symbols (space, address);
|
|
125
|
+
CREATE TABLE analyzer_names (
|
|
126
|
+
id TEXT PRIMARY KEY,
|
|
127
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
128
|
+
address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
|
|
129
|
+
name TEXT NOT NULL CHECK (length(name) > 0),
|
|
130
|
+
kind TEXT NOT NULL CHECK (kind IN (${list(SYMBOL_KINDS)})),
|
|
131
|
+
origin TEXT NOT NULL CHECK (origin IN ('dxa', 'ghidra', 'heuristic')),
|
|
132
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
133
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
134
|
+
);
|
|
135
|
+
CREATE INDEX analyzer_names_address ON analyzer_names (space, address);
|
|
136
|
+
CREATE INDEX analyzer_names_name ON analyzer_names (space, name);
|
|
103
137
|
CREATE TABLE regions (
|
|
104
|
-
id
|
|
138
|
+
id TEXT PRIMARY KEY,
|
|
105
139
|
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
106
140
|
start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
|
|
107
141
|
end_address INTEGER NOT NULL CHECK (end_address BETWEEN start_address AND 65535),
|
|
108
142
|
type TEXT NOT NULL CHECK (type IN (${list(REGION_TYPES)})),
|
|
109
143
|
element INTEGER CHECK (element BETWEEN 1 AND ${MAX_ELEMENT}),
|
|
110
144
|
origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
|
|
111
|
-
valid_from_revision
|
|
112
|
-
valid_to_revision
|
|
145
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
146
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
113
147
|
);
|
|
114
148
|
CREATE INDEX regions_start ON regions (space, start_address);
|
|
115
149
|
CREATE TABLE evidence (
|
|
116
150
|
id INTEGER PRIMARY KEY,
|
|
117
|
-
revision
|
|
151
|
+
revision TEXT NOT NULL REFERENCES revisions (id),
|
|
118
152
|
kind TEXT NOT NULL CHECK (kind IN (${list(EVIDENCE_KINDS)})),
|
|
119
153
|
ref TEXT NOT NULL CHECK (length(ref) > 0),
|
|
120
|
-
sha256 TEXT CHECK (sha256 IS NULL OR length(sha256) = 64)
|
|
154
|
+
sha256 TEXT CHECK (sha256 IS NULL OR length(sha256) = 64),
|
|
155
|
+
space TEXT CHECK (space IS NULL OR ${SPACE_CHECK})
|
|
121
156
|
);
|
|
122
157
|
CREATE INDEX evidence_revision ON evidence (revision);
|
|
123
158
|
CREATE TABLE comments (
|
|
124
|
-
id
|
|
159
|
+
id TEXT PRIMARY KEY,
|
|
125
160
|
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
126
161
|
address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
|
|
127
162
|
placement TEXT NOT NULL CHECK (placement IN (${list(COMMENT_PLACEMENTS)})),
|
|
128
163
|
text TEXT NOT NULL CHECK (length(text) > 0),
|
|
129
164
|
origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
|
|
130
|
-
valid_from_revision
|
|
131
|
-
valid_to_revision
|
|
165
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
166
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
132
167
|
);
|
|
133
|
-
CREATE
|
|
168
|
+
CREATE INDEX comments_current ON comments (space, address, placement) WHERE valid_to_revision IS NULL;
|
|
134
169
|
CREATE INDEX comments_address ON comments (space, address);
|
|
135
170
|
CREATE TABLE "references" (
|
|
136
|
-
id
|
|
171
|
+
id TEXT PRIMARY KEY,
|
|
137
172
|
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
138
173
|
from_address INTEGER NOT NULL CHECK (from_address BETWEEN 0 AND 65535),
|
|
139
174
|
to_address INTEGER NOT NULL CHECK (to_address BETWEEN 0 AND 65535),
|
|
140
175
|
kind TEXT NOT NULL CHECK (kind IN (${list(REFERENCE_KINDS)})),
|
|
141
176
|
origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
|
|
142
|
-
valid_from_revision
|
|
143
|
-
valid_to_revision
|
|
177
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
178
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
144
179
|
);
|
|
145
|
-
CREATE
|
|
180
|
+
CREATE INDEX references_current ON "references" (space, from_address, to_address, kind) WHERE valid_to_revision IS NULL;
|
|
146
181
|
CREATE INDEX references_from ON "references" (space, from_address);
|
|
147
182
|
CREATE INDEX references_to ON "references" (space, to_address);
|
|
148
183
|
CREATE TABLE suppressions (
|
|
149
|
-
id
|
|
184
|
+
id TEXT PRIMARY KEY,
|
|
150
185
|
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
151
186
|
entity TEXT NOT NULL CHECK (entity IN (${list(SUPPRESSION_ENTITIES)})),
|
|
152
187
|
start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
|
|
@@ -154,76 +189,165 @@ function createSchema(db: DatabaseSync): void {
|
|
|
154
189
|
to_address INTEGER CHECK (to_address BETWEEN 0 AND 65535),
|
|
155
190
|
kind TEXT CHECK (kind IN (${list(REFERENCE_KINDS)})),
|
|
156
191
|
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
157
|
-
valid_from_revision
|
|
158
|
-
valid_to_revision
|
|
192
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
193
|
+
valid_to_revision TEXT REFERENCES revisions (id),
|
|
159
194
|
CHECK ((entity = 'reference') = (to_address IS NOT NULL AND kind IS NOT NULL)),
|
|
160
195
|
CHECK (entity = 'region' OR start_address = end_address)
|
|
161
196
|
);
|
|
162
197
|
CREATE INDEX suppressions_start ON suppressions (space, start_address);
|
|
163
198
|
CREATE TABLE modules (
|
|
164
|
-
id
|
|
199
|
+
id TEXT PRIMARY KEY,
|
|
165
200
|
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
166
201
|
start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
|
|
167
202
|
file TEXT NOT NULL CHECK (length(file) > 0),
|
|
168
203
|
title TEXT CHECK (title IS NULL OR length(title) > 0),
|
|
169
204
|
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
170
|
-
valid_from_revision
|
|
171
|
-
valid_to_revision
|
|
205
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
206
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
207
|
+
);
|
|
208
|
+
CREATE INDEX modules_current_start ON modules (space, start_address) WHERE valid_to_revision IS NULL;
|
|
209
|
+
CREATE INDEX modules_current_file ON modules (space, file) WHERE valid_to_revision IS NULL;
|
|
210
|
+
CREATE TABLE operands (
|
|
211
|
+
id TEXT PRIMARY KEY,
|
|
212
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
213
|
+
address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
|
|
214
|
+
selector TEXT NOT NULL CHECK (selector IN (${list(OPERAND_SELECTS)})),
|
|
215
|
+
symbol TEXT CHECK (symbol IS NULL OR length(symbol) > 0),
|
|
216
|
+
offset INTEGER NOT NULL DEFAULT 0,
|
|
217
|
+
expression TEXT CHECK (expression IS NULL OR length(expression) > 0),
|
|
218
|
+
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
219
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
220
|
+
valid_to_revision TEXT REFERENCES revisions (id),
|
|
221
|
+
CHECK ((selector = 'expr') = (expression IS NOT NULL AND symbol IS NULL AND offset = 0)),
|
|
222
|
+
CHECK (selector = 'expr' OR symbol IS NOT NULL)
|
|
223
|
+
);
|
|
224
|
+
CREATE INDEX operands_current ON operands (space, address) WHERE valid_to_revision IS NULL;
|
|
225
|
+
CREATE INDEX operands_address ON operands (space, address);
|
|
226
|
+
CREATE TABLE patches (
|
|
227
|
+
id TEXT PRIMARY KEY,
|
|
228
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
229
|
+
start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
|
|
230
|
+
end_address INTEGER NOT NULL CHECK (end_address BETWEEN start_address AND 65535),
|
|
231
|
+
switch_name TEXT NOT NULL CHECK (length(switch_name) > 0),
|
|
232
|
+
code TEXT NOT NULL,
|
|
233
|
+
comment TEXT CHECK (comment IS NULL OR length(comment) > 0),
|
|
234
|
+
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
235
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
236
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
237
|
+
);
|
|
238
|
+
CREATE INDEX patches_start ON patches (space, start_address);
|
|
239
|
+
CREATE TABLE macros (
|
|
240
|
+
id TEXT PRIMARY KEY,
|
|
241
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
242
|
+
name TEXT NOT NULL CHECK (length(name) > 0),
|
|
243
|
+
params TEXT NOT NULL,
|
|
244
|
+
body TEXT NOT NULL CHECK (length(body) > 0),
|
|
245
|
+
comment TEXT CHECK (comment IS NULL OR length(comment) > 0),
|
|
246
|
+
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
247
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
248
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
249
|
+
);
|
|
250
|
+
CREATE INDEX macros_name ON macros (space, name);
|
|
251
|
+
CREATE TABLE macro_uses (
|
|
252
|
+
id TEXT PRIMARY KEY,
|
|
253
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
254
|
+
start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
|
|
255
|
+
end_address INTEGER NOT NULL CHECK (end_address BETWEEN start_address AND 65535),
|
|
256
|
+
macro TEXT NOT NULL CHECK (length(macro) > 0),
|
|
257
|
+
args TEXT NOT NULL,
|
|
258
|
+
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
259
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
260
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
261
|
+
);
|
|
262
|
+
CREATE INDEX macro_uses_start ON macro_uses (space, start_address);
|
|
263
|
+
CREATE TABLE memory (
|
|
264
|
+
id TEXT PRIMARY KEY,
|
|
265
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
266
|
+
address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
|
|
267
|
+
bytes TEXT NOT NULL CHECK (length(bytes) BETWEEN 2 AND ${MEMORY_ROW * 2} AND length(bytes) % 2 = 0 AND address + length(bytes) / 2 <= 65536),
|
|
268
|
+
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
269
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
270
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
271
|
+
);
|
|
272
|
+
CREATE INDEX memory_current ON memory (space, address) WHERE valid_to_revision IS NULL;
|
|
273
|
+
CREATE TABLE entries (
|
|
274
|
+
id TEXT PRIMARY KEY,
|
|
275
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
276
|
+
address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
|
|
277
|
+
state TEXT,
|
|
278
|
+
stages TEXT,
|
|
279
|
+
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
280
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
281
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
172
282
|
);
|
|
173
|
-
CREATE
|
|
174
|
-
CREATE
|
|
283
|
+
CREATE INDEX entries_current ON entries (space) WHERE valid_to_revision IS NULL;
|
|
284
|
+
CREATE TABLE coverage (
|
|
285
|
+
id TEXT PRIMARY KEY,
|
|
286
|
+
space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
|
|
287
|
+
kind TEXT NOT NULL CHECK (kind IN (${list(COVERAGE_KINDS)})),
|
|
288
|
+
start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
|
|
289
|
+
end_address INTEGER NOT NULL CHECK (end_address BETWEEN start_address AND 65535),
|
|
290
|
+
origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
|
|
291
|
+
valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
|
|
292
|
+
valid_to_revision TEXT REFERENCES revisions (id)
|
|
293
|
+
);
|
|
294
|
+
CREATE INDEX coverage_current ON coverage (space, kind) WHERE valid_to_revision IS NULL;
|
|
295
|
+
CREATE VIEW current_memory AS SELECT * FROM memory WHERE valid_to_revision IS NULL;
|
|
296
|
+
CREATE VIEW current_entries AS SELECT * FROM entries WHERE valid_to_revision IS NULL;
|
|
297
|
+
CREATE VIEW current_coverage AS SELECT * FROM coverage WHERE valid_to_revision IS NULL;
|
|
175
298
|
CREATE VIEW current_symbols AS SELECT * FROM symbols WHERE valid_to_revision IS NULL;
|
|
299
|
+
CREATE VIEW current_analyzer_names AS SELECT * FROM analyzer_names WHERE valid_to_revision IS NULL;
|
|
176
300
|
CREATE VIEW current_regions AS SELECT * FROM regions WHERE valid_to_revision IS NULL;
|
|
177
301
|
CREATE VIEW current_comments AS SELECT * FROM comments WHERE valid_to_revision IS NULL;
|
|
178
302
|
CREATE VIEW current_references AS SELECT * FROM "references" WHERE valid_to_revision IS NULL;
|
|
179
303
|
CREATE VIEW current_suppressions AS SELECT * FROM suppressions WHERE valid_to_revision IS NULL;
|
|
180
304
|
CREATE VIEW current_modules AS SELECT * FROM modules WHERE valid_to_revision IS NULL;
|
|
181
|
-
|
|
305
|
+
CREATE VIEW current_operands AS SELECT * FROM operands WHERE valid_to_revision IS NULL;
|
|
306
|
+
CREATE VIEW current_patches AS SELECT * FROM patches WHERE valid_to_revision IS NULL;
|
|
307
|
+
CREATE VIEW current_macros AS SELECT * FROM macros WHERE valid_to_revision IS NULL;
|
|
308
|
+
CREATE VIEW current_macro_uses AS SELECT * FROM macro_uses WHERE valid_to_revision IS NULL;
|
|
182
309
|
`);
|
|
183
310
|
}
|
|
184
311
|
|
|
185
|
-
|
|
312
|
+
/** The version of the index this build makes. An index with another version is emptied and made again. */
|
|
313
|
+
export const INDEX_VERSION = 6;
|
|
314
|
+
|
|
315
|
+
function indexVersion(db: DatabaseSync): number | undefined {
|
|
186
316
|
const hasMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'meta'").get();
|
|
187
|
-
if (hasMeta === undefined)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return 0;
|
|
191
|
-
}
|
|
192
|
-
const row = db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get() as { value: unknown } | undefined;
|
|
193
|
-
if (row === undefined) throw new KnowledgeError("invalid-database", "The knowledge database has no schema version.");
|
|
194
|
-
const value = String(row.value);
|
|
195
|
-
if (!/^(0|[1-9][0-9]{0,8})$/.test(value)) throw new KnowledgeError("invalid-database", `The schema version of the knowledge database is not a whole number: ${JSON.stringify(value)}.`);
|
|
196
|
-
return Number(value);
|
|
317
|
+
if (hasMeta === undefined) return undefined;
|
|
318
|
+
const row = db.prepare("SELECT value FROM meta WHERE key = 'index_version'").get() as { value: unknown } | undefined;
|
|
319
|
+
return row === undefined ? undefined : Number(row.value);
|
|
197
320
|
}
|
|
198
321
|
|
|
199
|
-
/**
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
`The knowledge database has schema version ${version}. This c64-re-tools reads and writes only version ${SCHEMA_VERSION}. Use the c64-re-tools release that wrote it.`,
|
|
205
|
-
);
|
|
206
|
-
}
|
|
322
|
+
/** Drops every table and view of the database; the indexes go with their tables. */
|
|
323
|
+
function dropAll(db: DatabaseSync): void {
|
|
324
|
+
const objects = db.prepare("SELECT type, name FROM sqlite_master WHERE name NOT LIKE 'sqlite_%' AND type IN ('view', 'table')").all() as Array<{ type: string; name: string }>;
|
|
325
|
+
for (const { name } of objects.filter((object) => object.type === "view")) db.exec(`DROP VIEW "${name}"`);
|
|
326
|
+
for (const { name } of objects.filter((object) => object.type === "table")) db.exec(`DROP TABLE "${name}"`);
|
|
207
327
|
}
|
|
208
328
|
|
|
209
329
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
330
|
+
* Makes the index schema of this build. An empty database gets it; a database
|
|
331
|
+
* with another index version, or one that is not an index, is emptied first.
|
|
332
|
+
* The version is read again under the write lock, so two processes that open
|
|
333
|
+
* a new index at the same time make the schema once.
|
|
213
334
|
*/
|
|
214
335
|
export function ensureSchema(db: DatabaseSync): void {
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
db
|
|
223
|
-
|
|
224
|
-
db.exec("ROLLBACK");
|
|
225
|
-
throw error;
|
|
336
|
+
if (indexVersion(db) === INDEX_VERSION) return;
|
|
337
|
+
// PRAGMA foreign_keys has no effect inside a transaction: switch it before.
|
|
338
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
339
|
+
db.exec("BEGIN IMMEDIATE");
|
|
340
|
+
try {
|
|
341
|
+
if (indexVersion(db) !== INDEX_VERSION) {
|
|
342
|
+
dropAll(db);
|
|
343
|
+
createSchema(db);
|
|
344
|
+
db.prepare("INSERT INTO meta (key, value) VALUES ('index_version', ?)").run(String(INDEX_VERSION));
|
|
226
345
|
}
|
|
346
|
+
db.exec("COMMIT");
|
|
347
|
+
} catch (error) {
|
|
348
|
+
db.exec("ROLLBACK");
|
|
349
|
+
throw error;
|
|
350
|
+
} finally {
|
|
351
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
227
352
|
}
|
|
228
|
-
checkVersion(schemaVersion(db));
|
|
229
353
|
}
|