@henols/c64-re-tools 2.0.0-rc.9 → 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 +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 +7 -4
- package/src/cli/status.ts +70 -3
- package/src/graphics.ts +158 -0
- package/src/host/main.ts +18 -4
- 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/skills/c64-assembler/scripts/compare.ts +0 -84
- package/skills/c64-source/scripts/source.ts +0 -595
package/src/knowledge/read.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
// Current-state knowledge queries. A project without
|
|
2
|
-
// reads as empty knowledge. Each query reads one address space
|
|
1
|
+
// Current-state knowledge queries. A project without knowledge
|
|
2
|
+
// reads as empty knowledge. Each query reads one address space; the
|
|
3
3
|
// default is the C64.
|
|
4
4
|
|
|
5
5
|
import type { DatabaseSync } from "node:sqlite";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { KnowledgeError } from "./database.ts";
|
|
8
|
+
import { DEFAULT_SPACE, type CommentPlacement, type OperandSelect, type Origin, type ReferenceKind, type RegionType, type Space, type SuppressionEntity, type SymbolKind } from "./schema.ts";
|
|
9
|
+
import { NO_REVISION } from "./text.ts";
|
|
8
10
|
|
|
9
11
|
export interface SymbolRow {
|
|
10
12
|
space: Space;
|
|
@@ -12,7 +14,7 @@ export interface SymbolRow {
|
|
|
12
14
|
name: string;
|
|
13
15
|
kind: SymbolKind;
|
|
14
16
|
origin: Origin;
|
|
15
|
-
revision:
|
|
17
|
+
revision: string;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export interface RegionRow {
|
|
@@ -20,10 +22,10 @@ export interface RegionRow {
|
|
|
20
22
|
start: number;
|
|
21
23
|
end: number;
|
|
22
24
|
type: RegionType;
|
|
23
|
-
/** Bytes per element of a table; the length is a multiple of it
|
|
25
|
+
/** Bytes per element of a table; the length is a multiple of it. */
|
|
24
26
|
element?: number;
|
|
25
27
|
origin: Origin;
|
|
26
|
-
revision:
|
|
28
|
+
revision: string;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export interface CommentRow {
|
|
@@ -32,7 +34,7 @@ export interface CommentRow {
|
|
|
32
34
|
placement: CommentPlacement;
|
|
33
35
|
text: string;
|
|
34
36
|
origin: Origin;
|
|
35
|
-
revision:
|
|
37
|
+
revision: string;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export interface ReferenceRow {
|
|
@@ -41,10 +43,10 @@ export interface ReferenceRow {
|
|
|
41
43
|
to: number;
|
|
42
44
|
kind: ReferenceKind;
|
|
43
45
|
origin: Origin;
|
|
44
|
-
revision:
|
|
46
|
+
revision: string;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
/** A place that a user/LLM removal blocks for every analyzer
|
|
49
|
+
/** A place that a user/LLM removal blocks for every analyzer. `to` and `kind` are set for a reference only. */
|
|
48
50
|
export interface SuppressionRow {
|
|
49
51
|
space: Space;
|
|
50
52
|
entity: SuppressionEntity;
|
|
@@ -53,19 +55,76 @@ export interface SuppressionRow {
|
|
|
53
55
|
to?: number;
|
|
54
56
|
kind?: ReferenceKind;
|
|
55
57
|
origin: Extract<Origin, "user" | "llm">;
|
|
56
|
-
revision:
|
|
58
|
+
revision: string;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
/** Where a file of the generated source starts
|
|
61
|
+
/** Where a file of the generated source starts: it runs to the next module or the end of its segment. */
|
|
60
62
|
export interface ModuleRow {
|
|
61
63
|
space: Space;
|
|
62
64
|
start: number;
|
|
63
65
|
file: string;
|
|
64
66
|
title?: string;
|
|
65
67
|
origin: Extract<Origin, "user" | "llm">;
|
|
66
|
-
revision:
|
|
68
|
+
revision: string;
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
/**
|
|
72
|
+
* How the operand of the instruction at `address` is written: a byte
|
|
73
|
+
* (lo, hi) or the value of a symbol plus an offset, or expression text (expr).
|
|
74
|
+
*/
|
|
75
|
+
export interface OperandRow {
|
|
76
|
+
space: Space;
|
|
77
|
+
address: number;
|
|
78
|
+
select: OperandSelect;
|
|
79
|
+
symbol?: string;
|
|
80
|
+
/** Added to the symbol before lo or hi takes its byte; 0 for expr. */
|
|
81
|
+
offset: number;
|
|
82
|
+
expression?: string;
|
|
83
|
+
origin: Extract<Origin, "user" | "llm">;
|
|
84
|
+
revision: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Code that replaces [start, end] when the build defines the switch as 1
|
|
89
|
+
*. The code fits the range; NOP fills the rest. Empty code is only NOP.
|
|
90
|
+
*/
|
|
91
|
+
export interface PatchRow {
|
|
92
|
+
space: Space;
|
|
93
|
+
start: number;
|
|
94
|
+
end: number;
|
|
95
|
+
switch: string;
|
|
96
|
+
code: string;
|
|
97
|
+
comment?: string;
|
|
98
|
+
origin: Extract<Origin, "user" | "llm">;
|
|
99
|
+
revision: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** An ACME macro of the generated source. The body names a parameter with a dot in front: .dst. */
|
|
103
|
+
export interface MacroRow {
|
|
104
|
+
space: Space;
|
|
105
|
+
name: string;
|
|
106
|
+
params: string[];
|
|
107
|
+
body: string;
|
|
108
|
+
comment?: string;
|
|
109
|
+
origin: Extract<Origin, "user" | "llm">;
|
|
110
|
+
revision: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The instructions in [start, end] written as one use of a macro, with one expression for each parameter. */
|
|
114
|
+
export interface MacroUseRow {
|
|
115
|
+
space: Space;
|
|
116
|
+
start: number;
|
|
117
|
+
end: number;
|
|
118
|
+
macro: string;
|
|
119
|
+
args: string[];
|
|
120
|
+
origin: Extract<Origin, "user" | "llm">;
|
|
121
|
+
revision: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** A list of params or args as the knowledge stores it: the items with ", " between them. */
|
|
125
|
+
export const listText = (items: readonly string[]) => items.join(", ");
|
|
126
|
+
const items = (text: string) => (text.trim() === "" ? [] : text.split(",").map((item) => item.trim()));
|
|
127
|
+
|
|
69
128
|
type Raw = Record<string, unknown>;
|
|
70
129
|
|
|
71
130
|
export const SYMBOL_COLUMNS = "space, address, name, kind, origin, valid_from_revision";
|
|
@@ -74,9 +133,13 @@ export const COMMENT_COLUMNS = "space, address, placement, text, origin, valid_f
|
|
|
74
133
|
export const REFERENCE_COLUMNS = "space, from_address, to_address, kind, origin, valid_from_revision";
|
|
75
134
|
export const SUPPRESSION_COLUMNS = "space, entity, start_address, end_address, to_address, kind, origin, valid_from_revision";
|
|
76
135
|
export const MODULE_COLUMNS = "space, start_address, file, title, origin, valid_from_revision";
|
|
136
|
+
export const OPERAND_COLUMNS = "space, address, selector, symbol, offset, expression, origin, valid_from_revision";
|
|
137
|
+
export const PATCH_COLUMNS = "space, start_address, end_address, switch_name, code, comment, origin, valid_from_revision";
|
|
138
|
+
export const MACRO_COLUMNS = "space, name, params, body, comment, origin, valid_from_revision";
|
|
139
|
+
export const MACRO_USE_COLUMNS = "space, start_address, end_address, macro, args, origin, valid_from_revision";
|
|
77
140
|
|
|
78
141
|
export function toSymbol(row: Raw): SymbolRow {
|
|
79
|
-
return { space: row.space as Space, address: row.address as number, name: row.name as string, kind: row.kind as SymbolKind, origin: row.origin as Origin, revision: row.valid_from_revision as
|
|
142
|
+
return { space: row.space as Space, address: row.address as number, name: row.name as string, kind: row.kind as SymbolKind, origin: row.origin as Origin, revision: row.valid_from_revision as string };
|
|
80
143
|
}
|
|
81
144
|
|
|
82
145
|
export function toRegion(row: Raw): RegionRow {
|
|
@@ -86,7 +149,7 @@ export function toRegion(row: Raw): RegionRow {
|
|
|
86
149
|
end: row.end_address as number,
|
|
87
150
|
type: row.type as RegionType,
|
|
88
151
|
origin: row.origin as Origin,
|
|
89
|
-
revision: row.valid_from_revision as
|
|
152
|
+
revision: row.valid_from_revision as string,
|
|
90
153
|
};
|
|
91
154
|
if (row.element !== null && row.element !== undefined) region.element = row.element as number;
|
|
92
155
|
return region;
|
|
@@ -99,12 +162,12 @@ export function toComment(row: Raw): CommentRow {
|
|
|
99
162
|
placement: row.placement as CommentPlacement,
|
|
100
163
|
text: row.text as string,
|
|
101
164
|
origin: row.origin as Origin,
|
|
102
|
-
revision: row.valid_from_revision as
|
|
165
|
+
revision: row.valid_from_revision as string,
|
|
103
166
|
};
|
|
104
167
|
}
|
|
105
168
|
|
|
106
169
|
export function toReference(row: Raw): ReferenceRow {
|
|
107
|
-
return { space: row.space as Space, from: row.from_address as number, to: row.to_address as number, kind: row.kind as ReferenceKind, origin: row.origin as Origin, revision: row.valid_from_revision as
|
|
170
|
+
return { space: row.space as Space, from: row.from_address as number, to: row.to_address as number, kind: row.kind as ReferenceKind, origin: row.origin as Origin, revision: row.valid_from_revision as string };
|
|
108
171
|
}
|
|
109
172
|
|
|
110
173
|
export function toSuppression(row: Raw): SuppressionRow {
|
|
@@ -114,7 +177,7 @@ export function toSuppression(row: Raw): SuppressionRow {
|
|
|
114
177
|
start: row.start_address as number,
|
|
115
178
|
end: row.end_address as number,
|
|
116
179
|
origin: row.origin as SuppressionRow["origin"],
|
|
117
|
-
revision: row.valid_from_revision as
|
|
180
|
+
revision: row.valid_from_revision as string,
|
|
118
181
|
};
|
|
119
182
|
if (row.to_address !== null) suppression.to = row.to_address as number;
|
|
120
183
|
if (row.kind !== null) suppression.kind = row.kind as ReferenceKind;
|
|
@@ -122,14 +185,80 @@ export function toSuppression(row: Raw): SuppressionRow {
|
|
|
122
185
|
}
|
|
123
186
|
|
|
124
187
|
export function toModule(row: Raw): ModuleRow {
|
|
125
|
-
const module: ModuleRow = { space: row.space as Space, start: row.start_address as number, file: row.file as string, origin: row.origin as ModuleRow["origin"], revision: row.valid_from_revision as
|
|
188
|
+
const module: ModuleRow = { space: row.space as Space, start: row.start_address as number, file: row.file as string, origin: row.origin as ModuleRow["origin"], revision: row.valid_from_revision as string };
|
|
126
189
|
if (row.title !== null && row.title !== undefined) module.title = row.title as string;
|
|
127
190
|
return module;
|
|
128
191
|
}
|
|
129
192
|
|
|
130
|
-
export function
|
|
131
|
-
|
|
132
|
-
|
|
193
|
+
export function toOperand(row: Raw): OperandRow {
|
|
194
|
+
const operand: OperandRow = {
|
|
195
|
+
space: row.space as Space,
|
|
196
|
+
address: row.address as number,
|
|
197
|
+
select: row.selector as OperandSelect,
|
|
198
|
+
offset: row.offset as number,
|
|
199
|
+
origin: row.origin as OperandRow["origin"],
|
|
200
|
+
revision: row.valid_from_revision as string,
|
|
201
|
+
};
|
|
202
|
+
if (row.symbol !== null && row.symbol !== undefined) operand.symbol = row.symbol as string;
|
|
203
|
+
if (row.expression !== null && row.expression !== undefined) operand.expression = row.expression as string;
|
|
204
|
+
return operand;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function toPatch(row: Raw): PatchRow {
|
|
208
|
+
const patch: PatchRow = {
|
|
209
|
+
space: row.space as Space,
|
|
210
|
+
start: row.start_address as number,
|
|
211
|
+
end: row.end_address as number,
|
|
212
|
+
switch: row.switch_name as string,
|
|
213
|
+
code: row.code as string,
|
|
214
|
+
origin: row.origin as PatchRow["origin"],
|
|
215
|
+
revision: row.valid_from_revision as string,
|
|
216
|
+
};
|
|
217
|
+
if (row.comment !== null && row.comment !== undefined) patch.comment = row.comment as string;
|
|
218
|
+
return patch;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function toMacro(row: Raw): MacroRow {
|
|
222
|
+
const macro: MacroRow = { space: row.space as Space, name: row.name as string, params: items(row.params as string), body: row.body as string, origin: row.origin as MacroRow["origin"], revision: row.valid_from_revision as string };
|
|
223
|
+
if (row.comment !== null && row.comment !== undefined) macro.comment = row.comment as string;
|
|
224
|
+
return macro;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function toMacroUse(row: Raw): MacroUseRow {
|
|
228
|
+
return {
|
|
229
|
+
space: row.space as Space,
|
|
230
|
+
start: row.start_address as number,
|
|
231
|
+
end: row.end_address as number,
|
|
232
|
+
macro: row.macro as string,
|
|
233
|
+
args: items(row.args as string),
|
|
234
|
+
origin: row.origin as MacroUseRow["origin"],
|
|
235
|
+
revision: row.valid_from_revision as string,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** The id of the newest revision, or "0" when the knowledge has none. */
|
|
240
|
+
export function currentRevision(db: DatabaseSync | undefined): string {
|
|
241
|
+
if (db === undefined) return "0";
|
|
242
|
+
return (db.prepare("SELECT max(id) AS id FROM revisions").get() as { id: string | null }).id ?? "0";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** At most this many ids are named when a start of an id fits several revisions. */
|
|
246
|
+
const NAMED_REVISIONS = 5;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The revision that `text` names: "0" (no revision), a full revision id,
|
|
250
|
+
* or a start of an id that only one revision has.
|
|
251
|
+
*/
|
|
252
|
+
export function resolveRevision(db: DatabaseSync | undefined, text: string): string {
|
|
253
|
+
if (text === NO_REVISION) return NO_REVISION;
|
|
254
|
+
if (!/^[0-9a-zT.-]+$/.test(text)) throw new KnowledgeError("invalid-input", `${JSON.stringify(text)} is not a revision id. A revision id looks like 20261015T214501.123-k3f9qz; its start is enough when only one revision has it.`);
|
|
255
|
+
const found = (db?.prepare("SELECT id FROM revisions WHERE substr(id, 1, ?) = ? ORDER BY id LIMIT ?").all(text.length, text, NAMED_REVISIONS + 1) ?? []) as Array<{ id: string }>;
|
|
256
|
+
if (found.length === 0) throw new KnowledgeError("not-found", `There is no revision ${text}.`);
|
|
257
|
+
if (found.length > 1) {
|
|
258
|
+
const named = found.slice(0, NAMED_REVISIONS).map((row) => row.id).join(", ");
|
|
259
|
+
throw new KnowledgeError("invalid-input", `${text} is the start of more than one revision: ${named}${found.length > NAMED_REVISIONS ? ", ..." : ""}. Give more of the id.`);
|
|
260
|
+
}
|
|
261
|
+
return found[0]!.id;
|
|
133
262
|
}
|
|
134
263
|
|
|
135
264
|
export function symbolAt(db: DatabaseSync | undefined, address: number, space: Space = DEFAULT_SPACE): SymbolRow | undefined {
|
|
@@ -142,6 +271,31 @@ export function symbolNamed(db: DatabaseSync | undefined, name: string, space: S
|
|
|
142
271
|
return row === undefined ? undefined : toSymbol(row);
|
|
143
272
|
}
|
|
144
273
|
|
|
274
|
+
/** The analyzer names at an address: one for each analyzer, in the order heuristic, Ghidra, DXA. */
|
|
275
|
+
export function analyzerNamesAt(db: DatabaseSync | undefined, address: number, space: Space = DEFAULT_SPACE): SymbolRow[] {
|
|
276
|
+
if (db === undefined) return [];
|
|
277
|
+
return (db.prepare(`SELECT ${SYMBOL_COLUMNS} FROM current_analyzer_names WHERE space = ? AND address = ?`).all(space, address) as Raw[]).map(toSymbol).sort(byAnalyzer);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** The analyzer names with this name: more than one when two analyzers gave it to different addresses. */
|
|
281
|
+
export function analyzerNamed(db: DatabaseSync | undefined, name: string, space: Space = DEFAULT_SPACE): SymbolRow[] {
|
|
282
|
+
if (db === undefined) return [];
|
|
283
|
+
return (db.prepare(`SELECT ${SYMBOL_COLUMNS} FROM current_analyzer_names WHERE space = ? AND name = ?`).all(space, name) as Raw[]).map(toSymbol).sort(byAnalyzer);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** The analyzers in the order that their names are used when no other name exists. */
|
|
287
|
+
export const ANALYZER_NAME_ORDER: readonly Origin[] = ["heuristic", "ghidra", "dxa"];
|
|
288
|
+
const byAnalyzer = (a: SymbolRow, b: SymbolRow) => a.address - b.address || ANALYZER_NAME_ORDER.indexOf(a.origin) - ANALYZER_NAME_ORDER.indexOf(b.origin);
|
|
289
|
+
|
|
290
|
+
/** Current analyzer names in a range, in address order and then heuristic, Ghidra, DXA. */
|
|
291
|
+
export function listAnalyzerNames(db: DatabaseSync | undefined, range: Range = ALL, kind?: SymbolKind, origins?: OriginFilter, space: Space = DEFAULT_SPACE): SymbolRow[] {
|
|
292
|
+
if (db === undefined) return [];
|
|
293
|
+
return (db.prepare(`SELECT ${SYMBOL_COLUMNS} FROM current_analyzer_names WHERE space = ? AND address BETWEEN ? AND ?`).all(space, range.start, range.end) as Raw[])
|
|
294
|
+
.map(toSymbol)
|
|
295
|
+
.filter((row) => (kind === undefined || row.kind === kind) && kept(origins)(row))
|
|
296
|
+
.sort(byAnalyzer);
|
|
297
|
+
}
|
|
298
|
+
|
|
145
299
|
/** Current regions that share at least one address with [start, end], in address order. */
|
|
146
300
|
export function regionsOverlapping(db: DatabaseSync | undefined, start: number, end: number, space: Space = DEFAULT_SPACE): RegionRow[] {
|
|
147
301
|
if (db === undefined) return [];
|
|
@@ -213,19 +367,55 @@ export function moduleWithFile(db: DatabaseSync | undefined, file: string, space
|
|
|
213
367
|
return row === undefined ? undefined : toModule(row);
|
|
214
368
|
}
|
|
215
369
|
|
|
370
|
+
export function operandAt(db: DatabaseSync | undefined, address: number, space: Space = DEFAULT_SPACE): OperandRow | undefined {
|
|
371
|
+
const row = db?.prepare(`SELECT ${OPERAND_COLUMNS} FROM current_operands WHERE space = ? AND address = ?`).get(space, address) as Raw | undefined;
|
|
372
|
+
return row === undefined ? undefined : toOperand(row);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Current patches that share at least one address with [start, end], in address order. */
|
|
376
|
+
export function patchesOverlapping(db: DatabaseSync | undefined, start: number, end: number, space: Space = DEFAULT_SPACE): PatchRow[] {
|
|
377
|
+
if (db === undefined) return [];
|
|
378
|
+
return db
|
|
379
|
+
.prepare(`SELECT ${PATCH_COLUMNS} FROM current_patches WHERE space = ? AND start_address <= ? AND end_address >= ? ORDER BY start_address`)
|
|
380
|
+
.all(space, end, start)
|
|
381
|
+
.map((row) => toPatch(row as Raw));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** Current macro uses that share at least one address with [start, end], in address order. */
|
|
385
|
+
export function macroUsesOverlapping(db: DatabaseSync | undefined, start: number, end: number, space: Space = DEFAULT_SPACE): MacroUseRow[] {
|
|
386
|
+
if (db === undefined) return [];
|
|
387
|
+
return db
|
|
388
|
+
.prepare(`SELECT ${MACRO_USE_COLUMNS} FROM current_macro_uses WHERE space = ? AND start_address <= ? AND end_address >= ? ORDER BY start_address`)
|
|
389
|
+
.all(space, end, start)
|
|
390
|
+
.map((row) => toMacroUse(row as Raw));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function macroNamed(db: DatabaseSync | undefined, name: string, space: Space = DEFAULT_SPACE): MacroRow | undefined {
|
|
394
|
+
const row = db?.prepare(`SELECT ${MACRO_COLUMNS} FROM current_macros WHERE space = ? AND name = ?`).get(space, name) as Raw | undefined;
|
|
395
|
+
return row === undefined ? undefined : toMacro(row);
|
|
396
|
+
}
|
|
397
|
+
|
|
216
398
|
/** Everything currently known at one address: the main lookup for skills. */
|
|
217
399
|
export interface AddressKnowledge {
|
|
218
400
|
space: Space;
|
|
219
401
|
address: number;
|
|
220
402
|
symbol?: SymbolRow;
|
|
403
|
+
/** The second names that analyzers gave the address. */
|
|
404
|
+
analyzerNames: SymbolRow[];
|
|
221
405
|
/** The current region that contains the address. */
|
|
222
406
|
region?: RegionRow;
|
|
223
407
|
comments: CommentRow[];
|
|
224
408
|
referencesFrom: ReferenceRow[];
|
|
225
409
|
referencesTo: ReferenceRow[];
|
|
410
|
+
/** How the operand of the instruction here is written. */
|
|
411
|
+
operand?: OperandRow;
|
|
412
|
+
/** The patch whose range holds the address. */
|
|
413
|
+
patch?: PatchRow;
|
|
414
|
+
/** The macro use whose range holds the address. */
|
|
415
|
+
macroUse?: MacroUseRow;
|
|
226
416
|
/** What user/LLM removals block here for the analyzers. */
|
|
227
417
|
suppressions: SuppressionRow[];
|
|
228
|
-
revision:
|
|
418
|
+
revision: string;
|
|
229
419
|
}
|
|
230
420
|
|
|
231
421
|
export function at(db: DatabaseSync | undefined, address: number, space: Space = DEFAULT_SPACE): AddressKnowledge {
|
|
@@ -235,6 +425,7 @@ export function at(db: DatabaseSync | undefined, address: number, space: Space =
|
|
|
235
425
|
comments: commentsAt(db, address, space),
|
|
236
426
|
referencesFrom: referencesFrom(db, address, space),
|
|
237
427
|
referencesTo: referencesTo(db, address, space),
|
|
428
|
+
analyzerNames: analyzerNamesAt(db, address, space),
|
|
238
429
|
suppressions: suppressionsAt(db, address, space),
|
|
239
430
|
revision: currentRevision(db),
|
|
240
431
|
};
|
|
@@ -242,6 +433,12 @@ export function at(db: DatabaseSync | undefined, address: number, space: Space =
|
|
|
242
433
|
if (symbol !== undefined) result.symbol = symbol;
|
|
243
434
|
const [region] = regionsOverlapping(db, address, address, space);
|
|
244
435
|
if (region !== undefined) result.region = region;
|
|
436
|
+
const operand = operandAt(db, address, space);
|
|
437
|
+
if (operand !== undefined) result.operand = operand;
|
|
438
|
+
const [patch] = patchesOverlapping(db, address, address, space);
|
|
439
|
+
if (patch !== undefined) result.patch = patch;
|
|
440
|
+
const [macroUse] = macroUsesOverlapping(db, address, address, space);
|
|
441
|
+
if (macroUse !== undefined) result.macroUse = macroUse;
|
|
245
442
|
return result;
|
|
246
443
|
}
|
|
247
444
|
|
|
@@ -252,7 +449,7 @@ export interface Range {
|
|
|
252
449
|
|
|
253
450
|
const ALL: Range = { start: 0x0000, end: 0xffff };
|
|
254
451
|
|
|
255
|
-
/** Rows of these origins only; undefined keeps every origin
|
|
452
|
+
/** Rows of these origins only; undefined keeps every origin. */
|
|
256
453
|
export type OriginFilter = readonly Origin[] | undefined;
|
|
257
454
|
const kept = (origins: OriginFilter) => (row: { origin: Origin }) => origins === undefined || origins.includes(row.origin);
|
|
258
455
|
|
|
@@ -312,12 +509,42 @@ export function listModules(db: DatabaseSync | undefined, range: Range = ALL, or
|
|
|
312
509
|
.filter(kept(origins));
|
|
313
510
|
}
|
|
314
511
|
|
|
512
|
+
/** Current operand expressions of instructions in the range, in address order. */
|
|
513
|
+
export function listOperands(db: DatabaseSync | undefined, range: Range = ALL, origins?: OriginFilter, space: Space = DEFAULT_SPACE): OperandRow[] {
|
|
514
|
+
if (db === undefined) return [];
|
|
515
|
+
return db
|
|
516
|
+
.prepare(`SELECT ${OPERAND_COLUMNS} FROM current_operands WHERE space = ? AND address BETWEEN ? AND ? ORDER BY address`)
|
|
517
|
+
.all(space, range.start, range.end)
|
|
518
|
+
.map((row) => toOperand(row as Raw))
|
|
519
|
+
.filter(kept(origins));
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** Current patches that share an address with the range, of one switch or of all, in address order. */
|
|
523
|
+
export function listPatches(db: DatabaseSync | undefined, range: Range = ALL, switchName?: string, origins?: OriginFilter, space: Space = DEFAULT_SPACE): PatchRow[] {
|
|
524
|
+
return patchesOverlapping(db, range.start, range.end, space).filter((patch) => (switchName === undefined || patch.switch === switchName) && kept(origins)(patch));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** Current macros, by name. */
|
|
528
|
+
export function listMacros(db: DatabaseSync | undefined, origins?: OriginFilter, space: Space = DEFAULT_SPACE): MacroRow[] {
|
|
529
|
+
if (db === undefined) return [];
|
|
530
|
+
return db
|
|
531
|
+
.prepare(`SELECT ${MACRO_COLUMNS} FROM current_macros WHERE space = ? ORDER BY name`)
|
|
532
|
+
.all(space)
|
|
533
|
+
.map((row) => toMacro(row as Raw))
|
|
534
|
+
.filter(kept(origins));
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/** Current macro uses that share an address with the range, of one macro or of all, in address order. */
|
|
538
|
+
export function listMacroUses(db: DatabaseSync | undefined, range: Range = ALL, macro?: string, origins?: OriginFilter, space: Space = DEFAULT_SPACE): MacroUseRow[] {
|
|
539
|
+
return macroUsesOverlapping(db, range.start, range.end, space).filter((use) => (macro === undefined || use.macro === macro) && kept(origins)(use));
|
|
540
|
+
}
|
|
541
|
+
|
|
315
542
|
/** The spaces that hold current knowledge, the C64 first. */
|
|
316
543
|
export function listSpaces(db: DatabaseSync | undefined): Space[] {
|
|
317
544
|
if (db === undefined) return [];
|
|
318
545
|
const rows = db
|
|
319
546
|
.prepare(
|
|
320
|
-
`SELECT space FROM current_symbols UNION SELECT space FROM current_regions UNION SELECT space FROM current_comments UNION SELECT space FROM current_references UNION SELECT space FROM current_suppressions UNION SELECT space FROM current_modules`,
|
|
547
|
+
`SELECT space FROM current_symbols UNION SELECT space FROM current_analyzer_names UNION SELECT space FROM current_regions UNION SELECT space FROM current_comments UNION SELECT space FROM current_references UNION SELECT space FROM current_suppressions UNION SELECT space FROM current_modules UNION SELECT space FROM current_operands UNION SELECT space FROM current_patches UNION SELECT space FROM current_macros UNION SELECT space FROM current_macro_uses UNION SELECT space FROM current_memory UNION SELECT space FROM current_entries UNION SELECT space FROM current_coverage`,
|
|
321
548
|
)
|
|
322
549
|
.all() as Array<{ space: Space }>;
|
|
323
550
|
const order = (space: Space) => (space === "c64" ? -2 : space === "drive8" ? -1 : Number(space.slice(4)));
|
|
@@ -325,8 +552,8 @@ export function listSpaces(db: DatabaseSync | undefined): Space[] {
|
|
|
325
552
|
}
|
|
326
553
|
|
|
327
554
|
/** Case-insensitive text search in current symbol names and comment texts of one space. */
|
|
328
|
-
export function search(db: DatabaseSync | undefined, text: string, limit = 50, origins?: OriginFilter, space: Space = DEFAULT_SPACE): { symbols: SymbolRow[]; comments: CommentRow[] } {
|
|
329
|
-
if (db === undefined || text.length === 0) return { symbols: [], comments: [] };
|
|
555
|
+
export function search(db: DatabaseSync | undefined, text: string, limit = 50, origins?: OriginFilter, space: Space = DEFAULT_SPACE): { symbols: SymbolRow[]; analyzerNames: SymbolRow[]; comments: CommentRow[] } {
|
|
556
|
+
if (db === undefined || text.length === 0) return { symbols: [], analyzerNames: [], comments: [] };
|
|
330
557
|
const pattern = `%${text.replace(/[\\%_]/g, (character) => `\\${character}`)}%`;
|
|
331
558
|
const only = origins === undefined ? "" : ` AND origin IN (${origins.map(() => "?").join(", ")})`;
|
|
332
559
|
const values = origins ?? [];
|
|
@@ -334,9 +561,13 @@ export function search(db: DatabaseSync | undefined, text: string, limit = 50, o
|
|
|
334
561
|
.prepare(`SELECT ${SYMBOL_COLUMNS} FROM current_symbols WHERE space = ? AND name LIKE ? ESCAPE '\\'${only} ORDER BY address LIMIT ?`)
|
|
335
562
|
.all(space, pattern, ...values, limit)
|
|
336
563
|
.map((row) => toSymbol(row as Raw));
|
|
564
|
+
const analyzerNames = db
|
|
565
|
+
.prepare(`SELECT ${SYMBOL_COLUMNS} FROM current_analyzer_names WHERE space = ? AND name LIKE ? ESCAPE '\\'${only} ORDER BY address LIMIT ?`)
|
|
566
|
+
.all(space, pattern, ...values, limit)
|
|
567
|
+
.map((row) => toSymbol(row as Raw));
|
|
337
568
|
const comments = db
|
|
338
569
|
.prepare(`SELECT ${COMMENT_COLUMNS} FROM current_comments WHERE space = ? AND text LIKE ? ESCAPE '\\'${only} ORDER BY address, placement LIMIT ?`)
|
|
339
570
|
.all(space, pattern, ...values, limit)
|
|
340
571
|
.map((row) => toComment(row as Raw));
|
|
341
|
-
return { symbols, comments };
|
|
572
|
+
return { symbols, analyzerNames, comments };
|
|
342
573
|
}
|