@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/graphics.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Indexed images: the PNG encoder of emulator frames, and the C64 graphics
|
|
2
|
+
// in memory as images for the documentation. A region of the knowledge
|
|
3
|
+
// gives no display mode and no colours, so a sprite, a bitmap or a charset is read
|
|
4
|
+
// both in hires and in multicolour, with fixed pattern palettes.
|
|
5
|
+
|
|
6
|
+
import { crc32, deflateSync } from "node:zlib";
|
|
7
|
+
|
|
8
|
+
export interface IndexedFrame {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
/** One palette index per pixel, row by row. */
|
|
12
|
+
pixels: Uint8Array;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type Rgb = readonly [number, number, number];
|
|
16
|
+
|
|
17
|
+
function chunk(type: string, data: Uint8Array): Buffer {
|
|
18
|
+
const length = Buffer.alloc(4);
|
|
19
|
+
length.writeUInt32BE(data.length, 0);
|
|
20
|
+
const body = Buffer.concat([Buffer.from(type, "latin1"), data]);
|
|
21
|
+
const crc = Buffer.alloc(4);
|
|
22
|
+
crc.writeUInt32BE(crc32(body), 0);
|
|
23
|
+
return Buffer.concat([length, body, crc]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Encodes an indexed frame as an 8-bit palette PNG. */
|
|
27
|
+
export function encodePng(frame: IndexedFrame, palette: ReadonlyArray<Rgb>): Buffer {
|
|
28
|
+
for (const index of frame.pixels) {
|
|
29
|
+
if (index >= palette.length) throw new Error(`pixel colour ${index} is not in the ${palette.length}-colour palette`);
|
|
30
|
+
}
|
|
31
|
+
const header = Buffer.alloc(13);
|
|
32
|
+
header.writeUInt32BE(frame.width, 0);
|
|
33
|
+
header.writeUInt32BE(frame.height, 4);
|
|
34
|
+
header[8] = 8; // bit depth
|
|
35
|
+
header[9] = 3; // indexed colour
|
|
36
|
+
const rows = Buffer.alloc((frame.width + 1) * frame.height);
|
|
37
|
+
for (let row = 0; row < frame.height; row++) {
|
|
38
|
+
// Filter byte 0 (none), then the row.
|
|
39
|
+
rows.set(frame.pixels.subarray(row * frame.width, (row + 1) * frame.width), row * (frame.width + 1) + 1);
|
|
40
|
+
}
|
|
41
|
+
return Buffer.concat([
|
|
42
|
+
Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
|
|
43
|
+
chunk("IHDR", header),
|
|
44
|
+
chunk("PLTE", Buffer.from(palette.flat())),
|
|
45
|
+
chunk("IDAT", deflateSync(rows)),
|
|
46
|
+
chunk("IEND", new Uint8Array(0)),
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type GraphicsMode = "hires" | "multicolour";
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The pattern palettes: the index is the pixel value (hires: the bit; multicolour:
|
|
54
|
+
* the bit pair, 0 = background $d021, 1 = $d022 or screen high nibble, 2 = $d023 or
|
|
55
|
+
* screen low nibble, 3 = colour RAM), and the last index is the gap between sprites.
|
|
56
|
+
*/
|
|
57
|
+
export const PATTERN_PALETTES: Record<GraphicsMode, readonly Rgb[]> = {
|
|
58
|
+
hires: [
|
|
59
|
+
[0x00, 0x00, 0x00],
|
|
60
|
+
[0xff, 0xff, 0xff],
|
|
61
|
+
[0x35, 0x28, 0x79],
|
|
62
|
+
],
|
|
63
|
+
multicolour: [
|
|
64
|
+
[0x00, 0x00, 0x00],
|
|
65
|
+
[0xad, 0xad, 0xad],
|
|
66
|
+
[0x6c, 0x6c, 0x6c],
|
|
67
|
+
[0xed, 0xf1, 0x71],
|
|
68
|
+
[0x35, 0x28, 0x79],
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** The pixel values of one byte, left to right: 8 hires pixels, or 4 multicolour pixels of 2 each. */
|
|
73
|
+
function bytePixels(byte: number, mode: GraphicsMode): number[] {
|
|
74
|
+
if (mode === "hires") return Array.from({ length: 8 }, (_, bit) => (byte >> (7 - bit)) & 1);
|
|
75
|
+
return Array.from({ length: 8 }, (_, pixel) => (byte >> (6 - 2 * (pixel >> 1))) & 3);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const SPRITE = { width: 24, height: 21, bytes: 63 } as const;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The sprites of `bytes` as one sheet, `perRow` sprites wide with a gap of one
|
|
82
|
+
* pixel. Each sprite takes `stride` bytes (64 in a VIC bank, 63 when packed).
|
|
83
|
+
*/
|
|
84
|
+
export function spriteSheet(bytes: Uint8Array, mode: GraphicsMode, stride = 64, perRow = 8): IndexedFrame {
|
|
85
|
+
const count = Math.max(1, Math.floor((bytes.length + stride - SPRITE.bytes) / stride));
|
|
86
|
+
const columns = Math.min(count, perRow);
|
|
87
|
+
const rows = Math.ceil(count / perRow);
|
|
88
|
+
const width = columns * (SPRITE.width + 1) + 1;
|
|
89
|
+
const height = rows * (SPRITE.height + 1) + 1;
|
|
90
|
+
const gap = PATTERN_PALETTES[mode].length - 1;
|
|
91
|
+
const pixels = new Uint8Array(width * height).fill(gap);
|
|
92
|
+
for (let sprite = 0; sprite < count; sprite++) {
|
|
93
|
+
const left = (sprite % perRow) * (SPRITE.width + 1) + 1;
|
|
94
|
+
const top = Math.floor(sprite / perRow) * (SPRITE.height + 1) + 1;
|
|
95
|
+
for (let row = 0; row < SPRITE.height; row++) {
|
|
96
|
+
for (let column = 0; column < 3; column++) {
|
|
97
|
+
const values = bytePixels(bytes[sprite * stride + row * 3 + column] ?? 0, mode);
|
|
98
|
+
values.forEach((value, x) => (pixels[(top + row) * width + left + column * 8 + x] = value));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { width, height, pixels };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Cells of 8 bytes as 8x8 pixels, `perRow` cells to a row, cell 0 at the top left. */
|
|
106
|
+
function cellFrame(bytes: Uint8Array, mode: GraphicsMode, perRow: number): IndexedFrame {
|
|
107
|
+
const cells = Math.ceil(bytes.length / 8);
|
|
108
|
+
const width = Math.min(cells, perRow) * 8;
|
|
109
|
+
const height = Math.ceil(cells / perRow) * 8;
|
|
110
|
+
const pixels = new Uint8Array(width * height);
|
|
111
|
+
for (let cell = 0; cell < cells; cell++) {
|
|
112
|
+
const left = (cell % perRow) * 8;
|
|
113
|
+
const top = Math.floor(cell / perRow) * 8;
|
|
114
|
+
for (let row = 0; row < 8; row++) {
|
|
115
|
+
bytePixels(bytes[cell * 8 + row] ?? 0, mode).forEach((value, x) => (pixels[(top + row) * width + left + x] = value));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { width, height, pixels };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** A bitmap of 8x8 cells of 8 bytes, 40 cells to a row, as the VIC reads it. */
|
|
122
|
+
export function bitmapFrame(bytes: Uint8Array, mode: GraphicsMode): IndexedFrame {
|
|
123
|
+
return cellFrame(bytes, mode, 40);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** A character set: characters of 8 bytes, 16 to a row, so character N is in row N / 16 and column N % 16. */
|
|
127
|
+
export function charsetFrame(bytes: Uint8Array, mode: GraphicsMode): IndexedFrame {
|
|
128
|
+
return cellFrame(bytes, mode, 16);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Each pixel as a square of `factor` pixels, so a small image is readable on a page. */
|
|
132
|
+
export function scaleFrame(frame: IndexedFrame, factor: number): IndexedFrame {
|
|
133
|
+
const width = frame.width * factor;
|
|
134
|
+
const height = frame.height * factor;
|
|
135
|
+
const pixels = new Uint8Array(width * height);
|
|
136
|
+
for (let y = 0; y < height; y++) {
|
|
137
|
+
for (let x = 0; x < width; x++) pixels[y * width + x] = frame.pixels[Math.floor(y / factor) * frame.width + Math.floor(x / factor)]!;
|
|
138
|
+
}
|
|
139
|
+
return { width, height, pixels };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Screen codes as text rows of 40, as the character ROM shows the upper-case
|
|
144
|
+
* set: @, A-Z, the signs and digits; a reversed code (bit 7) shows as its
|
|
145
|
+
* code without bit 7, and a graphics code as a dot.
|
|
146
|
+
*/
|
|
147
|
+
export function screenText(bytes: Uint8Array): string[] {
|
|
148
|
+
const rows: string[] = [];
|
|
149
|
+
for (let start = 0; start < bytes.length; start += 40) {
|
|
150
|
+
let row = "";
|
|
151
|
+
for (const code of bytes.subarray(start, start + 40)) {
|
|
152
|
+
const plain = code & 0x7f;
|
|
153
|
+
row += plain === 0 ? "@" : plain <= 26 ? String.fromCharCode(0x40 + plain) : plain <= 31 ? "[£]^<"[plain - 27]! : plain <= 63 ? String.fromCharCode(plain) : ".";
|
|
154
|
+
}
|
|
155
|
+
rows.push(row);
|
|
156
|
+
}
|
|
157
|
+
return rows;
|
|
158
|
+
}
|
package/src/host/main.ts
CHANGED
|
@@ -7,10 +7,11 @@ import { ProcessSupervisor } from "../native/processes.ts";
|
|
|
7
7
|
import { startTrace, trace } from "../trace.ts";
|
|
8
8
|
import { ListenError, parsePort, startHostServer, stopSignal } from "./server.ts";
|
|
9
9
|
import { viceFlaws, type ViceVersion } from "../native/discover.ts";
|
|
10
|
+
import { inspect } from "./tools/c1541.ts";
|
|
10
11
|
import { createToolDispatcher } from "./tools/index.ts";
|
|
11
12
|
import { checkViceStarts, findVice } from "./vice/process.ts";
|
|
12
13
|
import { viceSessionFactory } from "./vice/session.ts";
|
|
13
|
-
import { PACKAGE } from "../package.ts";
|
|
14
|
+
import { oldNodeRefusal, PACKAGE } from "../package.ts";
|
|
14
15
|
|
|
15
16
|
const HELP = `c64-re-tools-host
|
|
16
17
|
|
|
@@ -34,8 +35,9 @@ Options:
|
|
|
34
35
|
directory, with a copy of each VICE log.
|
|
35
36
|
--help Show this help.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
The host always logs its start and stop, connections, failures, and each call
|
|
39
|
+
from a skill script (c1541, petcat) with its outcome. --log-console adds the
|
|
40
|
+
calls of the MCP server and the tool runs. C64RT_TRACE does not apply to the host.`;
|
|
39
41
|
|
|
40
42
|
function log(line: string): void {
|
|
41
43
|
process.stderr.write(`c64-re-tools-host: ${line}\n`);
|
|
@@ -76,6 +78,9 @@ async function main(): Promise<number> {
|
|
|
76
78
|
return 2;
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
// First, so that every start, also a refused one, tells which runtime runs.
|
|
82
|
+
log(`${PACKAGE.name} ${PACKAGE.version} (Node.js ${process.version}, ${process.platform} ${process.arch})`);
|
|
83
|
+
|
|
79
84
|
// Without an emulator the runtime is useless: refuse to start, by name and with the remedy.
|
|
80
85
|
try {
|
|
81
86
|
findVice(process.env);
|
|
@@ -101,7 +106,7 @@ async function main(): Promise<number> {
|
|
|
101
106
|
}
|
|
102
107
|
log(`VICE ${vice.major}.${vice.minor}${vice.revision === undefined ? "" : ` r${vice.revision}`} starts`);
|
|
103
108
|
const flaws = viceFlaws(vice);
|
|
104
|
-
// A known VICE flaw is named, not worked around
|
|
109
|
+
// A known VICE flaw is named, not worked around.
|
|
105
110
|
for (const flaw of flaws) log(`Known VICE flaw: ${flaw}`);
|
|
106
111
|
|
|
107
112
|
let server;
|
|
@@ -110,7 +115,16 @@ async function main(): Promise<number> {
|
|
|
110
115
|
host: "0.0.0.0",
|
|
111
116
|
port,
|
|
112
117
|
log,
|
|
113
|
-
createViceSession: viceSessionFactory({
|
|
118
|
+
createViceSession: viceSessionFactory({
|
|
119
|
+
supervisor,
|
|
120
|
+
log,
|
|
121
|
+
flaws,
|
|
122
|
+
// c1541 lists the disk, so an autostart index selects a program.
|
|
123
|
+
directory: async (imageType, image) => {
|
|
124
|
+
const { result } = await inspect({ action: "directory", imageType }, image, { supervisor, signal: new AbortController().signal, log });
|
|
125
|
+
return result.action === "directory" ? result.entries : [];
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
114
128
|
tools: createToolDispatcher({ supervisor, log, vice }),
|
|
115
129
|
});
|
|
116
130
|
} catch (error) {
|
|
@@ -130,4 +144,10 @@ async function main(): Promise<number> {
|
|
|
130
144
|
return 0;
|
|
131
145
|
}
|
|
132
146
|
|
|
147
|
+
const oldNode = oldNodeRefusal();
|
|
148
|
+
if (oldNode !== undefined) {
|
|
149
|
+
log(oldNode);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
|
|
133
153
|
process.exitCode = await main();
|
package/src/host/server.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { encodeFrame, MessageReader } from "../protocol/framing.ts";
|
|
|
4
4
|
import { checkHello, isOneOf, HEARTBEAT_TIMEOUT_MS, parseClientMessage, ProtocolError, type HostMessage, type ReceivedRequest, type VideoStandard, type WireError, WireFailure } from "../protocol/messages.ts";
|
|
5
5
|
import { TOOL_OPERATIONS, validateToolParams, type ToolOperation, type ToolOperations } from "../protocol/tools.ts";
|
|
6
6
|
import { attachmentCount, VICE_OPERATIONS, validateViceParams, type ViceOperation, type ViceOperations } from "../protocol/vice.ts";
|
|
7
|
-
import { elapsedMs, trace } from "../trace.ts";
|
|
7
|
+
import { elapsedMs, summarize, trace } from "../trace.ts";
|
|
8
8
|
|
|
9
9
|
/** One live VICE session, owned by one connection. */
|
|
10
10
|
export interface ViceSessionHandle {
|
|
@@ -234,8 +234,15 @@ class Connection {
|
|
|
234
234
|
|
|
235
235
|
async #answer(request: ReceivedRequest, attachments: Buffer[]): Promise<void> {
|
|
236
236
|
const started = performance.now();
|
|
237
|
-
const record = (outcome: Record<string, unknown>) =>
|
|
238
|
-
|
|
237
|
+
const record = (outcome: Record<string, unknown>) => {
|
|
238
|
+
const ms = elapsedMs(started);
|
|
239
|
+
trace().event("request", { connection: this.#id, op: request.op, id: request.id, params: request.params, attachments: attachments.length, ms, ...outcome });
|
|
240
|
+
// A call from a skill script (c1541, petcat, status) is always logged; the many calls of a VICE session only in the trace.
|
|
241
|
+
if (this.#isTool) {
|
|
242
|
+
const what = outcome.ok === true ? "ok" : `${String(outcome.code)}: ${String(outcome.message)}`;
|
|
243
|
+
this.#log(`${request.op} ${JSON.stringify(summarize(request.params) ?? {})}: ${what} (${ms} ms)`);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
239
246
|
try {
|
|
240
247
|
let result: unknown;
|
|
241
248
|
let replyAttachments: Buffer[] = [];
|
package/src/host/tools/petcat.ts
CHANGED
|
@@ -18,7 +18,7 @@ const TIMEOUT_MS = 30_000;
|
|
|
18
18
|
const LISTING = "listing.txt";
|
|
19
19
|
/**
|
|
20
20
|
* petcat's listing lines (" 10 sys2061"), as number and text. petcat
|
|
21
|
-
* prints π ($ff) as `~`, which is no PETSCII character: the text has
|
|
21
|
+
* prints π ($ff) as `~`, which is no PETSCII character: the text has π.
|
|
22
22
|
*/
|
|
23
23
|
export function parseListing(listing: string): Array<{ number: number; text: string }> {
|
|
24
24
|
const lines: Array<{ number: number; text: string }> = [];
|
package/src/host/tools/status.ts
CHANGED
|
@@ -38,7 +38,7 @@ export async function hostStatus(context: ToolContext, started?: ViceVersion): P
|
|
|
38
38
|
);
|
|
39
39
|
const tools = started === undefined ? probed : [{ name: VICE.name, found: true, path: findVice(context.env ?? process.env), version: versionText(started), runs: true }, ...probed];
|
|
40
40
|
const checked = tools.map((status, index) => requireMinimum(status, specs[index]!));
|
|
41
|
-
// VICE runs with its known flaws; status names them
|
|
41
|
+
// VICE runs with its known flaws; status names them.
|
|
42
42
|
const vice = checked[0]!;
|
|
43
43
|
const version = vice.runs ? viceVersionOf(vice.version ?? "") : undefined;
|
|
44
44
|
const flaws = version === undefined ? [] : viceFlaws(version);
|
package/src/host/vice/adapter.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import { WireFailure, type VideoStandard } from "../../protocol/messages.ts";
|
|
5
5
|
import { type Comparison, type Condition, type BacktraceFrame, type HistoryEntry, type Instruction, type MemmapRange, type ProfileEntry, type JoystickState, type MemoryView, type RegisterValues, type Registers, type Space } from "../../protocol/vice.ts";
|
|
6
6
|
import { COVERAGE, formatC64Address, formatC64Byte } from "../../c64.ts";
|
|
7
|
-
import {
|
|
7
|
+
import type { IndexedFrame } from "../../graphics.ts";
|
|
8
|
+
import { decodeDisplay, decodePalette, visibleFrame } from "./screen.ts";
|
|
8
9
|
import type { TextMonitor } from "./text-monitor.ts";
|
|
9
10
|
import type { CiaHidden } from "./chips.ts";
|
|
10
11
|
import {
|
|
@@ -260,7 +261,7 @@ function unknownForm(what: string, lines: string[]): WireFailure {
|
|
|
260
261
|
return new WireFailure("operation-failed", `VICE printed the ${what} in a form that c64-re-tools does not read: ${JSON.stringify(lines.slice(0, 3))}`);
|
|
261
262
|
}
|
|
262
263
|
|
|
263
|
-
/** The coverage byte
|
|
264
|
+
/** The coverage byte of one memmap row's I/O, ROM and RAM columns, each "rwx" with "-" for no access. */
|
|
264
265
|
export function memmapFlags(io: string, rom: string, ram: string): number {
|
|
265
266
|
let flags = 0;
|
|
266
267
|
if (ram[0] === "r") flags |= COVERAGE.ramRead;
|
|
@@ -434,7 +435,7 @@ export class ViceAdapter {
|
|
|
434
435
|
|
|
435
436
|
/**
|
|
436
437
|
* The timer latches and the interrupt mask of the CIA at `base`, from VICE's
|
|
437
|
-
* text-monitor dump: no register read gives them
|
|
438
|
+
* text-monitor dump: no register read gives them.
|
|
438
439
|
*/
|
|
439
440
|
async ciaHidden(base: 0xdc00 | 0xdd00): Promise<CiaHidden> {
|
|
440
441
|
const answer = await this.#computerText(`io ${hex4(base)}`);
|
package/src/host/vice/process.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// - autostart of a PRG needs `-autostartprgmode 1` (inject into RAM);
|
|
13
13
|
// - the joyport command reaches the C64 only through the "Joyport I/O
|
|
14
14
|
// simulation" device (37). In VICE 3.10 and older it holds bits 5-7 of
|
|
15
|
-
// both CIA 1 ports low (VICE bug #2202, a known flaw
|
|
15
|
+
// both CIA 1 ports low (VICE bug #2202, a known flaw);
|
|
16
16
|
// - with the default JAM action a jammed CPU hangs silently; -jamaction 2
|
|
17
17
|
// enters the monitor instead;
|
|
18
18
|
// - the monitor's profiler prints numbers in the host locale, so VICE runs
|
|
@@ -97,7 +97,7 @@ export function viceArguments(options: {
|
|
|
97
97
|
// Autostart injects a PRG into RAM; the default mode copies it to a disk image first.
|
|
98
98
|
"-autostartprgmode",
|
|
99
99
|
"1",
|
|
100
|
-
// VICE's default waits a random time before an autostart types its LOAD, so two runs of one file differ
|
|
100
|
+
// VICE's default waits a random time before an autostart types its LOAD, so two runs of one file differ.
|
|
101
101
|
"+autostart-delay-random",
|
|
102
102
|
// The same RAM after every power-up: a hard reset is a power cycle, and VICE's default can flip
|
|
103
103
|
// random bits, so two runs from one start state could differ.
|
package/src/host/vice/screen.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
// Canonical emulator-frame capture: VICE's own rendered VIC-II frame, never a
|
|
2
2
|
// desktop screenshot; the frame comparison and difference image for baselines.
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import type { IndexedFrame } from "../../graphics.ts";
|
|
6
5
|
import type { VideoStandard } from "../../protocol/messages.ts";
|
|
7
6
|
import { FRAME_SIZE } from "../../protocol/vice.ts";
|
|
8
7
|
|
|
9
|
-
export interface IndexedFrame {
|
|
10
|
-
width: number;
|
|
11
|
-
height: number;
|
|
12
|
-
/** One palette index per pixel, row by row. */
|
|
13
|
-
pixels: Uint8Array;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
8
|
export interface RawDisplay {
|
|
17
9
|
/** Full buffer size, blanking included. */
|
|
18
10
|
bufferWidth: number;
|
|
@@ -83,39 +75,6 @@ export function decodePalette(body: Buffer): Array<[number, number, number]> {
|
|
|
83
75
|
return colours;
|
|
84
76
|
}
|
|
85
77
|
|
|
86
|
-
function chunk(type: string, data: Uint8Array): Buffer {
|
|
87
|
-
const length = Buffer.alloc(4);
|
|
88
|
-
length.writeUInt32BE(data.length, 0);
|
|
89
|
-
const body = Buffer.concat([Buffer.from(type, "latin1"), data]);
|
|
90
|
-
const crc = Buffer.alloc(4);
|
|
91
|
-
crc.writeUInt32BE(crc32(body), 0);
|
|
92
|
-
return Buffer.concat([length, body, crc]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Encodes an indexed frame as an 8-bit palette PNG. */
|
|
96
|
-
export function encodePng(frame: IndexedFrame, palette: ReadonlyArray<readonly [number, number, number]>): Buffer {
|
|
97
|
-
for (const index of frame.pixels) {
|
|
98
|
-
if (index >= palette.length) throw new Error(`pixel colour ${index} is not in the ${palette.length}-colour palette`);
|
|
99
|
-
}
|
|
100
|
-
const header = Buffer.alloc(13);
|
|
101
|
-
header.writeUInt32BE(frame.width, 0);
|
|
102
|
-
header.writeUInt32BE(frame.height, 4);
|
|
103
|
-
header[8] = 8; // bit depth
|
|
104
|
-
header[9] = 3; // indexed colour
|
|
105
|
-
const rows = Buffer.alloc((frame.width + 1) * frame.height);
|
|
106
|
-
for (let row = 0; row < frame.height; row++) {
|
|
107
|
-
// Filter byte 0 (none), then the row.
|
|
108
|
-
rows.set(frame.pixels.subarray(row * frame.width, (row + 1) * frame.width), row * (frame.width + 1) + 1);
|
|
109
|
-
}
|
|
110
|
-
return Buffer.concat([
|
|
111
|
-
Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
|
|
112
|
-
chunk("IHDR", header),
|
|
113
|
-
chunk("PLTE", Buffer.from(palette.flat())),
|
|
114
|
-
chunk("IDAT", deflateSync(rows)),
|
|
115
|
-
chunk("IEND", new Uint8Array(0)),
|
|
116
|
-
]);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
78
|
export interface FrameDifference {
|
|
120
79
|
mismatchingPixels: number;
|
|
121
80
|
/** Pixels compared: the frame minus the masked ones. */
|