readMemory
Read raw memory data from the Commodore 64's address space to inspect variables, screen RAM, sound registers, and system memory during debugging sessions.
Instructions
Read memory from the C64's address space.
Returns raw bytes plus hex and ASCII representations.
C64 memory map highlights:
$0000-$00FF: Zero page (fast access, common variables)
$0100-$01FF: Stack
$0400-$07FF: Default screen RAM (1000 bytes)
$D000-$D3FF: VIC-II registers (graphics)
$D400-$D7FF: SID registers (sound)
$D800-$DBFF: Color RAM
For screen content, consider using readScreen instead for interpreted output. For sprite info, use readSprites for semantic data.
Related tools: writeMemory, readScreen, readSprites, readVicState
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | Start address (0x0000-0xFFFF) | |
| length | No | Number of bytes to read (default: 256, max: 65536) |
Implementation Reference
- src/index.ts:221-264 (handler)MCP tool handler for 'readMemory': computes end address, reads memory via ViceClient.readMemory, generates hex dump and ASCII representation, provides memory region hint, returns formatted response.}, async (args) => { const address = args.address; const length = Math.min(args.length || 256, 65536); const endAddress = Math.min(address + length - 1, 0xffff); try { const data = await client.readMemory(address, endAddress); // Format as hex dump const hexLines: string[] = []; const asciiLines: string[] = []; for (let i = 0; i < data.length; i += 16) { const chunk = data.subarray(i, Math.min(i + 16, data.length)); const hex = Array.from(chunk) .map((b) => b.toString(16).padStart(2, "0")) .join(" "); const ascii = Array.from(chunk) .map((b) => (b >= 32 && b < 127 ? String.fromCharCode(b) : ".")) .join(""); hexLines.push( `$${(address + i).toString(16).padStart(4, "0")}: ${hex}` ); asciiLines.push(ascii); } return formatResponse({ address: { value: address, hex: `$${address.toString(16).padStart(4, "0")}`, }, length: data.length, bytes: Array.from(data), hex: hexLines.join("\n"), ascii: asciiLines.join(""), hint: getMemoryHint(address, endAddress), }); } catch (error) { return formatError(error as ViceError); } } );
- src/index.ts:208-220 (schema)Zod input schema for readMemory tool: requires 'address' (0-65535), optional 'length' (1-65536, default 256).inputSchema: z.object({ address: z .number() .min(0) .max(0xffff) .describe("Start address (0x0000-0xFFFF)"), length: z .number() .min(1) .max(65536) .optional() .describe("Number of bytes to read (default: 256, max: 65536)"), }),
- src/index.ts:188-265 (registration)Registration of the 'readMemory' tool using McpServer.registerTool, including name, description, input schema, and handler function.// Tool: readMemory - Read memory from the C64 server.registerTool( "readMemory", { description: `Read memory from the C64's address space. Returns raw bytes plus hex and ASCII representations. C64 memory map highlights: - $0000-$00FF: Zero page (fast access, common variables) - $0100-$01FF: Stack - $0400-$07FF: Default screen RAM (1000 bytes) - $D000-$D3FF: VIC-II registers (graphics) - $D400-$D7FF: SID registers (sound) - $D800-$DBFF: Color RAM For screen content, consider using readScreen instead for interpreted output. For sprite info, use readSprites for semantic data. Related tools: writeMemory, readScreen, readSprites, readVicState`, inputSchema: z.object({ address: z .number() .min(0) .max(0xffff) .describe("Start address (0x0000-0xFFFF)"), length: z .number() .min(1) .max(65536) .optional() .describe("Number of bytes to read (default: 256, max: 65536)"), }), }, async (args) => { const address = args.address; const length = Math.min(args.length || 256, 65536); const endAddress = Math.min(address + length - 1, 0xffff); try { const data = await client.readMemory(address, endAddress); // Format as hex dump const hexLines: string[] = []; const asciiLines: string[] = []; for (let i = 0; i < data.length; i += 16) { const chunk = data.subarray(i, Math.min(i + 16, data.length)); const hex = Array.from(chunk) .map((b) => b.toString(16).padStart(2, "0")) .join(" "); const ascii = Array.from(chunk) .map((b) => (b >= 32 && b < 127 ? String.fromCharCode(b) : ".")) .join(""); hexLines.push( `$${(address + i).toString(16).padStart(4, "0")}: ${hex}` ); asciiLines.push(ascii); } return formatResponse({ address: { value: address, hex: `$${address.toString(16).padStart(4, "0")}`, }, length: data.length, bytes: Array.from(data), hex: hexLines.join("\n"), ascii: asciiLines.join(""), hint: getMemoryHint(address, endAddress), }); } catch (error) { return formatError(error as ViceError); } } );
- src/protocol/client.ts:354-395 (helper)ViceClient.readMemory implementation: validates address range, constructs MemoryGet command packet for VICE binary monitor protocol, sends via sendCommand, parses response Buffer, returns raw memory bytes.async readMemory( startAddress: number, endAddress: number, memspace: MemorySpace = MemorySpace.MainCPU ): Promise<Buffer> { // Validate addresses 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" ); } // Build request: side_effects(1) + start(2) + memspace(1) + end(2) const body = Buffer.alloc(6); body[0] = 0; // No side effects body.writeUInt16LE(startAddress, 1); body[3] = memspace; body.writeUInt16LE(endAddress, 4); // VICE may send MemoryGet (0x31) as async event with ReqID=0xff const response = await this.sendCommand(Command.MemoryGet, body, ResponseType.MemoryGet); // Response body: length(2) + data(N) const dataLength = response.body.readUInt16LE(0); return response.body.subarray(2, 2 + dataLength); }