c64-debug-mcp 1.0.6 → 1.0.7
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 +61 -3
- package/dist/http.cjs +207 -11
- package/dist/http.js +207 -11
- package/dist/stdio.cjs +207 -11
- package/dist/stdio.js +207 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,64 @@ Debug Commodore 64 programs through conversation - AI-powered control of the VIC
|
|
|
14
14
|
- 📸 **Display Capture**: Capture screen state and text content
|
|
15
15
|
- ⌨️ **Input Control**: Send keyboard and joystick input
|
|
16
16
|
- 📝 **Program Loading**: Load PRG files and manage execution
|
|
17
|
+
- 🔢 **Flexible Formats**: Use C64 notation ($D000, $FF, %11111111) or standard formats (0xD000, 255, 0b11111111)
|
|
18
|
+
|
|
19
|
+
## Address Formats
|
|
20
|
+
|
|
21
|
+
The MCP server accepts C64 addresses in multiple formats for natural interaction:
|
|
22
|
+
|
|
23
|
+
- **C64 style**: `$D000` (dollar sign prefix - classic 6502 assembler notation)
|
|
24
|
+
- **C style**: `0xD000` (0x prefix - standard programming hex notation)
|
|
25
|
+
- **Decimal**: `53248` (traditional decimal format)
|
|
26
|
+
|
|
27
|
+
**Note**: Bare hex without prefix (e.g., `D000`) is NOT supported to avoid ambiguity with 4-digit decimals.
|
|
28
|
+
|
|
29
|
+
**Common C64 Addresses**:
|
|
30
|
+
- `$D000` - SID chip registers (sound)
|
|
31
|
+
- `$D020` - Border color register
|
|
32
|
+
- `$D021` - Background color register
|
|
33
|
+
- `$0400` - Default screen memory
|
|
34
|
+
- `$0800` - Common program start
|
|
35
|
+
- `$C000` - BASIC ROM start
|
|
36
|
+
|
|
37
|
+
## Byte Value Formats
|
|
38
|
+
|
|
39
|
+
The MCP server accepts byte values (0-255) in multiple formats for natural C64-style input:
|
|
40
|
+
|
|
41
|
+
- **C64 hex**: `$FF` (dollar sign prefix - classic 6502 notation)
|
|
42
|
+
- **C hex**: `0xFF` (0x prefix - standard programming notation)
|
|
43
|
+
- **C64 binary**: `%11111111` (percent prefix - classic 6502 bit notation)
|
|
44
|
+
- **C binary**: `0b11111111` (0b prefix - standard programming notation)
|
|
45
|
+
- **Decimal**: `255` (traditional decimal format)
|
|
46
|
+
|
|
47
|
+
**Note**: Bare hex/binary without prefix (e.g., `FF`, `11111111`) is NOT supported to avoid ambiguity.
|
|
48
|
+
|
|
49
|
+
**Mixed formats in arrays**:
|
|
50
|
+
```json
|
|
51
|
+
[255, "$FF", "0xFF", "%11111111", "0b11111111"]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Common C64 Byte Values**:
|
|
55
|
+
- `$00`-`$0F` - Color values (0-15)
|
|
56
|
+
- `$20` - PETSCII space character
|
|
57
|
+
- `$41` - PETSCII 'A' character
|
|
58
|
+
- `%00011011` - VIC-II D011 control register (text mode, 25 rows, screen on)
|
|
59
|
+
- `%11111111` - All bits set (enable all sprites, etc.)
|
|
60
|
+
|
|
61
|
+
**Use Cases**:
|
|
62
|
+
```javascript
|
|
63
|
+
// Set border to light blue
|
|
64
|
+
memory_write(address="$D020", data=["$0E"])
|
|
65
|
+
|
|
66
|
+
// Enable all 8 sprites
|
|
67
|
+
memory_write(address="$D015", data=["%11111111"])
|
|
68
|
+
|
|
69
|
+
// Write " AB" to screen (PETSCII)
|
|
70
|
+
memory_write(address="$0400", data=["$20", "$41", "$42"])
|
|
71
|
+
|
|
72
|
+
// Set VIC-II control register with bit pattern
|
|
73
|
+
memory_write(address="$D011", data=["%00011011"])
|
|
74
|
+
```
|
|
17
75
|
|
|
18
76
|
## Requirements
|
|
19
77
|
|
|
@@ -24,7 +82,7 @@ Debug Commodore 64 programs through conversation - AI-powered control of the VIC
|
|
|
24
82
|
## Installation
|
|
25
83
|
|
|
26
84
|
```bash
|
|
27
|
-
claude mcp add
|
|
85
|
+
claude mcp add c64-dev-tools -- npx -y c64-debug-mcp@latest
|
|
28
86
|
```
|
|
29
87
|
|
|
30
88
|
Or add manually to your MCP client config:
|
|
@@ -32,9 +90,9 @@ Or add manually to your MCP client config:
|
|
|
32
90
|
```json
|
|
33
91
|
{
|
|
34
92
|
"mcpServers": {
|
|
35
|
-
"
|
|
93
|
+
"c64-dev-tools": {
|
|
36
94
|
"command": "npx",
|
|
37
|
-
"args": ["-y", "c64-debug-mcp"]
|
|
95
|
+
"args": ["-y", "c64-debug-mcp@latest"]
|
|
38
96
|
}
|
|
39
97
|
}
|
|
40
98
|
}
|
package/dist/http.cjs
CHANGED
|
@@ -152,9 +152,193 @@ var import_zod3 = require("zod");
|
|
|
152
152
|
// src/schemas.ts
|
|
153
153
|
var import_zod2 = require("zod");
|
|
154
154
|
var warningSchema = warningItemSchema;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
function parseAddress16(input) {
|
|
156
|
+
if (typeof input === "number") {
|
|
157
|
+
if (!Number.isInteger(input) || input < 0 || input > 65535) {
|
|
158
|
+
throw new import_zod2.z.ZodError([
|
|
159
|
+
{
|
|
160
|
+
code: "custom",
|
|
161
|
+
message: `Address must be an integer between 0 and 65535 (0xFFFF), got ${input}`,
|
|
162
|
+
path: []
|
|
163
|
+
}
|
|
164
|
+
]);
|
|
165
|
+
}
|
|
166
|
+
return input;
|
|
167
|
+
}
|
|
168
|
+
if (typeof input !== "string") {
|
|
169
|
+
throw new import_zod2.z.ZodError([
|
|
170
|
+
{
|
|
171
|
+
code: "custom",
|
|
172
|
+
message: `Address must be a number or string, got ${typeof input}`,
|
|
173
|
+
path: []
|
|
174
|
+
}
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
const trimmed = input.trim();
|
|
178
|
+
let hexString;
|
|
179
|
+
let format;
|
|
180
|
+
if (trimmed.startsWith("$")) {
|
|
181
|
+
hexString = trimmed.slice(1);
|
|
182
|
+
format = "C64 hex ($)";
|
|
183
|
+
} else if (trimmed.toLowerCase().startsWith("0x")) {
|
|
184
|
+
hexString = trimmed.slice(2);
|
|
185
|
+
format = "C hex (0x)";
|
|
186
|
+
} else {
|
|
187
|
+
throw new import_zod2.z.ZodError([
|
|
188
|
+
{
|
|
189
|
+
code: "custom",
|
|
190
|
+
message: `Invalid address format: "${input}". Expected formats: decimal number (53248), hex with $ ($D000), or hex with 0x (0xD000). Bare hex not supported to avoid ambiguity.`,
|
|
191
|
+
path: []
|
|
192
|
+
}
|
|
193
|
+
]);
|
|
194
|
+
}
|
|
195
|
+
if (!/^[0-9A-Fa-f]{1,4}$/.test(hexString)) {
|
|
196
|
+
throw new import_zod2.z.ZodError([
|
|
197
|
+
{
|
|
198
|
+
code: "custom",
|
|
199
|
+
message: `Invalid ${format} address: "${input}". Hex portion must be 1-4 hex digits (0-9, A-F)`,
|
|
200
|
+
path: []
|
|
201
|
+
}
|
|
202
|
+
]);
|
|
203
|
+
}
|
|
204
|
+
const parsed = parseInt(hexString, 16);
|
|
205
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 65535) {
|
|
206
|
+
throw new import_zod2.z.ZodError([
|
|
207
|
+
{
|
|
208
|
+
code: "custom",
|
|
209
|
+
message: `Address out of range: "${input}" (${parsed}). Must be 0x0000-0xFFFF (0-65535)`,
|
|
210
|
+
path: []
|
|
211
|
+
}
|
|
212
|
+
]);
|
|
213
|
+
}
|
|
214
|
+
return parsed;
|
|
215
|
+
}
|
|
216
|
+
var address16Schema = import_zod2.z.preprocess(parseAddress16, import_zod2.z.number().int().min(0).max(65535)).describe("16-bit C64 address: decimal (53248) or hex string with prefix ($D000, 0xD000)");
|
|
217
|
+
function parseByte(input) {
|
|
218
|
+
if (typeof input === "number") {
|
|
219
|
+
if (!Number.isInteger(input) || input < 0 || input > 255) {
|
|
220
|
+
throw new import_zod2.z.ZodError([
|
|
221
|
+
{
|
|
222
|
+
code: "custom",
|
|
223
|
+
message: `Byte value must be an integer between 0 and 255 (0xFF), got ${input}`,
|
|
224
|
+
path: []
|
|
225
|
+
}
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
return input;
|
|
229
|
+
}
|
|
230
|
+
if (typeof input !== "string") {
|
|
231
|
+
throw new import_zod2.z.ZodError([
|
|
232
|
+
{
|
|
233
|
+
code: "custom",
|
|
234
|
+
message: `Byte value must be a number or string, got ${typeof input}`,
|
|
235
|
+
path: []
|
|
236
|
+
}
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
239
|
+
const trimmed = input.trim();
|
|
240
|
+
if (trimmed.startsWith("$")) {
|
|
241
|
+
const hexString = trimmed.slice(1);
|
|
242
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
243
|
+
throw new import_zod2.z.ZodError([
|
|
244
|
+
{
|
|
245
|
+
code: "custom",
|
|
246
|
+
message: `Invalid C64 hex ($) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
247
|
+
path: []
|
|
248
|
+
}
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
const parsed = parseInt(hexString, 16);
|
|
252
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
253
|
+
throw new import_zod2.z.ZodError([
|
|
254
|
+
{
|
|
255
|
+
code: "custom",
|
|
256
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
257
|
+
path: []
|
|
258
|
+
}
|
|
259
|
+
]);
|
|
260
|
+
}
|
|
261
|
+
return parsed;
|
|
262
|
+
}
|
|
263
|
+
if (trimmed.toLowerCase().startsWith("0x")) {
|
|
264
|
+
const hexString = trimmed.slice(2);
|
|
265
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
266
|
+
throw new import_zod2.z.ZodError([
|
|
267
|
+
{
|
|
268
|
+
code: "custom",
|
|
269
|
+
message: `Invalid C hex (0x) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
270
|
+
path: []
|
|
271
|
+
}
|
|
272
|
+
]);
|
|
273
|
+
}
|
|
274
|
+
const parsed = parseInt(hexString, 16);
|
|
275
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
276
|
+
throw new import_zod2.z.ZodError([
|
|
277
|
+
{
|
|
278
|
+
code: "custom",
|
|
279
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
280
|
+
path: []
|
|
281
|
+
}
|
|
282
|
+
]);
|
|
283
|
+
}
|
|
284
|
+
return parsed;
|
|
285
|
+
}
|
|
286
|
+
if (trimmed.startsWith("%")) {
|
|
287
|
+
const binString = trimmed.slice(1);
|
|
288
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
289
|
+
throw new import_zod2.z.ZodError([
|
|
290
|
+
{
|
|
291
|
+
code: "custom",
|
|
292
|
+
message: `Invalid C64 binary (%) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
293
|
+
path: []
|
|
294
|
+
}
|
|
295
|
+
]);
|
|
296
|
+
}
|
|
297
|
+
const parsed = parseInt(binString, 2);
|
|
298
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
299
|
+
throw new import_zod2.z.ZodError([
|
|
300
|
+
{
|
|
301
|
+
code: "custom",
|
|
302
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
303
|
+
path: []
|
|
304
|
+
}
|
|
305
|
+
]);
|
|
306
|
+
}
|
|
307
|
+
return parsed;
|
|
308
|
+
}
|
|
309
|
+
if (trimmed.toLowerCase().startsWith("0b")) {
|
|
310
|
+
const binString = trimmed.slice(2);
|
|
311
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
312
|
+
throw new import_zod2.z.ZodError([
|
|
313
|
+
{
|
|
314
|
+
code: "custom",
|
|
315
|
+
message: `Invalid C binary (0b) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
316
|
+
path: []
|
|
317
|
+
}
|
|
318
|
+
]);
|
|
319
|
+
}
|
|
320
|
+
const parsed = parseInt(binString, 2);
|
|
321
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
322
|
+
throw new import_zod2.z.ZodError([
|
|
323
|
+
{
|
|
324
|
+
code: "custom",
|
|
325
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
326
|
+
path: []
|
|
327
|
+
}
|
|
328
|
+
]);
|
|
329
|
+
}
|
|
330
|
+
return parsed;
|
|
331
|
+
}
|
|
332
|
+
throw new import_zod2.z.ZodError([
|
|
333
|
+
{
|
|
334
|
+
code: "custom",
|
|
335
|
+
message: `Invalid byte format: "${input}". Expected formats: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111). Bare hex/binary not supported to avoid ambiguity.`,
|
|
336
|
+
path: []
|
|
337
|
+
}
|
|
338
|
+
]);
|
|
339
|
+
}
|
|
340
|
+
var byteValueSchema = import_zod2.z.preprocess(parseByte, import_zod2.z.number().int().min(0).max(255)).describe("8-bit byte value: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111)");
|
|
341
|
+
var byteArraySchema = import_zod2.z.array(byteValueSchema).describe('Array of byte values in mixed formats: [255, "$FF", "%11111111", 42]');
|
|
158
342
|
var c64RegisterValueSchema = import_zod2.z.object(
|
|
159
343
|
Object.fromEntries(
|
|
160
344
|
C64_REGISTER_DEFINITIONS.map((register) => [
|
|
@@ -2437,9 +2621,21 @@ var ViceSession = class {
|
|
|
2437
2621
|
async #withAutoResumeForInput(commandName, operation) {
|
|
2438
2622
|
await this.#ensureReady();
|
|
2439
2623
|
this.#syncMonitorRuntimeState();
|
|
2624
|
+
if (this.#executionState === "unknown") {
|
|
2625
|
+
throw new ViceMcpError(
|
|
2626
|
+
"emulator_state_unknown",
|
|
2627
|
+
`${commandName} requires a known execution state (running or stopped)`,
|
|
2628
|
+
"session_state",
|
|
2629
|
+
false,
|
|
2630
|
+
{
|
|
2631
|
+
executionState: this.#executionState,
|
|
2632
|
+
lastStopReason: this.#lastStopReason
|
|
2633
|
+
}
|
|
2634
|
+
);
|
|
2635
|
+
}
|
|
2440
2636
|
const wasRunning = this.#executionState === "running";
|
|
2441
2637
|
const wasPaused = this.#explicitPauseActive;
|
|
2442
|
-
if (
|
|
2638
|
+
if (this.#executionState === "stopped") {
|
|
2443
2639
|
this.#writeProcessLogLine(`[${commandName}] auto-resuming for input operation`);
|
|
2444
2640
|
await this.#client.continueExecution();
|
|
2445
2641
|
await this.waitForState("running", 5e3, INPUT_RUNNING_STABLE_MS);
|
|
@@ -3440,9 +3636,9 @@ var setRegistersTool = createViceTool({
|
|
|
3440
3636
|
});
|
|
3441
3637
|
var readMemoryTool = createViceTool({
|
|
3442
3638
|
id: "memory_read",
|
|
3443
|
-
description: "Reads a memory chunk by start address and length
|
|
3639
|
+
description: "Reads a memory chunk by start address and length. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
|
|
3444
3640
|
inputSchema: import_zod4.z.object({
|
|
3445
|
-
address: address16Schema.describe("Start address
|
|
3641
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3446
3642
|
length: import_zod4.z.number().int().positive().max(65535).describe("Size of the data chunk to read in bytes")
|
|
3447
3643
|
}).refine((input) => input.address + input.length <= 65536, {
|
|
3448
3644
|
message: "address + length must stay within the 64K address space",
|
|
@@ -3464,10 +3660,10 @@ var readMemoryTool = createViceTool({
|
|
|
3464
3660
|
});
|
|
3465
3661
|
var writeMemoryTool = createViceTool({
|
|
3466
3662
|
id: "memory_write",
|
|
3467
|
-
description:
|
|
3663
|
+
description: "Writes raw byte values into C64 memory. Address and byte values support decimal, hex ($FF, 0xFF), and binary (%11111111, 0b11111111) formats. Requires emulator to be stopped.",
|
|
3468
3664
|
inputSchema: import_zod4.z.object({
|
|
3469
|
-
address: address16Schema.describe("Start address
|
|
3470
|
-
data: byteArraySchema.min(1).describe("
|
|
3665
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3666
|
+
data: byteArraySchema.min(1).describe("Bytes to write: decimal (255), hex ($FF, 0xFF), or binary (%11111111, 0b11111111). Mixed formats allowed.")
|
|
3471
3667
|
}).refine((input) => input.address + input.data.length - 1 <= 65535, {
|
|
3472
3668
|
message: "address + data.length must stay within the 16-bit address space",
|
|
3473
3669
|
path: ["data"]
|
|
@@ -3523,10 +3719,10 @@ var listBreakpointsTool = createViceTool({
|
|
|
3523
3719
|
});
|
|
3524
3720
|
var breakpointSetTool = createViceTool({
|
|
3525
3721
|
id: "breakpoint_set",
|
|
3526
|
-
description: "Creates an execution breakpoint or read/write watchpoint.",
|
|
3722
|
+
description: "Creates an execution breakpoint or read/write watchpoint. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
|
|
3527
3723
|
inputSchema: import_zod4.z.object({
|
|
3528
3724
|
kind: breakpointKindSchema,
|
|
3529
|
-
address: address16Schema.describe("Start address
|
|
3725
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3530
3726
|
length: import_zod4.z.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
|
|
3531
3727
|
condition: import_zod4.z.string().optional(),
|
|
3532
3728
|
label: import_zod4.z.string().optional(),
|
package/dist/http.js
CHANGED
|
@@ -129,9 +129,193 @@ import { ZodError } from "zod";
|
|
|
129
129
|
// src/schemas.ts
|
|
130
130
|
import { z as z2 } from "zod";
|
|
131
131
|
var warningSchema = warningItemSchema;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
function parseAddress16(input) {
|
|
133
|
+
if (typeof input === "number") {
|
|
134
|
+
if (!Number.isInteger(input) || input < 0 || input > 65535) {
|
|
135
|
+
throw new z2.ZodError([
|
|
136
|
+
{
|
|
137
|
+
code: "custom",
|
|
138
|
+
message: `Address must be an integer between 0 and 65535 (0xFFFF), got ${input}`,
|
|
139
|
+
path: []
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
return input;
|
|
144
|
+
}
|
|
145
|
+
if (typeof input !== "string") {
|
|
146
|
+
throw new z2.ZodError([
|
|
147
|
+
{
|
|
148
|
+
code: "custom",
|
|
149
|
+
message: `Address must be a number or string, got ${typeof input}`,
|
|
150
|
+
path: []
|
|
151
|
+
}
|
|
152
|
+
]);
|
|
153
|
+
}
|
|
154
|
+
const trimmed = input.trim();
|
|
155
|
+
let hexString;
|
|
156
|
+
let format;
|
|
157
|
+
if (trimmed.startsWith("$")) {
|
|
158
|
+
hexString = trimmed.slice(1);
|
|
159
|
+
format = "C64 hex ($)";
|
|
160
|
+
} else if (trimmed.toLowerCase().startsWith("0x")) {
|
|
161
|
+
hexString = trimmed.slice(2);
|
|
162
|
+
format = "C hex (0x)";
|
|
163
|
+
} else {
|
|
164
|
+
throw new z2.ZodError([
|
|
165
|
+
{
|
|
166
|
+
code: "custom",
|
|
167
|
+
message: `Invalid address format: "${input}". Expected formats: decimal number (53248), hex with $ ($D000), or hex with 0x (0xD000). Bare hex not supported to avoid ambiguity.`,
|
|
168
|
+
path: []
|
|
169
|
+
}
|
|
170
|
+
]);
|
|
171
|
+
}
|
|
172
|
+
if (!/^[0-9A-Fa-f]{1,4}$/.test(hexString)) {
|
|
173
|
+
throw new z2.ZodError([
|
|
174
|
+
{
|
|
175
|
+
code: "custom",
|
|
176
|
+
message: `Invalid ${format} address: "${input}". Hex portion must be 1-4 hex digits (0-9, A-F)`,
|
|
177
|
+
path: []
|
|
178
|
+
}
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
const parsed = parseInt(hexString, 16);
|
|
182
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 65535) {
|
|
183
|
+
throw new z2.ZodError([
|
|
184
|
+
{
|
|
185
|
+
code: "custom",
|
|
186
|
+
message: `Address out of range: "${input}" (${parsed}). Must be 0x0000-0xFFFF (0-65535)`,
|
|
187
|
+
path: []
|
|
188
|
+
}
|
|
189
|
+
]);
|
|
190
|
+
}
|
|
191
|
+
return parsed;
|
|
192
|
+
}
|
|
193
|
+
var address16Schema = z2.preprocess(parseAddress16, z2.number().int().min(0).max(65535)).describe("16-bit C64 address: decimal (53248) or hex string with prefix ($D000, 0xD000)");
|
|
194
|
+
function parseByte(input) {
|
|
195
|
+
if (typeof input === "number") {
|
|
196
|
+
if (!Number.isInteger(input) || input < 0 || input > 255) {
|
|
197
|
+
throw new z2.ZodError([
|
|
198
|
+
{
|
|
199
|
+
code: "custom",
|
|
200
|
+
message: `Byte value must be an integer between 0 and 255 (0xFF), got ${input}`,
|
|
201
|
+
path: []
|
|
202
|
+
}
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
205
|
+
return input;
|
|
206
|
+
}
|
|
207
|
+
if (typeof input !== "string") {
|
|
208
|
+
throw new z2.ZodError([
|
|
209
|
+
{
|
|
210
|
+
code: "custom",
|
|
211
|
+
message: `Byte value must be a number or string, got ${typeof input}`,
|
|
212
|
+
path: []
|
|
213
|
+
}
|
|
214
|
+
]);
|
|
215
|
+
}
|
|
216
|
+
const trimmed = input.trim();
|
|
217
|
+
if (trimmed.startsWith("$")) {
|
|
218
|
+
const hexString = trimmed.slice(1);
|
|
219
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
220
|
+
throw new z2.ZodError([
|
|
221
|
+
{
|
|
222
|
+
code: "custom",
|
|
223
|
+
message: `Invalid C64 hex ($) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
224
|
+
path: []
|
|
225
|
+
}
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
const parsed = parseInt(hexString, 16);
|
|
229
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
230
|
+
throw new z2.ZodError([
|
|
231
|
+
{
|
|
232
|
+
code: "custom",
|
|
233
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
234
|
+
path: []
|
|
235
|
+
}
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
return parsed;
|
|
239
|
+
}
|
|
240
|
+
if (trimmed.toLowerCase().startsWith("0x")) {
|
|
241
|
+
const hexString = trimmed.slice(2);
|
|
242
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
243
|
+
throw new z2.ZodError([
|
|
244
|
+
{
|
|
245
|
+
code: "custom",
|
|
246
|
+
message: `Invalid C hex (0x) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
247
|
+
path: []
|
|
248
|
+
}
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
const parsed = parseInt(hexString, 16);
|
|
252
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
253
|
+
throw new z2.ZodError([
|
|
254
|
+
{
|
|
255
|
+
code: "custom",
|
|
256
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
257
|
+
path: []
|
|
258
|
+
}
|
|
259
|
+
]);
|
|
260
|
+
}
|
|
261
|
+
return parsed;
|
|
262
|
+
}
|
|
263
|
+
if (trimmed.startsWith("%")) {
|
|
264
|
+
const binString = trimmed.slice(1);
|
|
265
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
266
|
+
throw new z2.ZodError([
|
|
267
|
+
{
|
|
268
|
+
code: "custom",
|
|
269
|
+
message: `Invalid C64 binary (%) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
270
|
+
path: []
|
|
271
|
+
}
|
|
272
|
+
]);
|
|
273
|
+
}
|
|
274
|
+
const parsed = parseInt(binString, 2);
|
|
275
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
276
|
+
throw new z2.ZodError([
|
|
277
|
+
{
|
|
278
|
+
code: "custom",
|
|
279
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
280
|
+
path: []
|
|
281
|
+
}
|
|
282
|
+
]);
|
|
283
|
+
}
|
|
284
|
+
return parsed;
|
|
285
|
+
}
|
|
286
|
+
if (trimmed.toLowerCase().startsWith("0b")) {
|
|
287
|
+
const binString = trimmed.slice(2);
|
|
288
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
289
|
+
throw new z2.ZodError([
|
|
290
|
+
{
|
|
291
|
+
code: "custom",
|
|
292
|
+
message: `Invalid C binary (0b) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
293
|
+
path: []
|
|
294
|
+
}
|
|
295
|
+
]);
|
|
296
|
+
}
|
|
297
|
+
const parsed = parseInt(binString, 2);
|
|
298
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
299
|
+
throw new z2.ZodError([
|
|
300
|
+
{
|
|
301
|
+
code: "custom",
|
|
302
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
303
|
+
path: []
|
|
304
|
+
}
|
|
305
|
+
]);
|
|
306
|
+
}
|
|
307
|
+
return parsed;
|
|
308
|
+
}
|
|
309
|
+
throw new z2.ZodError([
|
|
310
|
+
{
|
|
311
|
+
code: "custom",
|
|
312
|
+
message: `Invalid byte format: "${input}". Expected formats: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111). Bare hex/binary not supported to avoid ambiguity.`,
|
|
313
|
+
path: []
|
|
314
|
+
}
|
|
315
|
+
]);
|
|
316
|
+
}
|
|
317
|
+
var byteValueSchema = z2.preprocess(parseByte, z2.number().int().min(0).max(255)).describe("8-bit byte value: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111)");
|
|
318
|
+
var byteArraySchema = z2.array(byteValueSchema).describe('Array of byte values in mixed formats: [255, "$FF", "%11111111", 42]');
|
|
135
319
|
var c64RegisterValueSchema = z2.object(
|
|
136
320
|
Object.fromEntries(
|
|
137
321
|
C64_REGISTER_DEFINITIONS.map((register) => [
|
|
@@ -2414,9 +2598,21 @@ var ViceSession = class {
|
|
|
2414
2598
|
async #withAutoResumeForInput(commandName, operation) {
|
|
2415
2599
|
await this.#ensureReady();
|
|
2416
2600
|
this.#syncMonitorRuntimeState();
|
|
2601
|
+
if (this.#executionState === "unknown") {
|
|
2602
|
+
throw new ViceMcpError(
|
|
2603
|
+
"emulator_state_unknown",
|
|
2604
|
+
`${commandName} requires a known execution state (running or stopped)`,
|
|
2605
|
+
"session_state",
|
|
2606
|
+
false,
|
|
2607
|
+
{
|
|
2608
|
+
executionState: this.#executionState,
|
|
2609
|
+
lastStopReason: this.#lastStopReason
|
|
2610
|
+
}
|
|
2611
|
+
);
|
|
2612
|
+
}
|
|
2417
2613
|
const wasRunning = this.#executionState === "running";
|
|
2418
2614
|
const wasPaused = this.#explicitPauseActive;
|
|
2419
|
-
if (
|
|
2615
|
+
if (this.#executionState === "stopped") {
|
|
2420
2616
|
this.#writeProcessLogLine(`[${commandName}] auto-resuming for input operation`);
|
|
2421
2617
|
await this.#client.continueExecution();
|
|
2422
2618
|
await this.waitForState("running", 5e3, INPUT_RUNNING_STABLE_MS);
|
|
@@ -3417,9 +3613,9 @@ var setRegistersTool = createViceTool({
|
|
|
3417
3613
|
});
|
|
3418
3614
|
var readMemoryTool = createViceTool({
|
|
3419
3615
|
id: "memory_read",
|
|
3420
|
-
description: "Reads a memory chunk by start address and length
|
|
3616
|
+
description: "Reads a memory chunk by start address and length. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
|
|
3421
3617
|
inputSchema: z3.object({
|
|
3422
|
-
address: address16Schema.describe("Start address
|
|
3618
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3423
3619
|
length: z3.number().int().positive().max(65535).describe("Size of the data chunk to read in bytes")
|
|
3424
3620
|
}).refine((input) => input.address + input.length <= 65536, {
|
|
3425
3621
|
message: "address + length must stay within the 64K address space",
|
|
@@ -3441,10 +3637,10 @@ var readMemoryTool = createViceTool({
|
|
|
3441
3637
|
});
|
|
3442
3638
|
var writeMemoryTool = createViceTool({
|
|
3443
3639
|
id: "memory_write",
|
|
3444
|
-
description:
|
|
3640
|
+
description: "Writes raw byte values into C64 memory. Address and byte values support decimal, hex ($FF, 0xFF), and binary (%11111111, 0b11111111) formats. Requires emulator to be stopped.",
|
|
3445
3641
|
inputSchema: z3.object({
|
|
3446
|
-
address: address16Schema.describe("Start address
|
|
3447
|
-
data: byteArraySchema.min(1).describe("
|
|
3642
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3643
|
+
data: byteArraySchema.min(1).describe("Bytes to write: decimal (255), hex ($FF, 0xFF), or binary (%11111111, 0b11111111). Mixed formats allowed.")
|
|
3448
3644
|
}).refine((input) => input.address + input.data.length - 1 <= 65535, {
|
|
3449
3645
|
message: "address + data.length must stay within the 16-bit address space",
|
|
3450
3646
|
path: ["data"]
|
|
@@ -3500,10 +3696,10 @@ var listBreakpointsTool = createViceTool({
|
|
|
3500
3696
|
});
|
|
3501
3697
|
var breakpointSetTool = createViceTool({
|
|
3502
3698
|
id: "breakpoint_set",
|
|
3503
|
-
description: "Creates an execution breakpoint or read/write watchpoint.",
|
|
3699
|
+
description: "Creates an execution breakpoint or read/write watchpoint. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
|
|
3504
3700
|
inputSchema: z3.object({
|
|
3505
3701
|
kind: breakpointKindSchema,
|
|
3506
|
-
address: address16Schema.describe("Start address
|
|
3702
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3507
3703
|
length: z3.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
|
|
3508
3704
|
condition: z3.string().optional(),
|
|
3509
3705
|
label: z3.string().optional(),
|
package/dist/stdio.cjs
CHANGED
|
@@ -149,9 +149,193 @@ var import_zod3 = require("zod");
|
|
|
149
149
|
// src/schemas.ts
|
|
150
150
|
var import_zod2 = require("zod");
|
|
151
151
|
var warningSchema = warningItemSchema;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
function parseAddress16(input) {
|
|
153
|
+
if (typeof input === "number") {
|
|
154
|
+
if (!Number.isInteger(input) || input < 0 || input > 65535) {
|
|
155
|
+
throw new import_zod2.z.ZodError([
|
|
156
|
+
{
|
|
157
|
+
code: "custom",
|
|
158
|
+
message: `Address must be an integer between 0 and 65535 (0xFFFF), got ${input}`,
|
|
159
|
+
path: []
|
|
160
|
+
}
|
|
161
|
+
]);
|
|
162
|
+
}
|
|
163
|
+
return input;
|
|
164
|
+
}
|
|
165
|
+
if (typeof input !== "string") {
|
|
166
|
+
throw new import_zod2.z.ZodError([
|
|
167
|
+
{
|
|
168
|
+
code: "custom",
|
|
169
|
+
message: `Address must be a number or string, got ${typeof input}`,
|
|
170
|
+
path: []
|
|
171
|
+
}
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
const trimmed = input.trim();
|
|
175
|
+
let hexString;
|
|
176
|
+
let format;
|
|
177
|
+
if (trimmed.startsWith("$")) {
|
|
178
|
+
hexString = trimmed.slice(1);
|
|
179
|
+
format = "C64 hex ($)";
|
|
180
|
+
} else if (trimmed.toLowerCase().startsWith("0x")) {
|
|
181
|
+
hexString = trimmed.slice(2);
|
|
182
|
+
format = "C hex (0x)";
|
|
183
|
+
} else {
|
|
184
|
+
throw new import_zod2.z.ZodError([
|
|
185
|
+
{
|
|
186
|
+
code: "custom",
|
|
187
|
+
message: `Invalid address format: "${input}". Expected formats: decimal number (53248), hex with $ ($D000), or hex with 0x (0xD000). Bare hex not supported to avoid ambiguity.`,
|
|
188
|
+
path: []
|
|
189
|
+
}
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
192
|
+
if (!/^[0-9A-Fa-f]{1,4}$/.test(hexString)) {
|
|
193
|
+
throw new import_zod2.z.ZodError([
|
|
194
|
+
{
|
|
195
|
+
code: "custom",
|
|
196
|
+
message: `Invalid ${format} address: "${input}". Hex portion must be 1-4 hex digits (0-9, A-F)`,
|
|
197
|
+
path: []
|
|
198
|
+
}
|
|
199
|
+
]);
|
|
200
|
+
}
|
|
201
|
+
const parsed = parseInt(hexString, 16);
|
|
202
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 65535) {
|
|
203
|
+
throw new import_zod2.z.ZodError([
|
|
204
|
+
{
|
|
205
|
+
code: "custom",
|
|
206
|
+
message: `Address out of range: "${input}" (${parsed}). Must be 0x0000-0xFFFF (0-65535)`,
|
|
207
|
+
path: []
|
|
208
|
+
}
|
|
209
|
+
]);
|
|
210
|
+
}
|
|
211
|
+
return parsed;
|
|
212
|
+
}
|
|
213
|
+
var address16Schema = import_zod2.z.preprocess(parseAddress16, import_zod2.z.number().int().min(0).max(65535)).describe("16-bit C64 address: decimal (53248) or hex string with prefix ($D000, 0xD000)");
|
|
214
|
+
function parseByte(input) {
|
|
215
|
+
if (typeof input === "number") {
|
|
216
|
+
if (!Number.isInteger(input) || input < 0 || input > 255) {
|
|
217
|
+
throw new import_zod2.z.ZodError([
|
|
218
|
+
{
|
|
219
|
+
code: "custom",
|
|
220
|
+
message: `Byte value must be an integer between 0 and 255 (0xFF), got ${input}`,
|
|
221
|
+
path: []
|
|
222
|
+
}
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
return input;
|
|
226
|
+
}
|
|
227
|
+
if (typeof input !== "string") {
|
|
228
|
+
throw new import_zod2.z.ZodError([
|
|
229
|
+
{
|
|
230
|
+
code: "custom",
|
|
231
|
+
message: `Byte value must be a number or string, got ${typeof input}`,
|
|
232
|
+
path: []
|
|
233
|
+
}
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
236
|
+
const trimmed = input.trim();
|
|
237
|
+
if (trimmed.startsWith("$")) {
|
|
238
|
+
const hexString = trimmed.slice(1);
|
|
239
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
240
|
+
throw new import_zod2.z.ZodError([
|
|
241
|
+
{
|
|
242
|
+
code: "custom",
|
|
243
|
+
message: `Invalid C64 hex ($) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
244
|
+
path: []
|
|
245
|
+
}
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
const parsed = parseInt(hexString, 16);
|
|
249
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
250
|
+
throw new import_zod2.z.ZodError([
|
|
251
|
+
{
|
|
252
|
+
code: "custom",
|
|
253
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
254
|
+
path: []
|
|
255
|
+
}
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
return parsed;
|
|
259
|
+
}
|
|
260
|
+
if (trimmed.toLowerCase().startsWith("0x")) {
|
|
261
|
+
const hexString = trimmed.slice(2);
|
|
262
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
263
|
+
throw new import_zod2.z.ZodError([
|
|
264
|
+
{
|
|
265
|
+
code: "custom",
|
|
266
|
+
message: `Invalid C hex (0x) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
267
|
+
path: []
|
|
268
|
+
}
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
271
|
+
const parsed = parseInt(hexString, 16);
|
|
272
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
273
|
+
throw new import_zod2.z.ZodError([
|
|
274
|
+
{
|
|
275
|
+
code: "custom",
|
|
276
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
277
|
+
path: []
|
|
278
|
+
}
|
|
279
|
+
]);
|
|
280
|
+
}
|
|
281
|
+
return parsed;
|
|
282
|
+
}
|
|
283
|
+
if (trimmed.startsWith("%")) {
|
|
284
|
+
const binString = trimmed.slice(1);
|
|
285
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
286
|
+
throw new import_zod2.z.ZodError([
|
|
287
|
+
{
|
|
288
|
+
code: "custom",
|
|
289
|
+
message: `Invalid C64 binary (%) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
290
|
+
path: []
|
|
291
|
+
}
|
|
292
|
+
]);
|
|
293
|
+
}
|
|
294
|
+
const parsed = parseInt(binString, 2);
|
|
295
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
296
|
+
throw new import_zod2.z.ZodError([
|
|
297
|
+
{
|
|
298
|
+
code: "custom",
|
|
299
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
300
|
+
path: []
|
|
301
|
+
}
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
return parsed;
|
|
305
|
+
}
|
|
306
|
+
if (trimmed.toLowerCase().startsWith("0b")) {
|
|
307
|
+
const binString = trimmed.slice(2);
|
|
308
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
309
|
+
throw new import_zod2.z.ZodError([
|
|
310
|
+
{
|
|
311
|
+
code: "custom",
|
|
312
|
+
message: `Invalid C binary (0b) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
313
|
+
path: []
|
|
314
|
+
}
|
|
315
|
+
]);
|
|
316
|
+
}
|
|
317
|
+
const parsed = parseInt(binString, 2);
|
|
318
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
319
|
+
throw new import_zod2.z.ZodError([
|
|
320
|
+
{
|
|
321
|
+
code: "custom",
|
|
322
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
323
|
+
path: []
|
|
324
|
+
}
|
|
325
|
+
]);
|
|
326
|
+
}
|
|
327
|
+
return parsed;
|
|
328
|
+
}
|
|
329
|
+
throw new import_zod2.z.ZodError([
|
|
330
|
+
{
|
|
331
|
+
code: "custom",
|
|
332
|
+
message: `Invalid byte format: "${input}". Expected formats: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111). Bare hex/binary not supported to avoid ambiguity.`,
|
|
333
|
+
path: []
|
|
334
|
+
}
|
|
335
|
+
]);
|
|
336
|
+
}
|
|
337
|
+
var byteValueSchema = import_zod2.z.preprocess(parseByte, import_zod2.z.number().int().min(0).max(255)).describe("8-bit byte value: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111)");
|
|
338
|
+
var byteArraySchema = import_zod2.z.array(byteValueSchema).describe('Array of byte values in mixed formats: [255, "$FF", "%11111111", 42]');
|
|
155
339
|
var c64RegisterValueSchema = import_zod2.z.object(
|
|
156
340
|
Object.fromEntries(
|
|
157
341
|
C64_REGISTER_DEFINITIONS.map((register) => [
|
|
@@ -2434,9 +2618,21 @@ var ViceSession = class {
|
|
|
2434
2618
|
async #withAutoResumeForInput(commandName, operation) {
|
|
2435
2619
|
await this.#ensureReady();
|
|
2436
2620
|
this.#syncMonitorRuntimeState();
|
|
2621
|
+
if (this.#executionState === "unknown") {
|
|
2622
|
+
throw new ViceMcpError(
|
|
2623
|
+
"emulator_state_unknown",
|
|
2624
|
+
`${commandName} requires a known execution state (running or stopped)`,
|
|
2625
|
+
"session_state",
|
|
2626
|
+
false,
|
|
2627
|
+
{
|
|
2628
|
+
executionState: this.#executionState,
|
|
2629
|
+
lastStopReason: this.#lastStopReason
|
|
2630
|
+
}
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2437
2633
|
const wasRunning = this.#executionState === "running";
|
|
2438
2634
|
const wasPaused = this.#explicitPauseActive;
|
|
2439
|
-
if (
|
|
2635
|
+
if (this.#executionState === "stopped") {
|
|
2440
2636
|
this.#writeProcessLogLine(`[${commandName}] auto-resuming for input operation`);
|
|
2441
2637
|
await this.#client.continueExecution();
|
|
2442
2638
|
await this.waitForState("running", 5e3, INPUT_RUNNING_STABLE_MS);
|
|
@@ -3437,9 +3633,9 @@ var setRegistersTool = createViceTool({
|
|
|
3437
3633
|
});
|
|
3438
3634
|
var readMemoryTool = createViceTool({
|
|
3439
3635
|
id: "memory_read",
|
|
3440
|
-
description: "Reads a memory chunk by start address and length
|
|
3636
|
+
description: "Reads a memory chunk by start address and length. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
|
|
3441
3637
|
inputSchema: import_zod4.z.object({
|
|
3442
|
-
address: address16Schema.describe("Start address
|
|
3638
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3443
3639
|
length: import_zod4.z.number().int().positive().max(65535).describe("Size of the data chunk to read in bytes")
|
|
3444
3640
|
}).refine((input) => input.address + input.length <= 65536, {
|
|
3445
3641
|
message: "address + length must stay within the 64K address space",
|
|
@@ -3461,10 +3657,10 @@ var readMemoryTool = createViceTool({
|
|
|
3461
3657
|
});
|
|
3462
3658
|
var writeMemoryTool = createViceTool({
|
|
3463
3659
|
id: "memory_write",
|
|
3464
|
-
description:
|
|
3660
|
+
description: "Writes raw byte values into C64 memory. Address and byte values support decimal, hex ($FF, 0xFF), and binary (%11111111, 0b11111111) formats. Requires emulator to be stopped.",
|
|
3465
3661
|
inputSchema: import_zod4.z.object({
|
|
3466
|
-
address: address16Schema.describe("Start address
|
|
3467
|
-
data: byteArraySchema.min(1).describe("
|
|
3662
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3663
|
+
data: byteArraySchema.min(1).describe("Bytes to write: decimal (255), hex ($FF, 0xFF), or binary (%11111111, 0b11111111). Mixed formats allowed.")
|
|
3468
3664
|
}).refine((input) => input.address + input.data.length - 1 <= 65535, {
|
|
3469
3665
|
message: "address + data.length must stay within the 16-bit address space",
|
|
3470
3666
|
path: ["data"]
|
|
@@ -3520,10 +3716,10 @@ var listBreakpointsTool = createViceTool({
|
|
|
3520
3716
|
});
|
|
3521
3717
|
var breakpointSetTool = createViceTool({
|
|
3522
3718
|
id: "breakpoint_set",
|
|
3523
|
-
description: "Creates an execution breakpoint or read/write watchpoint.",
|
|
3719
|
+
description: "Creates an execution breakpoint or read/write watchpoint. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
|
|
3524
3720
|
inputSchema: import_zod4.z.object({
|
|
3525
3721
|
kind: breakpointKindSchema,
|
|
3526
|
-
address: address16Schema.describe("Start address
|
|
3722
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3527
3723
|
length: import_zod4.z.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
|
|
3528
3724
|
condition: import_zod4.z.string().optional(),
|
|
3529
3725
|
label: import_zod4.z.string().optional(),
|
package/dist/stdio.js
CHANGED
|
@@ -126,9 +126,193 @@ import { ZodError } from "zod";
|
|
|
126
126
|
// src/schemas.ts
|
|
127
127
|
import { z as z2 } from "zod";
|
|
128
128
|
var warningSchema = warningItemSchema;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
function parseAddress16(input) {
|
|
130
|
+
if (typeof input === "number") {
|
|
131
|
+
if (!Number.isInteger(input) || input < 0 || input > 65535) {
|
|
132
|
+
throw new z2.ZodError([
|
|
133
|
+
{
|
|
134
|
+
code: "custom",
|
|
135
|
+
message: `Address must be an integer between 0 and 65535 (0xFFFF), got ${input}`,
|
|
136
|
+
path: []
|
|
137
|
+
}
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
return input;
|
|
141
|
+
}
|
|
142
|
+
if (typeof input !== "string") {
|
|
143
|
+
throw new z2.ZodError([
|
|
144
|
+
{
|
|
145
|
+
code: "custom",
|
|
146
|
+
message: `Address must be a number or string, got ${typeof input}`,
|
|
147
|
+
path: []
|
|
148
|
+
}
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
const trimmed = input.trim();
|
|
152
|
+
let hexString;
|
|
153
|
+
let format;
|
|
154
|
+
if (trimmed.startsWith("$")) {
|
|
155
|
+
hexString = trimmed.slice(1);
|
|
156
|
+
format = "C64 hex ($)";
|
|
157
|
+
} else if (trimmed.toLowerCase().startsWith("0x")) {
|
|
158
|
+
hexString = trimmed.slice(2);
|
|
159
|
+
format = "C hex (0x)";
|
|
160
|
+
} else {
|
|
161
|
+
throw new z2.ZodError([
|
|
162
|
+
{
|
|
163
|
+
code: "custom",
|
|
164
|
+
message: `Invalid address format: "${input}". Expected formats: decimal number (53248), hex with $ ($D000), or hex with 0x (0xD000). Bare hex not supported to avoid ambiguity.`,
|
|
165
|
+
path: []
|
|
166
|
+
}
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
if (!/^[0-9A-Fa-f]{1,4}$/.test(hexString)) {
|
|
170
|
+
throw new z2.ZodError([
|
|
171
|
+
{
|
|
172
|
+
code: "custom",
|
|
173
|
+
message: `Invalid ${format} address: "${input}". Hex portion must be 1-4 hex digits (0-9, A-F)`,
|
|
174
|
+
path: []
|
|
175
|
+
}
|
|
176
|
+
]);
|
|
177
|
+
}
|
|
178
|
+
const parsed = parseInt(hexString, 16);
|
|
179
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 65535) {
|
|
180
|
+
throw new z2.ZodError([
|
|
181
|
+
{
|
|
182
|
+
code: "custom",
|
|
183
|
+
message: `Address out of range: "${input}" (${parsed}). Must be 0x0000-0xFFFF (0-65535)`,
|
|
184
|
+
path: []
|
|
185
|
+
}
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
return parsed;
|
|
189
|
+
}
|
|
190
|
+
var address16Schema = z2.preprocess(parseAddress16, z2.number().int().min(0).max(65535)).describe("16-bit C64 address: decimal (53248) or hex string with prefix ($D000, 0xD000)");
|
|
191
|
+
function parseByte(input) {
|
|
192
|
+
if (typeof input === "number") {
|
|
193
|
+
if (!Number.isInteger(input) || input < 0 || input > 255) {
|
|
194
|
+
throw new z2.ZodError([
|
|
195
|
+
{
|
|
196
|
+
code: "custom",
|
|
197
|
+
message: `Byte value must be an integer between 0 and 255 (0xFF), got ${input}`,
|
|
198
|
+
path: []
|
|
199
|
+
}
|
|
200
|
+
]);
|
|
201
|
+
}
|
|
202
|
+
return input;
|
|
203
|
+
}
|
|
204
|
+
if (typeof input !== "string") {
|
|
205
|
+
throw new z2.ZodError([
|
|
206
|
+
{
|
|
207
|
+
code: "custom",
|
|
208
|
+
message: `Byte value must be a number or string, got ${typeof input}`,
|
|
209
|
+
path: []
|
|
210
|
+
}
|
|
211
|
+
]);
|
|
212
|
+
}
|
|
213
|
+
const trimmed = input.trim();
|
|
214
|
+
if (trimmed.startsWith("$")) {
|
|
215
|
+
const hexString = trimmed.slice(1);
|
|
216
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
217
|
+
throw new z2.ZodError([
|
|
218
|
+
{
|
|
219
|
+
code: "custom",
|
|
220
|
+
message: `Invalid C64 hex ($) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
221
|
+
path: []
|
|
222
|
+
}
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
const parsed = parseInt(hexString, 16);
|
|
226
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
227
|
+
throw new z2.ZodError([
|
|
228
|
+
{
|
|
229
|
+
code: "custom",
|
|
230
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
231
|
+
path: []
|
|
232
|
+
}
|
|
233
|
+
]);
|
|
234
|
+
}
|
|
235
|
+
return parsed;
|
|
236
|
+
}
|
|
237
|
+
if (trimmed.toLowerCase().startsWith("0x")) {
|
|
238
|
+
const hexString = trimmed.slice(2);
|
|
239
|
+
if (!/^[0-9A-Fa-f]{1,2}$/.test(hexString)) {
|
|
240
|
+
throw new z2.ZodError([
|
|
241
|
+
{
|
|
242
|
+
code: "custom",
|
|
243
|
+
message: `Invalid C hex (0x) byte value: "${input}". Hex portion must be 1-2 hex digits (0-9, A-F)`,
|
|
244
|
+
path: []
|
|
245
|
+
}
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
const parsed = parseInt(hexString, 16);
|
|
249
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
250
|
+
throw new z2.ZodError([
|
|
251
|
+
{
|
|
252
|
+
code: "custom",
|
|
253
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0x00-0xFF (0-255)`,
|
|
254
|
+
path: []
|
|
255
|
+
}
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
return parsed;
|
|
259
|
+
}
|
|
260
|
+
if (trimmed.startsWith("%")) {
|
|
261
|
+
const binString = trimmed.slice(1);
|
|
262
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
263
|
+
throw new z2.ZodError([
|
|
264
|
+
{
|
|
265
|
+
code: "custom",
|
|
266
|
+
message: `Invalid C64 binary (%) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
267
|
+
path: []
|
|
268
|
+
}
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
271
|
+
const parsed = parseInt(binString, 2);
|
|
272
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
273
|
+
throw new z2.ZodError([
|
|
274
|
+
{
|
|
275
|
+
code: "custom",
|
|
276
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
277
|
+
path: []
|
|
278
|
+
}
|
|
279
|
+
]);
|
|
280
|
+
}
|
|
281
|
+
return parsed;
|
|
282
|
+
}
|
|
283
|
+
if (trimmed.toLowerCase().startsWith("0b")) {
|
|
284
|
+
const binString = trimmed.slice(2);
|
|
285
|
+
if (!/^[01]{1,8}$/.test(binString)) {
|
|
286
|
+
throw new z2.ZodError([
|
|
287
|
+
{
|
|
288
|
+
code: "custom",
|
|
289
|
+
message: `Invalid C binary (0b) byte value: "${input}". Binary portion must be 1-8 binary digits (0-1)`,
|
|
290
|
+
path: []
|
|
291
|
+
}
|
|
292
|
+
]);
|
|
293
|
+
}
|
|
294
|
+
const parsed = parseInt(binString, 2);
|
|
295
|
+
if (isNaN(parsed) || parsed < 0 || parsed > 255) {
|
|
296
|
+
throw new z2.ZodError([
|
|
297
|
+
{
|
|
298
|
+
code: "custom",
|
|
299
|
+
message: `Byte value out of range: "${input}" (${parsed}). Must be 0b00000000-0b11111111 (0-255)`,
|
|
300
|
+
path: []
|
|
301
|
+
}
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
return parsed;
|
|
305
|
+
}
|
|
306
|
+
throw new z2.ZodError([
|
|
307
|
+
{
|
|
308
|
+
code: "custom",
|
|
309
|
+
message: `Invalid byte format: "${input}". Expected formats: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111). Bare hex/binary not supported to avoid ambiguity.`,
|
|
310
|
+
path: []
|
|
311
|
+
}
|
|
312
|
+
]);
|
|
313
|
+
}
|
|
314
|
+
var byteValueSchema = z2.preprocess(parseByte, z2.number().int().min(0).max(255)).describe("8-bit byte value: decimal (255), hex with prefix ($FF, 0xFF), or binary with prefix (%11111111, 0b11111111)");
|
|
315
|
+
var byteArraySchema = z2.array(byteValueSchema).describe('Array of byte values in mixed formats: [255, "$FF", "%11111111", 42]');
|
|
132
316
|
var c64RegisterValueSchema = z2.object(
|
|
133
317
|
Object.fromEntries(
|
|
134
318
|
C64_REGISTER_DEFINITIONS.map((register) => [
|
|
@@ -2411,9 +2595,21 @@ var ViceSession = class {
|
|
|
2411
2595
|
async #withAutoResumeForInput(commandName, operation) {
|
|
2412
2596
|
await this.#ensureReady();
|
|
2413
2597
|
this.#syncMonitorRuntimeState();
|
|
2598
|
+
if (this.#executionState === "unknown") {
|
|
2599
|
+
throw new ViceMcpError(
|
|
2600
|
+
"emulator_state_unknown",
|
|
2601
|
+
`${commandName} requires a known execution state (running or stopped)`,
|
|
2602
|
+
"session_state",
|
|
2603
|
+
false,
|
|
2604
|
+
{
|
|
2605
|
+
executionState: this.#executionState,
|
|
2606
|
+
lastStopReason: this.#lastStopReason
|
|
2607
|
+
}
|
|
2608
|
+
);
|
|
2609
|
+
}
|
|
2414
2610
|
const wasRunning = this.#executionState === "running";
|
|
2415
2611
|
const wasPaused = this.#explicitPauseActive;
|
|
2416
|
-
if (
|
|
2612
|
+
if (this.#executionState === "stopped") {
|
|
2417
2613
|
this.#writeProcessLogLine(`[${commandName}] auto-resuming for input operation`);
|
|
2418
2614
|
await this.#client.continueExecution();
|
|
2419
2615
|
await this.waitForState("running", 5e3, INPUT_RUNNING_STABLE_MS);
|
|
@@ -3414,9 +3610,9 @@ var setRegistersTool = createViceTool({
|
|
|
3414
3610
|
});
|
|
3415
3611
|
var readMemoryTool = createViceTool({
|
|
3416
3612
|
id: "memory_read",
|
|
3417
|
-
description: "Reads a memory chunk by start address and length
|
|
3613
|
+
description: "Reads a memory chunk by start address and length. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
|
|
3418
3614
|
inputSchema: z3.object({
|
|
3419
|
-
address: address16Schema.describe("Start address
|
|
3615
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3420
3616
|
length: z3.number().int().positive().max(65535).describe("Size of the data chunk to read in bytes")
|
|
3421
3617
|
}).refine((input) => input.address + input.length <= 65536, {
|
|
3422
3618
|
message: "address + length must stay within the 64K address space",
|
|
@@ -3438,10 +3634,10 @@ var readMemoryTool = createViceTool({
|
|
|
3438
3634
|
});
|
|
3439
3635
|
var writeMemoryTool = createViceTool({
|
|
3440
3636
|
id: "memory_write",
|
|
3441
|
-
description:
|
|
3637
|
+
description: "Writes raw byte values into C64 memory. Address and byte values support decimal, hex ($FF, 0xFF), and binary (%11111111, 0b11111111) formats. Requires emulator to be stopped.",
|
|
3442
3638
|
inputSchema: z3.object({
|
|
3443
|
-
address: address16Schema.describe("Start address
|
|
3444
|
-
data: byteArraySchema.min(1).describe("
|
|
3639
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3640
|
+
data: byteArraySchema.min(1).describe("Bytes to write: decimal (255), hex ($FF, 0xFF), or binary (%11111111, 0b11111111). Mixed formats allowed.")
|
|
3445
3641
|
}).refine((input) => input.address + input.data.length - 1 <= 65535, {
|
|
3446
3642
|
message: "address + data.length must stay within the 16-bit address space",
|
|
3447
3643
|
path: ["data"]
|
|
@@ -3497,10 +3693,10 @@ var listBreakpointsTool = createViceTool({
|
|
|
3497
3693
|
});
|
|
3498
3694
|
var breakpointSetTool = createViceTool({
|
|
3499
3695
|
id: "breakpoint_set",
|
|
3500
|
-
description: "Creates an execution breakpoint or read/write watchpoint.",
|
|
3696
|
+
description: "Creates an execution breakpoint or read/write watchpoint. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
|
|
3501
3697
|
inputSchema: z3.object({
|
|
3502
3698
|
kind: breakpointKindSchema,
|
|
3503
|
-
address: address16Schema.describe("Start address
|
|
3699
|
+
address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
|
|
3504
3700
|
length: z3.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
|
|
3505
3701
|
condition: z3.string().optional(),
|
|
3506
3702
|
label: z3.string().optional(),
|