c64bridge 0.7.4 → 0.8.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.
Files changed (109) hide show
  1. package/.github/prompts/README.md +14 -4
  2. package/.github/prompts/assembly-routine-session.prompt.md +9 -9
  3. package/.github/prompts/basic-program-session.prompt.md +11 -9
  4. package/.github/prompts/drive-management-session.prompt.md +9 -9
  5. package/.github/prompts/hello-world-session.prompt.md +17 -0
  6. package/.github/prompts/petscii-art-session.prompt.md +9 -9
  7. package/.github/prompts/play-classic-game.prompt.md +9 -9
  8. package/.github/prompts/pr-converge.prompt.md +13 -0
  9. package/.github/prompts/sid-music-session.prompt.md +9 -9
  10. package/AGENTS.md +59 -60
  11. package/README.md +550 -191
  12. package/data/assembly/assembly-spec.md +290 -1
  13. package/data/context/bootstrap.md +26 -2
  14. package/data/context/fast-paths.md +44 -0
  15. package/data/graphics/petscii-style-guide.md +6 -2
  16. package/data/graphics/sprite-charset-best-practices.md +5 -5
  17. package/dist/.tsbuildinfo +1 -1
  18. package/dist/c64Client.js +990 -26
  19. package/dist/config.js +5 -1
  20. package/dist/device.js +680 -57
  21. package/dist/diagnostics.js +371 -0
  22. package/dist/mcp/metadata.js +39 -0
  23. package/dist/mcp-server.js +170 -20
  24. package/dist/platform.js +10 -2
  25. package/dist/prompts/registry.js +166 -309
  26. package/dist/rag/init.js +37 -1
  27. package/dist/rag/knowledgeIndex.js +18 -6
  28. package/dist/rag/rateLimiter.js +2 -2
  29. package/dist/streamCapture.js +135 -0
  30. package/dist/tools/audio.js +3 -1
  31. package/dist/tools/debug.js +470 -0
  32. package/dist/tools/developer.js +10 -4
  33. package/dist/tools/graphics.js +183 -17
  34. package/dist/tools/machineControl.js +11 -5
  35. package/dist/tools/memory.js +19 -10
  36. package/dist/tools/meta/audio.js +240 -1
  37. package/dist/tools/meta/config.js +20 -28
  38. package/dist/tools/meta/configInventory.js +104 -0
  39. package/dist/tools/meta/diagnostics.js +45 -2
  40. package/dist/tools/meta/graphics.js +14 -6
  41. package/dist/tools/meta/index.js +1 -0
  42. package/dist/tools/meta/memory.js +20 -6
  43. package/dist/tools/meta/program.js +473 -0
  44. package/dist/tools/pollValidator.js +4 -2
  45. package/dist/tools/programRunners.js +8 -4
  46. package/dist/tools/registry/config.js +17 -0
  47. package/dist/tools/registry/disk.js +7 -0
  48. package/dist/tools/registry/drive.js +3 -0
  49. package/dist/tools/registry/graphics.js +111 -27
  50. package/dist/tools/registry/index.js +6 -0
  51. package/dist/tools/registry/memory.js +1 -0
  52. package/dist/tools/registry/platform.js +121 -0
  53. package/dist/tools/registry/program.js +26 -8
  54. package/dist/tools/registry/sound.js +96 -0
  55. package/dist/tools/registry/system.js +15 -2
  56. package/dist/tools/storage.js +1 -0
  57. package/dist/tools/streaming.js +1 -0
  58. package/dist/tools/types.js +38 -4
  59. package/dist/tools/vice.js +160 -0
  60. package/dist/vicBitmap.js +317 -0
  61. package/dist/vice/mockServer.js +439 -0
  62. package/dist/vice/process.js +310 -0
  63. package/dist/vice/readiness.js +18 -2
  64. package/dist/vice/viceClient.js +382 -22
  65. package/doc/developer.md +71 -25
  66. package/doc/img/backend-switch/hello-c64u.png +0 -0
  67. package/doc/img/backend-switch/hello-vice-and-c64u.png +0 -0
  68. package/doc/img/backend-switch/hello-vice.png +0 -0
  69. package/doc/img/backend-switch/metadata.json +50 -0
  70. package/doc/plans/vice-backend-switch/plan.md +486 -0
  71. package/doc/plans/vice-backend-switch-2/plan.md +48 -0
  72. package/doc/plans/vice-support/notes.md +1 -0
  73. package/doc/plans/vice-support/plan.md +5 -0
  74. package/doc/plans/vice-support/rollout.md +23 -17
  75. package/doc/reviews/vice-support-review.md +185 -0
  76. package/doc/troubleshooting-mcp.md +35 -6
  77. package/doc/vice/support-matrix.md +54 -0
  78. package/mcp/prompts.json +292 -0
  79. package/mcp/protocol-examples.json +46 -0
  80. package/mcp/resources.json +940 -0
  81. package/mcp/schemas/c64_config.schema.json +350 -0
  82. package/mcp/schemas/c64_debug.schema.json +380 -0
  83. package/mcp/schemas/c64_disk.schema.json +278 -0
  84. package/mcp/schemas/c64_drive.schema.json +120 -0
  85. package/mcp/schemas/c64_extract.schema.json +302 -0
  86. package/mcp/schemas/c64_graphics.schema.json +333 -0
  87. package/mcp/schemas/c64_memory.schema.json +155 -0
  88. package/mcp/schemas/c64_printer.schema.json +218 -0
  89. package/mcp/schemas/c64_program.schema.json +391 -0
  90. package/mcp/schemas/c64_rag.schema.json +67 -0
  91. package/mcp/schemas/c64_select_backend.schema.json +32 -0
  92. package/mcp/schemas/c64_sound.schema.json +644 -0
  93. package/mcp/schemas/c64_stream.schema.json +61 -0
  94. package/mcp/schemas/c64_system.schema.json +227 -0
  95. package/mcp/schemas/c64_vice.schema.json +63 -0
  96. package/mcp/server.json +15 -0
  97. package/mcp/tools.json +4517 -0
  98. package/mcp.json +90 -15
  99. package/package.json +11 -4
  100. package/scripts/capture-backend-switch-screenshots.mjs +276 -0
  101. package/scripts/generate-mcp-interface.ts +289 -0
  102. package/scripts/invoke-bun.mjs +67 -21
  103. package/scripts/mockC64Server.mjs +162 -6
  104. package/scripts/prepare-release.mjs +59 -2
  105. package/scripts/run-coverage.mjs +284 -0
  106. package/scripts/run-tests.mjs +95 -39
  107. package/scripts/run-tests.ts +378 -73
  108. package/scripts/update-readme.ts +147 -13
  109. package/server.json +94 -0
@@ -0,0 +1,439 @@
1
+ import net from "node:net";
2
+ import { asciiToScreenCodes } from "./readiness.js";
3
+ const SCREEN_BASE = 0x0400;
4
+ const SCREEN_SIZE = 40 * 25;
5
+ function buildDefaultRegisterMetadata() {
6
+ return Object.freeze([
7
+ { id: 0, name: "PC", bits: 16 },
8
+ { id: 1, name: "A", bits: 8 },
9
+ { id: 2, name: "X", bits: 8 },
10
+ { id: 3, name: "Y", bits: 8 },
11
+ { id: 4, name: "SP", bits: 8 },
12
+ { id: 5, name: "SR", bits: 8 },
13
+ ]);
14
+ }
15
+ function buildResponse(cmd, reqId, body = Buffer.alloc(0), err = 0) {
16
+ const header = Buffer.alloc(12 + body.length);
17
+ header[0] = 0x02;
18
+ header[1] = 0x02;
19
+ header.writeUInt32LE(body.length, 2);
20
+ header[6] = cmd;
21
+ header[7] = err;
22
+ header.writeUInt32LE(reqId >>> 0, 8);
23
+ if (body.length > 0) {
24
+ body.copy(header, 12);
25
+ }
26
+ return header;
27
+ }
28
+ export class ViceMockServer {
29
+ constructor(options) {
30
+ this.server = null;
31
+ this.memory = new Uint8Array(0x10000);
32
+ this.helloReady = false;
33
+ this.checkpoints = new Map();
34
+ this.nextCheckpointId = 1;
35
+ this.registerMetadata = buildDefaultRegisterMetadata();
36
+ this.registerValues = new Map();
37
+ this.resources = new Map();
38
+ this.sockets = new Set();
39
+ this.host = options?.host ?? "127.0.0.1";
40
+ this.requestedPort = options?.port;
41
+ this.resetState();
42
+ }
43
+ async start() {
44
+ if (this.server)
45
+ throw new Error("ViceMockServer already started");
46
+ await new Promise((resolve, reject) => {
47
+ this.server = net.createServer((socket) => this.handleSocket(socket));
48
+ const onError = (error) => {
49
+ this.server?.off("listening", onListening);
50
+ reject(error);
51
+ };
52
+ const onListening = () => {
53
+ this.server?.off("error", onError);
54
+ resolve();
55
+ };
56
+ this.server.once("error", onError);
57
+ this.server.listen(this.requestedPort ?? 0, this.host, () => resolve());
58
+ });
59
+ const address = this.server.address();
60
+ if (!address || typeof address === "string")
61
+ throw new Error("Failed to determine mock server port");
62
+ return { port: address.port };
63
+ }
64
+ async stop() {
65
+ if (!this.server)
66
+ return;
67
+ for (const socket of this.sockets) {
68
+ socket.destroy();
69
+ }
70
+ this.sockets.clear();
71
+ await new Promise((resolve) => this.server.close(() => resolve()));
72
+ this.server = null;
73
+ }
74
+ handleSocket(socket) {
75
+ this.sockets.add(socket);
76
+ let buffer = Buffer.alloc(0);
77
+ socket.once("close", () => {
78
+ this.sockets.delete(socket);
79
+ buffer = Buffer.alloc(0);
80
+ });
81
+ socket.on("data", (chunk) => {
82
+ const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
83
+ buffer = Buffer.concat([buffer, data]);
84
+ buffer = this.processBuffer(buffer, socket);
85
+ });
86
+ }
87
+ processBuffer(buffer, socket) {
88
+ while (buffer.length >= 11) {
89
+ if (buffer[0] !== 0x02 || buffer[1] !== 0x02) {
90
+ buffer = buffer.slice(1);
91
+ continue;
92
+ }
93
+ const bodyLen = buffer.readUInt32LE(2);
94
+ const frameLen = 11 + bodyLen;
95
+ if (buffer.length < frameLen)
96
+ break;
97
+ const reqId = buffer.readUInt32LE(6);
98
+ const cmd = buffer[10];
99
+ const body = buffer.slice(11, frameLen);
100
+ buffer = buffer.slice(frameLen);
101
+ const response = this.handleCommand(cmd, reqId, body, socket);
102
+ if (response) {
103
+ socket.write(response);
104
+ }
105
+ }
106
+ return buffer;
107
+ }
108
+ handleCommand(cmd, reqId, body, socket) {
109
+ switch (cmd) {
110
+ case 0x85: // info
111
+ return buildResponse(cmd, reqId);
112
+ case 0xCC: // reset
113
+ this.resetState();
114
+ return buildResponse(cmd, reqId);
115
+ case 0x11: { // checkpoint get
116
+ const id = body.readUInt32LE(0);
117
+ const checkpoint = this.checkpoints.get(id);
118
+ if (!checkpoint) {
119
+ return buildResponse(cmd, reqId, Buffer.alloc(0), 0x83);
120
+ }
121
+ return buildResponse(cmd, reqId, this.encodeCheckpoint(checkpoint));
122
+ }
123
+ case 0x12: { // checkpoint create
124
+ const start = body.readUInt16LE(0);
125
+ const end = body.readUInt16LE(2);
126
+ const stopOnHit = body[4] === 1;
127
+ const enabled = body[5] === 1;
128
+ const mask = body[6] ?? 0x04;
129
+ const temporary = body[7] === 1;
130
+ const memspace = (body[8] ?? 0) & 0xff;
131
+ const id = this.nextCheckpointId++;
132
+ const checkpoint = {
133
+ id,
134
+ start,
135
+ end,
136
+ stopOnHit,
137
+ enabled,
138
+ temporary,
139
+ load: (mask & 0x01) !== 0,
140
+ store: (mask & 0x02) !== 0,
141
+ execute: (mask & 0x04) !== 0,
142
+ hitCount: 0,
143
+ ignoreCount: 0,
144
+ hasCondition: false,
145
+ memspace,
146
+ };
147
+ this.checkpoints.set(id, checkpoint);
148
+ return buildResponse(0x11, reqId, this.encodeCheckpoint(checkpoint));
149
+ }
150
+ case 0x13: { // checkpoint delete
151
+ const id = body.readUInt32LE(0);
152
+ this.checkpoints.delete(id);
153
+ return buildResponse(cmd, reqId);
154
+ }
155
+ case 0x14: { // checkpoint list
156
+ for (const checkpoint of this.checkpoints.values()) {
157
+ socket.write(buildResponse(0x11, reqId, this.encodeCheckpoint(checkpoint)));
158
+ }
159
+ return buildResponse(cmd, reqId);
160
+ }
161
+ case 0x15: { // checkpoint toggle
162
+ const id = body.readUInt32LE(0);
163
+ const enabled = body[4] === 1;
164
+ const checkpoint = this.checkpoints.get(id);
165
+ if (!checkpoint) {
166
+ return buildResponse(cmd, reqId, Buffer.alloc(0), 0x83);
167
+ }
168
+ checkpoint.enabled = enabled;
169
+ return buildResponse(cmd, reqId);
170
+ }
171
+ case 0x22: { // checkpoint condition
172
+ const id = body.readUInt32LE(0);
173
+ const checkpoint = this.checkpoints.get(id);
174
+ if (!checkpoint) {
175
+ return buildResponse(cmd, reqId, Buffer.alloc(0), 0x83);
176
+ }
177
+ checkpoint.hasCondition = (body[4] ?? 0) > 0;
178
+ return buildResponse(cmd, reqId);
179
+ }
180
+ case 0x01: { // mem get
181
+ const start = body.readUInt16LE(1);
182
+ const end = body.readUInt16LE(3);
183
+ const length = Math.max(0, end - start + 1);
184
+ const payload = Buffer.alloc(2 + length);
185
+ payload.writeUInt16LE(length, 0);
186
+ for (let i = 0; i < length; i += 1) {
187
+ payload[2 + i] = this.memory[(start + i) & 0xffff];
188
+ }
189
+ return buildResponse(cmd, reqId, payload);
190
+ }
191
+ case 0x02: { // mem set
192
+ const start = body.readUInt16LE(1);
193
+ const end = body.readUInt16LE(3);
194
+ const payload = body.subarray(8);
195
+ for (let i = 0; i < payload.length && start + i <= 0xffff; i += 1) {
196
+ this.memory[start + i] = payload[i];
197
+ }
198
+ if (start <= SCREEN_BASE && end >= SCREEN_BASE) {
199
+ this.helloReady = false;
200
+ }
201
+ return buildResponse(cmd, reqId);
202
+ }
203
+ case 0x72: { // keyboard feed
204
+ const len = body[0] ?? 0;
205
+ const text = body.subarray(1, 1 + len).toString("ascii").toUpperCase();
206
+ if (text.includes("RUN")) {
207
+ this.renderHello();
208
+ }
209
+ return buildResponse(cmd, reqId);
210
+ }
211
+ case 0x31: { // registers get
212
+ const payload = this.encodeRegisterValues();
213
+ return buildResponse(cmd, reqId, payload);
214
+ }
215
+ case 0x32: { // registers set
216
+ this.applyRegisterWrites(body);
217
+ const payload = this.encodeRegisterValues();
218
+ return buildResponse(0x31, reqId, payload);
219
+ }
220
+ case 0x71: // step instructions
221
+ return buildResponse(cmd, reqId);
222
+ case 0x73: // step return
223
+ return buildResponse(cmd, reqId);
224
+ case 0x83: { // registers metadata
225
+ const payload = this.encodeRegisterMetadata();
226
+ return buildResponse(cmd, reqId, payload);
227
+ }
228
+ case 0x84: { // display get
229
+ const payload = this.encodeDisplay();
230
+ return buildResponse(cmd, reqId, payload);
231
+ }
232
+ case 0x51: { // resource get
233
+ const nameLen = body[0] ?? 0;
234
+ const name = body.subarray(1, 1 + nameLen).toString("utf8");
235
+ const value = this.resources.get(name);
236
+ if (value === undefined) {
237
+ return buildResponse(cmd, reqId, Buffer.from([0, 0]));
238
+ }
239
+ if (typeof value === "number") {
240
+ const buffer = Buffer.alloc(2 + 4);
241
+ buffer[0] = 1;
242
+ buffer[1] = 4;
243
+ buffer.writeInt32LE(value, 2);
244
+ return buildResponse(cmd, reqId, buffer);
245
+ }
246
+ const strBytes = Buffer.from(value, "utf8");
247
+ const buffer = Buffer.alloc(2 + strBytes.length);
248
+ buffer[0] = 0;
249
+ buffer[1] = strBytes.length & 0xff;
250
+ strBytes.copy(buffer, 2);
251
+ return buildResponse(cmd, reqId, buffer);
252
+ }
253
+ case 0x52: { // resource set
254
+ const isNumber = body[0] === 1;
255
+ const nameLen = body[1] ?? 0;
256
+ const name = body.subarray(2, 2 + nameLen).toString("utf8");
257
+ if (isNumber) {
258
+ const valueLen = body[2 + nameLen] ?? 0;
259
+ const valueBytes = body.subarray(3 + nameLen, 3 + nameLen + valueLen);
260
+ const padded = Buffer.alloc(4);
261
+ valueBytes.copy(padded, 0, 0, Math.min(4, valueBytes.length));
262
+ const value = padded.readInt32LE(0);
263
+ this.resources.set(name, value);
264
+ }
265
+ else {
266
+ const valueLen = body[2 + nameLen] ?? 0;
267
+ const value = body.subarray(3 + nameLen, 3 + nameLen + valueLen).toString("utf8");
268
+ this.resources.set(name, value);
269
+ }
270
+ return buildResponse(cmd, reqId);
271
+ }
272
+ case 0xAA: // exit monitor
273
+ return buildResponse(cmd, reqId);
274
+ case 0xBB: // quit
275
+ queueMicrotask(() => socket.destroy());
276
+ return buildResponse(cmd, reqId);
277
+ default:
278
+ return buildResponse(cmd, reqId, Buffer.alloc(0), 0x83);
279
+ }
280
+ }
281
+ encodeCheckpoint(checkpoint) {
282
+ const buffer = Buffer.alloc(23);
283
+ buffer.writeUInt32LE(checkpoint.id, 0);
284
+ buffer[4] = checkpoint.hitCount > 0 ? 1 : 0;
285
+ buffer.writeUInt16LE(checkpoint.start & 0xffff, 5);
286
+ buffer.writeUInt16LE(checkpoint.end & 0xffff, 7);
287
+ buffer[9] = checkpoint.stopOnHit ? 1 : 0;
288
+ buffer[10] = checkpoint.enabled ? 1 : 0;
289
+ let mask = 0;
290
+ if (checkpoint.load)
291
+ mask |= 0x01;
292
+ if (checkpoint.store)
293
+ mask |= 0x02;
294
+ if (checkpoint.execute)
295
+ mask |= 0x04;
296
+ buffer[11] = mask;
297
+ buffer[12] = checkpoint.temporary ? 1 : 0;
298
+ buffer.writeUInt32LE(checkpoint.hitCount >>> 0, 13);
299
+ buffer.writeUInt32LE(checkpoint.ignoreCount >>> 0, 17);
300
+ buffer[21] = checkpoint.hasCondition ? 1 : 0;
301
+ buffer[22] = checkpoint.memspace & 0xff;
302
+ return buffer;
303
+ }
304
+ encodeRegisterValues() {
305
+ const entries = [];
306
+ for (const meta of this.registerMetadata) {
307
+ const size = Math.max(1, Math.ceil(Math.min(meta.bits, 16) / 8));
308
+ const value = this.registerValues.get(meta.id) ?? 0;
309
+ const entryLength = 2 + size;
310
+ const entry = Buffer.alloc(entryLength);
311
+ entry[0] = size + 1;
312
+ entry[1] = meta.id & 0xff;
313
+ entry.writeUIntLE(value >>> 0, 2, size);
314
+ entries.push(entry);
315
+ }
316
+ const payloadLength = 2 + entries.reduce((sum, entry) => sum + entry.length, 0);
317
+ const payload = Buffer.alloc(payloadLength);
318
+ payload.writeUInt16LE(entries.length, 0);
319
+ let offset = 2;
320
+ for (const entry of entries) {
321
+ entry.copy(payload, offset);
322
+ offset += entry.length;
323
+ }
324
+ return payload;
325
+ }
326
+ encodeRegisterMetadata() {
327
+ const entries = [];
328
+ for (const meta of this.registerMetadata) {
329
+ const nameBytes = Buffer.from(meta.name, "utf8");
330
+ const entryLength = 4 + nameBytes.length;
331
+ const entry = Buffer.alloc(entryLength);
332
+ entry[0] = 3 + nameBytes.length;
333
+ entry[1] = meta.id & 0xff;
334
+ entry[2] = meta.bits & 0xff;
335
+ entry[3] = nameBytes.length & 0xff;
336
+ nameBytes.copy(entry, 4);
337
+ entries.push(entry);
338
+ }
339
+ const payloadLength = 2 + entries.reduce((sum, entry) => sum + entry.length, 0);
340
+ const payload = Buffer.alloc(payloadLength);
341
+ payload.writeUInt16LE(entries.length, 0);
342
+ let offset = 2;
343
+ for (const entry of entries) {
344
+ entry.copy(payload, offset);
345
+ offset += entry.length;
346
+ }
347
+ return payload;
348
+ }
349
+ encodeDisplay() {
350
+ const pixels = Buffer.alloc(64, 0);
351
+ const header = Buffer.alloc(17);
352
+ const pixelLength = Buffer.alloc(4);
353
+ header.writeUInt32LE(13, 0);
354
+ header.writeUInt16LE(320, 4);
355
+ header.writeUInt16LE(200, 6);
356
+ header.writeUInt16LE(0, 8);
357
+ header.writeUInt16LE(0, 10);
358
+ header.writeUInt16LE(320, 12);
359
+ header.writeUInt16LE(200, 14);
360
+ header[16] = 8;
361
+ pixelLength.writeUInt32LE(pixels.length, 0);
362
+ return Buffer.concat([header, pixelLength, pixels]);
363
+ }
364
+ applyRegisterWrites(body) {
365
+ if (body.length < 3) {
366
+ return;
367
+ }
368
+ const count = body.readUInt16LE(1);
369
+ let offset = 3;
370
+ for (let index = 0; index < count && offset < body.length; index += 1) {
371
+ const entrySize = body[offset] ?? 0;
372
+ const entryLength = entrySize + 1;
373
+ if (entryLength < 3 || offset + entryLength > body.length) {
374
+ break;
375
+ }
376
+ const id = body[offset + 1] ?? 0;
377
+ const valueBytes = Math.max(0, entryLength - 2);
378
+ let value = 0;
379
+ if (valueBytes > 0) {
380
+ value = body.readUIntLE(offset + 2, Math.min(valueBytes, 4));
381
+ }
382
+ const meta = this.registerMetadata.find((m) => m.id === id);
383
+ if (meta) {
384
+ const mask = meta.bits >= 31 ? 0xffffffff : (1 << meta.bits) - 1;
385
+ this.registerValues.set(id, value & mask);
386
+ }
387
+ else {
388
+ this.registerValues.set(id, value);
389
+ }
390
+ offset += entryLength;
391
+ }
392
+ }
393
+ resetState() {
394
+ this.memory.fill(0);
395
+ // Fill screen with spaces and READY.
396
+ this.memory.fill(0x20, SCREEN_BASE, SCREEN_BASE + SCREEN_SIZE);
397
+ const ready = asciiToScreenCodes("READY.");
398
+ ready.copy(Buffer.from(this.memory.buffer, SCREEN_BASE, ready.length));
399
+ // Initialise BASIC zero-page pointers so waitForBasicReady() succeeds
400
+ // immediately instead of spinning for 20 s on all-zero memory.
401
+ // $002B-$002C TXTTAB = $0801 (start of BASIC text)
402
+ // $002D-$002E VARTAB = $0803 (start of variables)
403
+ // $002F-$0030 ARYTAB = $0803 (start of arrays)
404
+ // $0031-$0032 STREND = $0803 (end of BASIC storage)
405
+ this.memory[0x2B] = 0x01;
406
+ this.memory[0x2C] = 0x08;
407
+ this.memory[0x2D] = 0x03;
408
+ this.memory[0x2E] = 0x08;
409
+ this.memory[0x2F] = 0x03;
410
+ this.memory[0x30] = 0x08;
411
+ this.memory[0x31] = 0x03;
412
+ this.memory[0x32] = 0x08;
413
+ this.helloReady = false;
414
+ this.checkpoints.clear();
415
+ this.nextCheckpointId = 1;
416
+ this.registerValues.clear();
417
+ for (const meta of this.registerMetadata) {
418
+ this.registerValues.set(meta.id, 0);
419
+ }
420
+ this.resources = new Map([
421
+ ["C64Model", 2],
422
+ ["SidEngine", 1],
423
+ ]);
424
+ }
425
+ renderHello() {
426
+ if (this.helloReady)
427
+ return;
428
+ const row = 10;
429
+ const offset = SCREEN_BASE + row * 40;
430
+ const hello = asciiToScreenCodes("HELLO");
431
+ hello.copy(Buffer.from(this.memory.buffer, offset, hello.length));
432
+ this.helloReady = true;
433
+ }
434
+ }
435
+ export async function startViceMockServer(options) {
436
+ const server = new ViceMockServer(options);
437
+ const { port } = await server.start();
438
+ return Object.assign(server, { port });
439
+ }