writeMemory
Modify Commodore 64 memory by writing bytes to specific addresses for debugging, patching code, or altering screen data in the VICE emulator.
Instructions
Write bytes to the C64's memory.
Directly modifies memory at the specified address. Changes take effect immediately.
Common uses:
Poke values for testing
Patch code at runtime
Modify screen/color RAM directly
Change VIC/SID registers
Be careful writing to ROM areas ($A000-$BFFF, $E000-$FFFF) - you may need to bank out ROM first.
Related tools: readMemory, fillMemory
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | Start address (0x0000-0xFFFF) | |
| bytes | Yes | Array of bytes to write (0-255 each) |
Implementation Reference
- src/index.ts:292-337 (registration)MCP registration of 'writeMemory' tool, including input schema (address and bytes array), description, and thin wrapper handler that validates args and delegates to ViceClient.writeMemory, returning formatted success response.server.registerTool( "writeMemory", { description: `Write bytes to the C64's memory. Directly modifies memory at the specified address. Changes take effect immediately. Common uses: - Poke values for testing - Patch code at runtime - Modify screen/color RAM directly - Change VIC/SID registers Be careful writing to ROM areas ($A000-$BFFF, $E000-$FFFF) - you may need to bank out ROM first. Related tools: readMemory, fillMemory`, inputSchema: z.object({ address: z .number() .min(0) .max(0xffff) .describe("Start address (0x0000-0xFFFF)"), bytes: z .array(z.number().min(0).max(255)) .min(1) .describe("Array of bytes to write (0-255 each)"), }), }, async (args) => { try { await client.writeMemory(args.address, args.bytes); return formatResponse({ success: true, address: { value: args.address, hex: `$${args.address.toString(16).padStart(4, "0")}`, }, bytesWritten: args.bytes.length, message: `Wrote ${args.bytes.length} byte(s) to $${args.address.toString(16).padStart(4, "0")}`, }); } catch (error) { return formatError(error as ViceError); } } );
- src/protocol/client.ts:428-474 (handler)Core handler logic in ViceClient class: validates address and data length, ensures VICE is stopped, constructs MemorySet command packet per VICE binary monitor protocol, and sends it via sendCommand.async writeMemory( address: number, data: Buffer | number[], memspace: MemorySpace = MemorySpace.MainCPU ): Promise<void> { const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data); if (address < 0 || address > 0xffff) { throw this.makeError( "INVALID_ADDRESS", `Address 0x${address.toString(16)} is outside C64 memory range`, "C64 addresses are 16-bit (0x0000-0xFFFF)" ); } if (dataBuffer.length === 0) { throw this.makeError( "INVALID_DATA", "Cannot write empty data", "Provide at least one byte to write" ); } if (address + dataBuffer.length > 0x10000) { throw this.makeError( "INVALID_RANGE", `Write would extend past end of memory (0x${address.toString(16)} + ${dataBuffer.length} bytes)`, "Reduce data length or use a lower start address" ); } // Ensure VICE is stopped before memory write await this.ensureStopped(); // Build request per official VICE docs: // side_effects(1) + start(2) + end(2) + memspace(1) + bankId(2) + data(N) = 8 byte header + data const endAddress = address + dataBuffer.length - 1; const body = Buffer.alloc(8 + dataBuffer.length); body[0] = 0; // No side effects body.writeUInt16LE(address, 1); body.writeUInt16LE(endAddress, 3); body[5] = memspace; body.writeUInt16LE(0, 6); // bankId = 0 (default bank) dataBuffer.copy(body, 8); await this.sendCommand(Command.MemorySet, body); }
- src/index.ts:308-317 (schema)Zod input schema defining parameters: address (0-65535), bytes (non-empty array of 0-255 integers).inputSchema: z.object({ address: z .number() .min(0) .max(0xffff) .describe("Start address (0x0000-0xFFFF)"), bytes: z .array(z.number().min(0).max(255)) .min(1) .describe("Array of bytes to write (0-255 each)"),