pine_write64
Atomic 64-bit write to PS2 EE memory at an 8-byte aligned address. Accepts a decimal string for values up to 2^64-1 to preserve precision.
Instructions
PURPOSE: Write an unsigned 64-bit little-endian value to EE main address space. USAGE: For true 64-bit writes — full pointers, large IDs, packed doubleword state. Atomic from the emulator's perspective; preferred over chaining two pine_write32 calls (a running game can observe the in-between state). For 8/16/32-bit values use the corresponding sibling. BEHAVIOR: DESTRUCTIVE: overwrites eight bytes from address with no undo. Direct write — bypasses TLB; writes to read-only regions silently dropped. Address MUST be 8-byte aligned. PINE on PCSX2 does NOT enforce alignment — unaligned access typically returns whatever bytes are at the aligned address below, silently corrupting the value. If you need an unaligned multi-byte read, use pine_read_range and assemble the bytes yourself. value is a DECIMAL STRING (0 through 18446744073709551615) to preserve precision past JS's 2^53 number limit. Errors on connection drop or PINE FAIL.
PlayStation 2 main address space landmarks (PCSX2): 0x00100000-0x01FFFFFF EE main RAM (32 MiB) — game code & data; the most common target 0x10000000 Hardware registers (DMA, GIF, VIF, etc.) 0x11000000 VU0 / VU1 memory 0x12000000 GS privileged registers 0x1C000000-0x1C1FFFFF IOP RAM (2 MiB) 0x1F800000 IOP scratchpad 0x70000000 EE scratchpad (16 KiB) PINE memory operations target the EE address space.
RETURNS: 'Wrote VAL_DEC (0xVAL_HEX) → ADDR_HEX' — VAL_DEC may exceed 2^53.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | Absolute byte address in the EE main address space (NOT a per-domain offset). Pass as a number; hex literals like 0x00200000 are fine. Reads 8 consecutive bytes starting here. MUST be 8-byte aligned (address % 8 === 0). PINE on PCSX2 does NOT enforce alignment — unaligned access typically returns whatever bytes are at the aligned address below, silently corrupting the value. If you need an unaligned multi-byte read, use pine_read_range and assemble the bytes yourself. Useful range: 0x00100000-0x01FFFFFF for EE main RAM (where 99% of game state lives). An unmapped or invalid address returns a PINE FAIL response. | |
| value | Yes | 64-bit value to write, as a non-negative DECIMAL STRING (digits only, no '0x' prefix, no sign, no separators). Range 0 through 18446744073709551615 (2^64 - 1). Example: "18446744073709551615" writes 0xFFFFFFFFFFFFFFFF. Encoded as a string so values past 2^53 are preserved exactly (JSON numbers lose precision at that point). For signed 64-bit values, encode as two's complement (e.g. -1 → "18446744073709551615"). |
Implementation Reference
- src/tools.ts:398-402 (handler)Handler case for 'pine_write64' in the CallToolRequestSchema switch: converts the string value to BigInt, calls pine.write64(), and returns a formatted success message.
case "pine_write64": { const v = BigInt(p.value as string); await pine.write64(addr(), v); return ok(`Wrote ${fmtHex(v)} → ${addrHex(addr())}`); } - src/tools.ts:269-293 (schema)Schema definition for 'pine_write64' tool: name, description, and inputSchema with 'address' (integer) and 'value' (decimal string pattern) properties.
{ name: "pine_write64", description: `PURPOSE: Write an unsigned 64-bit little-endian value to ${ADDR_SPACE}. ` + "USAGE: For true 64-bit writes — full pointers, large IDs, packed doubleword state. Atomic from the emulator's perspective; preferred over chaining two pine_write32 calls (a running game can observe the in-between state). For 8/16/32-bit values use the corresponding sibling. " + "BEHAVIOR: DESTRUCTIVE: overwrites eight bytes from `address` with no undo. Direct write — bypasses TLB; writes to read-only regions silently dropped. Address MUST be 8-byte aligned. " + target.alignmentNote + " `value` is a DECIMAL STRING (0 through 18446744073709551615) to preserve precision past JS's 2^53 number limit. Errors on connection drop or PINE FAIL.\n\n" + MEM + "\n\n" + "RETURNS: 'Wrote VAL_DEC (0xVAL_HEX) → ADDR_HEX' — VAL_DEC may exceed 2^53.", inputSchema: { type: "object", required: ["address", "value"], properties: { address: { type: "integer", minimum: 0, description: addressParamDesc(target, 8) }, value: { type: "string", pattern: "^[0-9]+$", description: "64-bit value to write, as a non-negative DECIMAL STRING (digits only, no '0x' prefix, no sign, no separators). " + "Range 0 through 18446744073709551615 (2^64 - 1). Example: \"18446744073709551615\" writes 0xFFFFFFFFFFFFFFFF. " + "Encoded as a string so values past 2^53 are preserved exactly (JSON numbers lose precision at that point). " + "For signed 64-bit values, encode as two's complement (e.g. -1 → \"18446744073709551615\")." }, }, additionalProperties: false, }, - src/tools.ts:345-347 (registration)The tool is registered via the tool list returned by buildTools(), and the switch handler is set up in registerTools() via server.setRequestHandler(CallToolRequestSchema, ...).
export function registerTools(server: Server, pine: PineClient, target: TargetInfo): void { const TOOLS = buildTools(target); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS })); - src/pine.ts:254-259 (helper)PineClient.write64() helper: allocates a 12-byte buffer, writes the 32-bit LE address and 64-bit LE bigint value, then sends it over the PINE protocol with the Write64 opcode (0x07).
async write64(addr: number, val: bigint): Promise<void> { const args = Buffer.alloc(12); args.writeUInt32LE(addr, 0); args.writeBigUInt64LE(val, 4); await this.call(Op.Write64, args); } - src/pine.ts:33-33 (helper)Opcode constant: Write64 = 0x07, used to identify the write64 operation in the PINE protocol.
Write64: 0x07,