@henols/c64-re-tools 2.0.0-rc.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -115
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +22 -6
- package/skills/c64-assembler/scripts/assemble.ts +42 -14
- package/skills/c64-assembler/scripts/lib/compare.ts +48 -0
- package/skills/c64-basic/SKILL.md +10 -1
- package/skills/c64-basic/scripts/basic.ts +3 -3
- package/skills/c64-disk/SKILL.md +13 -4
- package/skills/c64-disk/scripts/disk.ts +7 -7
- package/skills/c64-disk/scripts/{containers.ts → lib/containers.ts} +2 -2
- package/skills/c64-docs/SKILL.md +138 -0
- package/skills/c64-docs/references/page-types.md +141 -0
- package/skills/c64-docs/references/style.md +43 -0
- package/skills/c64-docs/scripts/generate.ts +109 -0
- package/skills/c64-docs/scripts/lib/links.ts +167 -0
- package/skills/c64-docs/scripts/lib/markdown.ts +79 -0
- package/skills/c64-docs/scripts/lib/reference.ts +406 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +81 -0
- package/skills/c64-docs/scripts/link.ts +239 -0
- package/skills/c64-emulator/SKILL.md +16 -5
- package/skills/c64-knowledge/SKILL.md +140 -18
- package/skills/c64-knowledge/scripts/knowledge.ts +737 -112
- package/skills/c64-knowledge/scripts/lib/listing.ts +162 -0
- package/skills/c64-memory-map/SKILL.md +11 -2
- package/skills/c64-memory-map/scripts/memmap.ts +2 -2
- package/skills/c64-provenance/SKILL.md +66 -11
- package/skills/c64-provenance/scripts/{compare.ts → lib/compare.ts} +3 -3
- package/skills/c64-provenance/scripts/lib/library.ts +584 -0
- package/skills/c64-provenance/scripts/{runtime.ts → lib/runtime.ts} +2 -2
- package/skills/c64-provenance/scripts/{separate.ts → lib/separate.ts} +3 -3
- package/skills/c64-provenance/scripts/provenance.ts +242 -40
- package/skills/c64-reverse-engineering/SKILL.md +51 -26
- package/skills/c64-source/SKILL.md +76 -28
- package/skills/c64-source/scripts/generate.ts +425 -127
- package/skills/c64-source/scripts/{loader.ts → lib/loader.ts} +84 -25
- package/skills/c64-source/scripts/lib/source.ts +1121 -0
- package/skills/c64-static-analysis/SKILL.md +35 -18
- package/skills/c64-static-analysis/scripts/analyze.ts +184 -55
- package/skills/c64-static-analysis/scripts/{coverage.ts → lib/coverage.ts} +19 -25
- package/skills/c64-static-analysis/scripts/{findings.ts → lib/findings.ts} +6 -2
- package/skills/c64-static-analysis/scripts/{idioms.ts → lib/idioms.ts} +21 -12
- package/skills/c64-static-analysis/scripts/{instructions.ts → lib/instructions.ts} +1 -1
- package/skills/c64-static-analysis/scripts/lib/macros.ts +233 -0
- package/skills/c64-static-analysis/scripts/lib/pitfalls.ts +68 -0
- package/skills/c64-static-analysis/scripts/lib/pointers.ts +108 -0
- package/skills/c64-testing/SKILL.md +36 -7
- package/skills/c64-testing/references/scenario-format.md +12 -8
- package/skills/c64-testing/scripts/checklist.ts +3 -3
- package/skills/c64-testing/scripts/diff.ts +167 -0
- package/skills/c64-testing/scripts/lib/diff.ts +70 -0
- package/skills/c64-testing/scripts/{scenario.ts → lib/scenario.ts} +45 -34
- package/skills/c64-testing/scripts/run.ts +73 -21
- package/skills/c64-testing/scripts/test.ts +5 -5
- package/skills/c64-unpacker/SKILL.md +23 -9
- package/skills/c64-unpacker/scripts/{evidence.ts → lib/evidence.ts} +1 -1
- package/skills/c64-unpacker/scripts/{loads.ts → lib/loads.ts} +4 -4
- package/skills/c64-unpacker/scripts/{machine.ts → lib/machine.ts} +4 -4
- package/skills/c64-unpacker/scripts/{stages.ts → lib/stages.ts} +17 -5
- package/skills/c64-unpacker/scripts/unpack.ts +41 -24
- package/src/c64.ts +103 -5
- package/src/cli/cleanup.ts +1 -1
- package/src/cli/main.ts +7 -4
- package/src/cli/status.ts +70 -3
- package/src/graphics.ts +158 -0
- package/src/host/main.ts +43 -8
- package/src/host/server.ts +10 -3
- package/src/host/tools/petcat.ts +1 -1
- package/src/host/tools/status.ts +1 -1
- package/src/host/vice/adapter.ts +4 -3
- package/src/host/vice/process.ts +2 -2
- package/src/host/vice/screen.ts +1 -42
- package/src/host/vice/session.ts +196 -23
- package/src/host-client/autostart.ts +1 -1
- package/src/host-client/connect.ts +1 -1
- package/src/host-client/coverage.ts +2 -29
- package/src/host-client/image.ts +66 -0
- package/src/host-client/steps.ts +9 -4
- package/src/host-client/tools.ts +1 -0
- package/src/host-client/transfer.ts +39 -11
- package/src/host-client/vice-session.ts +33 -16
- package/src/knowledge/conflicts.ts +161 -0
- package/src/knowledge/database.ts +150 -38
- package/src/knowledge/expression.ts +209 -0
- package/src/knowledge/graph.ts +104 -0
- package/src/knowledge/history.ts +70 -22
- package/src/knowledge/import.ts +64 -55
- package/src/knowledge/join.ts +211 -0
- package/src/knowledge/program.ts +266 -0
- package/src/knowledge/read.ts +258 -27
- package/src/knowledge/schema.ts +199 -75
- package/src/knowledge/text.ts +415 -0
- package/src/knowledge/write.ts +740 -57
- package/src/mcp/main.ts +1 -1
- package/src/mcp/output.ts +15 -8
- package/src/mcp/server.ts +3 -1
- package/src/mcp/tools/debug.ts +97 -16
- package/src/mcp/tools/execution.ts +38 -5
- package/src/mcp/tools/machine.ts +11 -2
- package/src/mcp/tools/media.ts +18 -13
- package/src/mcp/tools/memory.ts +17 -8
- package/src/mcp/tools/video.ts +9 -9
- package/src/native/discover.ts +2 -2
- package/src/native/ghidra/analyze.ts +2 -2
- package/src/native/ghidra/index.ts +1 -1
- package/src/native/ghidra/language/c64rt_6510.slaspec +4 -1
- package/src/native/ghidra/scripts/C64Export.java +1 -1
- package/src/native/ghidra/scripts/C64Prepare.java +7 -1
- package/src/native/types.ts +14 -8
- package/{skills/c64-source/scripts → src}/opcodes.ts +75 -3
- package/src/platform.ts +70 -7
- package/src/project.ts +180 -3
- package/src/protocol/messages.ts +1 -1
- package/src/protocol/tools.ts +2 -16
- package/src/protocol/vice.ts +124 -15
- package/src/script.ts +46 -7
- package/src/trace.ts +55 -23
- package/skills/c64-assembler/scripts/compare.ts +0 -84
- package/skills/c64-source/scripts/source.ts +0 -595
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// The loader of a generated source
|
|
1
|
+
// The loader of a generated source: a PRG that copies each segment and
|
|
2
2
|
// the entry state's pages $0002-$03ff into place, sets the display registers
|
|
3
|
-
// of the VIC-II and the VIC bank, waits for the entry's raster line
|
|
4
|
-
// sets the CIA timers and interrupt masks
|
|
3
|
+
// of the VIC-II and the VIC bank, waits for the entry's raster line,
|
|
4
|
+
// sets the CIA timers and interrupt masks, sets $00/$01, the stack
|
|
5
5
|
// pointer, A, X, Y and the flags, and jumps to the entry. The copies run
|
|
6
6
|
// from a free page with SEI and all RAM visible, in an order where no copy
|
|
7
7
|
// overwrites the packed bytes of a copy that has not run yet. The plan is
|
|
@@ -22,9 +22,9 @@ export interface EntryState {
|
|
|
22
22
|
port: [number, number];
|
|
23
23
|
/** $0000-$03ff at the entry. */
|
|
24
24
|
pages: Uint8Array;
|
|
25
|
-
/** The VIC-II at the entry
|
|
25
|
+
/** The VIC-II at the entry: $d000-$d02e, and the VIC bank (bits 0-1 of $dd00). */
|
|
26
26
|
vic?: { registers: Uint8Array; bank: number };
|
|
27
|
-
/** CIA 1 and CIA 2 at the entry
|
|
27
|
+
/** CIA 1 and CIA 2 at the entry. */
|
|
28
28
|
cia?: [EntryCia, EntryCia];
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -37,7 +37,7 @@ export interface EntryCia {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* The VIC-II registers that the loader sets
|
|
40
|
+
* The VIC-II registers that the loader sets, each with the bits that a
|
|
41
41
|
* read gives back: not the raster, the light pen, the interrupt registers or
|
|
42
42
|
* the collisions. The raster line comes from a wait instead.
|
|
43
43
|
*/
|
|
@@ -125,20 +125,31 @@ export interface LoaderPlan {
|
|
|
125
125
|
stackByte: number;
|
|
126
126
|
end: number;
|
|
127
127
|
text: string;
|
|
128
|
-
/** The CIA timers and masks that the loader sets, when the entry state has them
|
|
128
|
+
/** The CIA timers and masks that the loader sets, when the entry state has them. */
|
|
129
129
|
cia?: CiaPlan;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* What the loader cannot set as it was at the entry. An entry state
|
|
134
|
+
* without CIA values (from a capture of an older version) gives no timers at all.
|
|
135
|
+
*/
|
|
136
|
+
export function loaderLimits(plan: { cia?: CiaPlan }): string[] {
|
|
137
|
+
if (plan.cia === undefined) {
|
|
138
|
+
return ["The entry state has no CIA values, so the loader does not set the CIA timers. Capture the program again with c64-unpacker to get them."];
|
|
139
|
+
}
|
|
140
|
+
return plan.cia.limits;
|
|
141
|
+
}
|
|
142
|
+
|
|
132
143
|
/** The label of the loader's jump to the entry, in ACME's symbol list. */
|
|
133
144
|
export const LOADER_JUMP = "c64_source_loader_jump";
|
|
134
145
|
|
|
135
146
|
const LOAD = 0x0801;
|
|
136
147
|
const STUB = 0x080d;
|
|
137
|
-
/** sei, ldx, the 2-page move loop and jmp. */
|
|
138
|
-
const STUB_SIZE =
|
|
148
|
+
/** sei, all RAM, ldx, the 2-page move loop and jmp. */
|
|
149
|
+
const STUB_SIZE = 25;
|
|
139
150
|
/** The copier code and its three variables, without the table. */
|
|
140
151
|
const COPIER_CODE_SIZE = 167;
|
|
141
|
-
/** The VIC-II code of the copier
|
|
152
|
+
/** The VIC-II code of the copier, without its two tables of one byte per register. */
|
|
142
153
|
const VIC_CODE_SIZE = 44;
|
|
143
154
|
const VIC_SIZE = VIC_CODE_SIZE + 2 * VIC_DISPLAY_REGISTERS.length;
|
|
144
155
|
/** The copier and the stub's move of it: two pages. */
|
|
@@ -193,6 +204,31 @@ export function simulate(memory: Int16Array, copies: Copy[], expected: (address:
|
|
|
193
204
|
return undefined;
|
|
194
205
|
}
|
|
195
206
|
|
|
207
|
+
/** True when a read of the address with $01 = $37, as RUN leaves it, gives a ROM byte. */
|
|
208
|
+
const underRom = (address: number) => (address >= 0xa000 && address <= 0xbfff) || address >= 0xe000;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Why a loader stopped before its jump, from the RAM after the stop: the first
|
|
212
|
+
* byte of the moved copier that is not the byte of the loader file, else, when
|
|
213
|
+
* the copier ran, the first byte that a copy did not write as planned.
|
|
214
|
+
*/
|
|
215
|
+
export function stopCause(ram: Uint8Array, plan: LoaderPlan, copierSource: number, fileByte: (address: number) => number, copied: boolean, expected: (address: number) => number): string | undefined {
|
|
216
|
+
for (let offset = 0; offset <= plan.end - copierSource; offset++) {
|
|
217
|
+
const got = ram[plan.copier + offset]!;
|
|
218
|
+
const wanted = fileByte(copierSource + offset);
|
|
219
|
+
if (got === wanted) continue;
|
|
220
|
+
const rom = underRom(copierSource + offset) ? `; ${hex4(copierSource + offset)} is under a ROM, so the move read the ROM` : "";
|
|
221
|
+
return `The copier at ${hex4(plan.copier + offset)} holds ${hex2(got)}, not ${hex2(wanted)} from ${hex4(copierSource + offset)} of the loader file${rom}.`;
|
|
222
|
+
}
|
|
223
|
+
if (!copied) return undefined;
|
|
224
|
+
for (const copy of plan.copies) {
|
|
225
|
+
for (let address = copy.target.start; address <= copy.target.end; address++) {
|
|
226
|
+
if (ram[address] !== expected(address)) return `The copy of ${copy.what} wrote ${hex2(ram[address]!)} at ${hex4(address)}, not ${hex2(expected(address))}.`;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
|
|
196
232
|
/** The VIC-II and its bank from the io part of an entry state, when it has them. */
|
|
197
233
|
function readVic(io: Record<string, any> | undefined, fail: (what: string) => never): { vic: NonNullable<EntryState["vic"]> } | Record<string, never> {
|
|
198
234
|
if (io?.vic === undefined) return {};
|
|
@@ -260,7 +296,7 @@ interface Op {
|
|
|
260
296
|
}
|
|
261
297
|
|
|
262
298
|
/**
|
|
263
|
-
* The copier's CIA part
|
|
299
|
+
* The copier's CIA part, after the raster wait: the timers and
|
|
264
300
|
* interrupt masks of the entry state. A running timer is stopped, loaded
|
|
265
301
|
* with its counter at the entry plus the cycles to the entry, started, and
|
|
266
302
|
* given its latch, so it counts to the entry's value just at the entry.
|
|
@@ -354,9 +390,19 @@ export interface LoaderInput {
|
|
|
354
390
|
byteAt: (address: number) => number;
|
|
355
391
|
state: EntryState;
|
|
356
392
|
entry: number;
|
|
393
|
+
/** The label of the entry in the source: the loader jumps to it, so the jump follows the code when it moves. */
|
|
394
|
+
entryName?: string;
|
|
357
395
|
header: string[];
|
|
396
|
+
/** The source has macros.asm: the loader includes it after the constants. */
|
|
397
|
+
macros?: boolean;
|
|
358
398
|
}
|
|
359
399
|
|
|
400
|
+
/** The label of a block: its first byte and the byte after it in the loader file, and its first address where it runs. */
|
|
401
|
+
const blockLabel = (start: number, part: "from" | "end" | "to") => `loader_${part}_${start.toString(16).padStart(4, "0")}`;
|
|
402
|
+
const plus = (label: string, offset: number) => (offset === 0 ? label : `${label}+${offset}`);
|
|
403
|
+
/** The label of the copier in the loader file, which the stub moves to its two pages. */
|
|
404
|
+
export const COPIER_SOURCE = "loader_copier_src";
|
|
405
|
+
|
|
360
406
|
/** Plans the loader: the packed layout, the copy order and the copier page, and writes loader.asm. Refuses by name when no plan exists. */
|
|
361
407
|
export function planLoader(input: LoaderInput): LoaderPlan {
|
|
362
408
|
const { state } = input;
|
|
@@ -371,12 +417,15 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
371
417
|
let at = STUB + STUB_SIZE;
|
|
372
418
|
const plain: Array<Omit<Copy, "backward">> = [];
|
|
373
419
|
const packedAt: number[] = [];
|
|
420
|
+
/** The block start of each copy in `plain`, which names its labels. */
|
|
421
|
+
const blockOf: number[] = [];
|
|
374
422
|
for (const block of blocks) {
|
|
375
423
|
packedAt.push(at);
|
|
376
424
|
const target = { start: Math.max(block.range.start, 0x0002), end: block.range.end };
|
|
377
425
|
if (target.start <= target.end) {
|
|
378
426
|
const source = { start: at + (target.start - block.range.start), end: at + (target.end - block.range.start) };
|
|
379
427
|
plain.push({ what: block.what, target, source });
|
|
428
|
+
blockOf.push(block.range.start);
|
|
380
429
|
}
|
|
381
430
|
at += block.range.end - block.range.start + 1;
|
|
382
431
|
}
|
|
@@ -405,25 +454,30 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
405
454
|
const copy = plain[index]!;
|
|
406
455
|
return { ...copy, backward: copy.target.start > copy.source.start && overlaps(copy.target, copy.source) };
|
|
407
456
|
});
|
|
457
|
+
const copyBlocks = order.map((index) => blockOf[index]!);
|
|
408
458
|
|
|
409
459
|
const stackByte = 0x0100 + state.sp;
|
|
410
|
-
const lines: string[] = [...input.header.map((line) => `; ${line}`), "", "!cpu 6510", '!source "constants.asm"', "", `* = ${hex4(LOAD)}`];
|
|
460
|
+
const lines: string[] = [...input.header.map((line) => `; ${line}`), "", "!cpu 6510", '!source "constants.asm"', ...(input.macros ? ['!source "macros.asm"'] : []), "", `* = ${hex4(LOAD)}`];
|
|
411
461
|
lines.push(` !byte $0b,$08,$0a,$00,$9e,${[...String(STUB)].map((digit) => hex2(digit.charCodeAt(0))).join(",")},$00,$00,$00 ; 10 SYS ${STUB}`);
|
|
412
462
|
lines.push(
|
|
413
463
|
"!zone loader_stub {",
|
|
414
464
|
" sei",
|
|
465
|
+
// The copier lies at the end of the loader file, which can be at $a000 or higher, under the BASIC ROM.
|
|
466
|
+
" lda #$34",
|
|
467
|
+
" sta $01",
|
|
415
468
|
" ldx #0",
|
|
416
|
-
`- lda ${
|
|
469
|
+
`- lda ${COPIER_SOURCE},x`,
|
|
417
470
|
` sta ${hex4(copier)},x`,
|
|
418
|
-
` lda ${
|
|
471
|
+
` lda ${COPIER_SOURCE}+$100,x`,
|
|
419
472
|
` sta ${hex4(copier + 0x100)},x`,
|
|
420
473
|
" inx",
|
|
421
474
|
" bne -",
|
|
422
475
|
` jmp ${hex4(copier)}`,
|
|
423
476
|
"}",
|
|
424
477
|
);
|
|
478
|
+
// The labels around each block give the copy table its numbers, so a block can change its length.
|
|
425
479
|
for (const block of blocks) {
|
|
426
|
-
lines.push("", `; ${block.what}`, `!pseudopc ${hex4(block.pseudo)} {
|
|
480
|
+
lines.push("", `; ${block.what}`, blockLabel(block.range.start, "from"), `!pseudopc ${hex4(block.pseudo)} {`, blockLabel(block.range.start, "to"));
|
|
427
481
|
if (block.files !== undefined) for (const file of block.files) lines.push(`!source "${file}"`);
|
|
428
482
|
else {
|
|
429
483
|
for (let address = block.range.start; address <= block.range.end; address += 16) {
|
|
@@ -431,12 +485,13 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
431
485
|
lines.push(` !byte ${Array.from({ length: last - address + 1 }, (_, offset) => hex2(block.bytes(address + offset))).join(",")}`);
|
|
432
486
|
}
|
|
433
487
|
}
|
|
434
|
-
lines.push("}");
|
|
488
|
+
lines.push("}", blockLabel(block.range.start, "end"));
|
|
435
489
|
}
|
|
436
490
|
const [port0, port1] = state.port;
|
|
437
491
|
lines.push(
|
|
438
492
|
"",
|
|
439
493
|
`; The copier runs at ${hex4(copier)}. The flags pass through ${hex4(stackByte)}, the free stack byte below the stack pointer.`,
|
|
494
|
+
COPIER_SOURCE,
|
|
440
495
|
`!pseudopc ${hex4(copier)} {`,
|
|
441
496
|
"!zone loader_copier {",
|
|
442
497
|
" lda #$34",
|
|
@@ -507,8 +562,8 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
507
562
|
` ldx #${hex2(state.x)}`,
|
|
508
563
|
` ldy #${hex2(state.y)}`,
|
|
509
564
|
" plp",
|
|
510
|
-
// A global label: the check of the loader stops here, before an interrupt can run
|
|
511
|
-
`${LOADER_JUMP} jmp ${hex4(input.entry)}`,
|
|
565
|
+
// A global label: the check of the loader stops here, before an interrupt can run.
|
|
566
|
+
`${LOADER_JUMP} jmp ${input.entryName ?? hex4(input.entry)}`,
|
|
512
567
|
".count !word 0",
|
|
513
568
|
".index !byte 0",
|
|
514
569
|
".back !byte 0",
|
|
@@ -520,12 +575,16 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
520
575
|
]),
|
|
521
576
|
".table",
|
|
522
577
|
);
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
578
|
+
copies.forEach((copy, index) => {
|
|
579
|
+
// A block that starts at $0000 skips $00/$01, which the loader sets last.
|
|
580
|
+
const start = copyBlocks[index]!;
|
|
581
|
+
const skip = copy.target.start - start;
|
|
582
|
+
const [from, end, to] = [blockLabel(start, "from"), blockLabel(start, "end"), blockLabel(start, "to")];
|
|
583
|
+
const length = skip === 0 ? `${end} - ${from}` : `${end} - ${from} - ${skip}`;
|
|
584
|
+
const read = copy.backward ? `${end} - 1` : plus(from, skip);
|
|
585
|
+
const write = copy.backward ? `${to} + ${end} - ${from} - 1` : plus(to, skip);
|
|
586
|
+
lines.push(` !word ${length}, ${read}, ${write} : !byte ${copy.backward ? 1 : 0} ; ${copy.what}`);
|
|
587
|
+
});
|
|
529
588
|
lines.push(" !word 0", "}", "}");
|
|
530
589
|
|
|
531
590
|
return {
|
|
@@ -540,7 +599,7 @@ export function planLoader(input: LoaderInput): LoaderPlan {
|
|
|
540
599
|
}
|
|
541
600
|
|
|
542
601
|
/**
|
|
543
|
-
* The copier's VIC-II part
|
|
602
|
+
* The copier's VIC-II part: with the I/O area visible it sets the
|
|
544
603
|
* display registers and the VIC bank of the entry state, then waits for the
|
|
545
604
|
* entry's raster line, so the program starts in the same phase of the frame.
|
|
546
605
|
*/
|