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 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 c64debug -- npx -y c64-debug-mcp
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
- "c64debug": {
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
- var address16Schema = import_zod2.z.number().int().min(0).max(65535);
156
- var byteValueSchema = import_zod2.z.number().int().min(0).max(255).describe("Single raw byte value");
157
- var byteArraySchema = import_zod2.z.array(byteValueSchema).describe("Raw bytes as a JSON array");
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 (!wasRunning) {
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 and returns raw bytes as a JSON array.",
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 in the 16-bit C64 address space"),
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: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
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 in the 16-bit C64 address space"),
3470
- data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
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 of the breakpoint range"),
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
- var address16Schema = z2.number().int().min(0).max(65535);
133
- var byteValueSchema = z2.number().int().min(0).max(255).describe("Single raw byte value");
134
- var byteArraySchema = z2.array(byteValueSchema).describe("Raw bytes as a JSON array");
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 (!wasRunning) {
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 and returns raw bytes as a JSON array.",
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 in the 16-bit C64 address space"),
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: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
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 in the 16-bit C64 address space"),
3447
- data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
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 of the breakpoint range"),
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
- var address16Schema = import_zod2.z.number().int().min(0).max(65535);
153
- var byteValueSchema = import_zod2.z.number().int().min(0).max(255).describe("Single raw byte value");
154
- var byteArraySchema = import_zod2.z.array(byteValueSchema).describe("Raw bytes as a JSON array");
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 (!wasRunning) {
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 and returns raw bytes as a JSON array.",
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 in the 16-bit C64 address space"),
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: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
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 in the 16-bit C64 address space"),
3467
- data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
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 of the breakpoint range"),
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
- var address16Schema = z2.number().int().min(0).max(65535);
130
- var byteValueSchema = z2.number().int().min(0).max(255).describe("Single raw byte value");
131
- var byteArraySchema = z2.array(byteValueSchema).describe("Raw bytes as a JSON array");
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 (!wasRunning) {
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 and returns raw bytes as a JSON array.",
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 in the 16-bit C64 address space"),
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: 'Writes raw byte values into the active C64 memory space. Requires emulator to be stopped - call execute(action="pause") first if running.',
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 in the 16-bit C64 address space"),
3444
- data: byteArraySchema.min(1).describe("Raw bytes to write into memory")
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 of the breakpoint range"),
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(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c64-debug-mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Model Context Protocol server for C64 debugging via VICE emulator",
5
5
  "type": "module",
6
6
  "keywords": [