Fix VICE protocol: API v1, correct command codes, body length
Multiple fixes based on live testing: - API version changed to 0x01 (VICE 3.x uses v1, not v2) - Command codes updated per KB docs: - RegistersGet: 0x22 (was 0x31) - Continue: 0x31 (was 0x81) - Step: 0x32 (was 0x82) - Body length field now excludes ReqID and Cmd (just command body) - Response parsing: totalLength = 9 + bodyLength Added test script for debugging protocol issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0a6982ae40
commit
995683a936
3 changed files with 122 additions and 17 deletions
|
|
@ -169,6 +169,7 @@ export class ViceClient {
|
||||||
|
|
||||||
// Process complete packets
|
// Process complete packets
|
||||||
// Response header: STX(1) + API(1) + bodyLength(4) + responseType(1) + errorCode(1) + requestId(1) = 9 bytes
|
// Response header: STX(1) + API(1) + bodyLength(4) + responseType(1) + errorCode(1) + requestId(1) = 9 bytes
|
||||||
|
// Then body of `bodyLength` bytes follows
|
||||||
while (this.responseBuffer.length >= 9) {
|
while (this.responseBuffer.length >= 9) {
|
||||||
const stx = this.responseBuffer[0];
|
const stx = this.responseBuffer[0];
|
||||||
if (stx !== STX) {
|
if (stx !== STX) {
|
||||||
|
|
@ -179,7 +180,7 @@ export class ViceClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
const bodyLength = this.responseBuffer.readUInt32LE(2);
|
const bodyLength = this.responseBuffer.readUInt32LE(2);
|
||||||
const totalLength = 6 + bodyLength; // Header prefix (6) + body (which includes type, error, reqId)
|
const totalLength = 9 + bodyLength; // Header (9) + body
|
||||||
|
|
||||||
debugLog(`Packet: bodyLength=${bodyLength}, totalLength=${totalLength}, bufferLen=${this.responseBuffer.length}`);
|
debugLog(`Packet: bodyLength=${bodyLength}, totalLength=${totalLength}, bufferLen=${this.responseBuffer.length}`);
|
||||||
|
|
||||||
|
|
@ -272,11 +273,11 @@ export class ViceClient {
|
||||||
const requestId = this.nextRequestId();
|
const requestId = this.nextRequestId();
|
||||||
|
|
||||||
// Build packet: STX(1) + API(1) + Length(4) + RequestID(1) + Command(1) + Body
|
// Build packet: STX(1) + API(1) + Length(4) + RequestID(1) + Command(1) + Body
|
||||||
// Length field includes: RequestID(1) + Command(1) + Body
|
// Length field is ONLY the command body, NOT including ReqID or Command
|
||||||
const header = Buffer.alloc(8);
|
const header = Buffer.alloc(8);
|
||||||
header[0] = STX;
|
header[0] = STX;
|
||||||
header[1] = API_VERSION;
|
header[1] = API_VERSION;
|
||||||
header.writeUInt32LE(body.length + 2, 2); // Body length includes request ID (1) and command (1)
|
header.writeUInt32LE(body.length, 2); // Just the command body length
|
||||||
header[6] = requestId;
|
header[6] = requestId;
|
||||||
header[7] = command;
|
header[7] = command;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,42 +2,44 @@
|
||||||
|
|
||||||
// API Constants
|
// API Constants
|
||||||
export const STX = 0x02;
|
export const STX = 0x02;
|
||||||
export const API_VERSION = 0x02;
|
export const API_VERSION = 0x01; // VICE 3.x uses API v1
|
||||||
|
|
||||||
// Command codes
|
// Command codes (per KB docs)
|
||||||
export enum Command {
|
export enum Command {
|
||||||
// Memory operations
|
// Memory operations
|
||||||
MemoryGet = 0x01,
|
MemoryGet = 0x01,
|
||||||
MemorySet = 0x02,
|
MemorySet = 0x02,
|
||||||
|
|
||||||
// Checkpoint (breakpoint/watchpoint) operations
|
// Checkpoint (breakpoint/watchpoint) operations
|
||||||
CheckpointGet = 0x11,
|
CheckpointSet = 0x11,
|
||||||
CheckpointSet = 0x12,
|
CheckpointGet = 0x12,
|
||||||
CheckpointDelete = 0x13,
|
CheckpointDelete = 0x13,
|
||||||
CheckpointList = 0x14,
|
CheckpointList = 0x14,
|
||||||
CheckpointToggle = 0x15,
|
CheckpointToggle = 0x15,
|
||||||
|
|
||||||
// Register operations
|
// Register operations
|
||||||
RegistersGet = 0x31,
|
RegistersGet = 0x22,
|
||||||
RegistersSet = 0x32,
|
RegistersSet = 0x23,
|
||||||
|
|
||||||
// Execution control
|
// Execution control
|
||||||
|
Continue = 0x31,
|
||||||
|
Step = 0x32,
|
||||||
|
Reset = 0x43,
|
||||||
|
|
||||||
|
// Dump/Undump (snapshots)
|
||||||
Dump = 0x41,
|
Dump = 0x41,
|
||||||
Undump = 0x42,
|
Undump = 0x42,
|
||||||
Reset = 0x43,
|
|
||||||
|
|
||||||
// Resources
|
// Resources
|
||||||
ResourceGet = 0x51,
|
ResourceGet = 0x51,
|
||||||
ResourceSet = 0x52,
|
ResourceSet = 0x52,
|
||||||
|
|
||||||
// Advanced execution
|
// Exit
|
||||||
AdvanceInstructions = 0x71,
|
Exit = 0x71,
|
||||||
KeyboardFeed = 0x72,
|
|
||||||
Continue = 0x81,
|
|
||||||
Step = 0x82,
|
|
||||||
|
|
||||||
// Quit
|
// Advanced execution
|
||||||
Quit = 0xbb,
|
KeyboardFeed = 0x72,
|
||||||
|
AdvanceInstructions = 0x73,
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
DisplayGet = 0x84,
|
DisplayGet = 0x84,
|
||||||
|
|
|
||||||
102
test-binary-protocol.js
Normal file
102
test-binary-protocol.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// Quick test script to verify VICE binary monitor protocol
|
||||||
|
// Run: node test-binary-protocol.js
|
||||||
|
|
||||||
|
import { Socket } from 'net';
|
||||||
|
|
||||||
|
const HOST = '127.0.0.1';
|
||||||
|
const PORT = 6502;
|
||||||
|
|
||||||
|
const sock = new Socket();
|
||||||
|
let buffer = Buffer.alloc(0);
|
||||||
|
|
||||||
|
function parsePackets() {
|
||||||
|
while (buffer.length >= 9) {
|
||||||
|
const stx = buffer[0];
|
||||||
|
if (stx !== 0x02) {
|
||||||
|
console.log(`Skipping non-STX byte: 0x${stx.toString(16)}`);
|
||||||
|
buffer = buffer.subarray(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiVer = buffer[1];
|
||||||
|
const bodyLength = buffer.readUInt32LE(2);
|
||||||
|
const totalLength = 9 + bodyLength;
|
||||||
|
|
||||||
|
console.log(`\nPacket header: STX=0x${stx.toString(16)} API=0x${apiVer.toString(16)} bodyLen=${bodyLength} totalLen=${totalLength} bufLen=${buffer.length}`);
|
||||||
|
|
||||||
|
if (buffer.length < totalLength) {
|
||||||
|
console.log(' Waiting for more data...');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const respType = buffer[6];
|
||||||
|
const errorCode = buffer[7];
|
||||||
|
const reqId = buffer[8];
|
||||||
|
const body = buffer.subarray(9, totalLength);
|
||||||
|
|
||||||
|
console.log(` RespType: 0x${respType.toString(16)}`);
|
||||||
|
console.log(` Error: 0x${errorCode.toString(16)}`);
|
||||||
|
console.log(` ReqID: ${reqId} (0x${reqId.toString(16)})`);
|
||||||
|
console.log(` Body (${body.length} bytes): ${body.toString('hex')}`);
|
||||||
|
|
||||||
|
// Interpret response type
|
||||||
|
const typeNames = {
|
||||||
|
0x00: 'Invalid',
|
||||||
|
0x01: 'OK',
|
||||||
|
0x02: 'Object',
|
||||||
|
0x11: 'Stopped',
|
||||||
|
0x12: 'Resumed',
|
||||||
|
0x31: 'MemoryGet',
|
||||||
|
0x62: 'RegisterInfo',
|
||||||
|
0x63: 'CheckpointHit',
|
||||||
|
};
|
||||||
|
console.log(` Type name: ${typeNames[respType] || 'Unknown'}`);
|
||||||
|
|
||||||
|
buffer = buffer.subarray(totalLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sock.on('connect', () => {
|
||||||
|
console.log('Connected to VICE');
|
||||||
|
|
||||||
|
// Send a simple registers get command
|
||||||
|
const body = Buffer.from([0x00]); // memspace = main CPU
|
||||||
|
const header = Buffer.alloc(8);
|
||||||
|
header[0] = 0x02; // STX
|
||||||
|
header[1] = 0x01; // API version
|
||||||
|
header.writeUInt32LE(body.length, 2); // body length
|
||||||
|
header[6] = 0x01; // request ID
|
||||||
|
header[7] = 0x22; // RegistersGet command
|
||||||
|
|
||||||
|
const packet = Buffer.concat([header, body]);
|
||||||
|
console.log('Sending packet:', packet.toString('hex'));
|
||||||
|
|
||||||
|
sock.write(packet);
|
||||||
|
console.log('Packet sent, waiting for response...');
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.on('data', (data) => {
|
||||||
|
console.log(`\n--- Received ${data.length} bytes: ${data.toString('hex')}`);
|
||||||
|
buffer = Buffer.concat([buffer, data]);
|
||||||
|
parsePackets();
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.on('error', (err) => {
|
||||||
|
console.error('Socket error:', err.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.on('close', () => {
|
||||||
|
console.log('\nConnection closed');
|
||||||
|
if (buffer.length > 0) {
|
||||||
|
console.log(`Remaining buffer: ${buffer.toString('hex')}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.on('timeout', () => {
|
||||||
|
console.log('Socket timeout');
|
||||||
|
sock.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
sock.setTimeout(5000);
|
||||||
|
sock.connect(PORT, HOST);
|
||||||
Loading…
Reference in a new issue