setWatchpoint
Set memory watchpoints in C64 emulation to debug program execution by halting when specific memory addresses are read or written.
Instructions
Set a memory watchpoint to stop when memory is read or written.
Watchpoints are powerful for debugging:
"Why is this value changing?" → Use store watchpoint
"What's reading this address?" → Use load watchpoint
"Track all access to this region" → Use both
Range can be single address or address range (e.g., $D800-$DBFF for color RAM).
Related tools: deleteBreakpoint, listWatchpoints, continue
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| startAddress | Yes | Start address of watched range (0x0000-0xFFFF) | |
| endAddress | No | End address of watched range (default: same as start for single address) | |
| type | Yes | Watch type: 'load' (read), 'store' (write), or 'both' | |
| enabled | No | Whether watchpoint is active (default: true) | |
| temporary | No | Auto-delete after hit (default: false) |
Implementation Reference
- src/index.ts:716-748 (handler)MCP server tool handler for setWatchpoint: validates args, calls ViceClient.setWatchpoint, formats success/error response with metadataasync (args) => { try { const endAddr = args.endAddress ?? args.startAddress; const id = await client.setWatchpoint(args.startAddress, endAddr, args.type, { enabled: args.enabled ?? true, temporary: args.temporary ?? false, }); const isSingleAddress = args.startAddress === endAddr; return formatResponse({ success: true, watchpointId: id, startAddress: { value: args.startAddress, hex: `$${args.startAddress.toString(16).padStart(4, "0")}`, }, endAddress: { value: endAddr, hex: `$${endAddr.toString(16).padStart(4, "0")}`, }, type: args.type, enabled: args.enabled ?? true, temporary: args.temporary ?? false, message: isSingleAddress ? `Watchpoint ${id} set at $${args.startAddress.toString(16).padStart(4, "0")} (${args.type})` : `Watchpoint ${id} set for $${args.startAddress.toString(16).padStart(4, "0")}-$${endAddr.toString(16).padStart(4, "0")} (${args.type})`, hint: "Use continue() to run until watchpoint is triggered", }); } catch (error) { return formatError(error as ViceError); } }
- src/index.ts:703-714 (schema)Zod input schema defining parameters for setWatchpoint tool: startAddress, endAddress (opt), type (load/store/both), enabled (opt), temporary (opt)inputSchema: z.object({ startAddress: z.number().min(0).max(0xffff).describe("Start address of watched range (0x0000-0xFFFF)"), endAddress: z .number() .min(0) .max(0xffff) .optional() .describe("End address of watched range (default: same as start for single address)"), type: z.enum(["load", "store", "both"]).describe("Watch type: 'load' (read), 'store' (write), or 'both'"), enabled: z.boolean().optional().describe("Whether watchpoint is active (default: true)"), temporary: z.boolean().optional().describe("Auto-delete after hit (default: false)"), }),
- src/index.ts:690-749 (registration)MCP server registration of the setWatchpoint tool including description and input schemaserver.registerTool( "setWatchpoint", { description: `Set a memory watchpoint to stop when memory is read or written. Watchpoints are powerful for debugging: - "Why is this value changing?" → Use store watchpoint - "What's reading this address?" → Use load watchpoint - "Track all access to this region" → Use both Range can be single address or address range (e.g., $D800-$DBFF for color RAM). Related tools: deleteBreakpoint, listWatchpoints, continue`, inputSchema: z.object({ startAddress: z.number().min(0).max(0xffff).describe("Start address of watched range (0x0000-0xFFFF)"), endAddress: z .number() .min(0) .max(0xffff) .optional() .describe("End address of watched range (default: same as start for single address)"), type: z.enum(["load", "store", "both"]).describe("Watch type: 'load' (read), 'store' (write), or 'both'"), enabled: z.boolean().optional().describe("Whether watchpoint is active (default: true)"), temporary: z.boolean().optional().describe("Auto-delete after hit (default: false)"), }), }, async (args) => { try { const endAddr = args.endAddress ?? args.startAddress; const id = await client.setWatchpoint(args.startAddress, endAddr, args.type, { enabled: args.enabled ?? true, temporary: args.temporary ?? false, }); const isSingleAddress = args.startAddress === endAddr; return formatResponse({ success: true, watchpointId: id, startAddress: { value: args.startAddress, hex: `$${args.startAddress.toString(16).padStart(4, "0")}`, }, endAddress: { value: endAddr, hex: `$${endAddr.toString(16).padStart(4, "0")}`, }, type: args.type, enabled: args.enabled ?? true, temporary: args.temporary ?? false, message: isSingleAddress ? `Watchpoint ${id} set at $${args.startAddress.toString(16).padStart(4, "0")} (${args.type})` : `Watchpoint ${id} set for $${args.startAddress.toString(16).padStart(4, "0")}-$${endAddr.toString(16).padStart(4, "0")} (${args.type})`, hint: "Use continue() to run until watchpoint is triggered", }); } catch (error) { return formatError(error as ViceError); } } );
- src/protocol/client.ts:545-616 (handler)Core ViceClient.setWatchpoint method: validates addresses, builds CheckpointSet command packet for VICE binary monitor protocol, sends via sendCommand, parses ID, tracks locallyasync setWatchpoint( startAddress: number, endAddress: number, type: "load" | "store" | "both", options: { enabled?: boolean; stop?: boolean; temporary?: boolean; } = {} ): Promise<number> { const { enabled = true, stop = true, temporary = false } = options; if (startAddress < 0 || startAddress > 0xffff) { throw this.makeError( "INVALID_ADDRESS", `Start address 0x${startAddress.toString(16)} is outside C64 memory range`, "C64 addresses are 16-bit (0x0000-0xFFFF)" ); } if (endAddress < 0 || endAddress > 0xffff) { throw this.makeError( "INVALID_ADDRESS", `End address 0x${endAddress.toString(16)} is outside C64 memory range`, "C64 addresses are 16-bit (0x0000-0xFFFF)" ); } if (startAddress > endAddress) { throw this.makeError( "INVALID_RANGE", `Start address (0x${startAddress.toString(16)}) is greater than end address (0x${endAddress.toString(16)})`, "Swap the addresses or check your range" ); } // Determine operation type let op: number; let checkpointType: CheckpointType; if (type === "load") { op = CheckpointOp.Load; checkpointType = "load"; } else if (type === "store") { op = CheckpointOp.Store; checkpointType = "store"; } else { op = CheckpointOp.Load | CheckpointOp.Store; checkpointType = "load"; // Will track as load for simplicity } // Build request: start(2) + end(2) + stop(1) + enabled(1) + op(1) + temp(1) const body = Buffer.alloc(8); body.writeUInt16LE(startAddress, 0); body.writeUInt16LE(endAddress, 2); body[4] = stop ? 1 : 0; body[5] = enabled ? 1 : 0; body[6] = op; body[7] = temporary ? 1 : 0; const response = await this.sendCommand(Command.CheckpointSet, body); const id = response.body.readUInt32LE(0); // Track locally this.checkpoints.set(id, { id, startAddress, endAddress, enabled, temporary, type: checkpointType, }); return id; }