Fix DisplayGet API v2, writeMemory format, sprite address validation
- Change API_VERSION from 0x01 to 0x02 (required for DisplayGet, KeyboardFeed) - Fix writeMemory body format: add end address and bankId per VICE docs - Fix step() to use AdvanceInstructions (0x71) - no separate Step command - Add validateSpriteDataAddress() with region/severity/warning - Update readSprites to include address diagnostics in dataAddress field - Prioritize address issues over visibility issues in hints 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
57c4c43b8a
commit
26b5b694c1
4 changed files with 138 additions and 15 deletions
39
src/index.ts
39
src/index.ts
|
|
@ -11,6 +11,7 @@ import {
|
|||
getVideoAddresses,
|
||||
getGraphicsMode,
|
||||
isSpriteVisible,
|
||||
validateSpriteDataAddress,
|
||||
disassemble,
|
||||
getLabelForAddress,
|
||||
} from "./utils/index.js";
|
||||
|
|
@ -1434,9 +1435,10 @@ Related tools: readVicState, readMemory (for sprite data)`,
|
|||
|
||||
const visibility = isSpriteVisible(x, y, enabled);
|
||||
|
||||
// Sprite data address
|
||||
// Sprite data address with region validation
|
||||
const pointer = spritePointers[i];
|
||||
const dataAddress = bankInfo.baseAddress + pointer * 64;
|
||||
const addressInfo = validateSpriteDataAddress(dataAddress);
|
||||
|
||||
sprites.push({
|
||||
index: i,
|
||||
|
|
@ -1459,16 +1461,40 @@ Related tools: readVicState, readMemory (for sprite data)`,
|
|||
dataAddress: {
|
||||
value: dataAddress,
|
||||
hex: `$${dataAddress.toString(16).padStart(4, "0")}`,
|
||||
region: addressInfo.region,
|
||||
severity: addressInfo.severity,
|
||||
warning: addressInfo.warning,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const enabledCount = sprites.filter((s) => s.enabled).length;
|
||||
const visibleCount = sprites.filter((s) => s.position.visible).length;
|
||||
const issues = sprites
|
||||
|
||||
// Collect visibility issues
|
||||
const visibilityIssues = sprites
|
||||
.filter((s) => s.enabled && !s.position.visible)
|
||||
.map((s) => `Sprite ${s.index}: ${s.position.visibilityReason}`);
|
||||
|
||||
// Collect data address issues (warnings and errors)
|
||||
const addressIssues = sprites
|
||||
.filter((s) => s.enabled && s.dataAddress.warning)
|
||||
.map((s) => `Sprite ${s.index}: ${s.dataAddress.warning} (${s.dataAddress.hex})`);
|
||||
|
||||
const allIssues = [...visibilityIssues, ...addressIssues];
|
||||
|
||||
// Build hint based on most critical issue
|
||||
let hint: string;
|
||||
if (addressIssues.length > 0) {
|
||||
hint = `⚠️ ${addressIssues.length} sprite(s) with suspicious data address: ${addressIssues[0]}`;
|
||||
} else if (visibilityIssues.length > 0) {
|
||||
hint = `${visibilityIssues.length} enabled sprite(s) not visible: ${visibilityIssues[0]}`;
|
||||
} else if (enabledCount === 0) {
|
||||
hint = "No sprites enabled";
|
||||
} else {
|
||||
hint = `${enabledCount} sprite(s) enabled, ${visibleCount} visible`;
|
||||
}
|
||||
|
||||
return formatResponse({
|
||||
count: sprites.length,
|
||||
enabledCount,
|
||||
|
|
@ -1476,13 +1502,8 @@ Related tools: readVicState, readMemory (for sprite data)`,
|
|||
sprites,
|
||||
spriteMulticolor0: getColorInfo(vicData[0x25]),
|
||||
spriteMulticolor1: getColorInfo(vicData[0x26]),
|
||||
issues: issues.length > 0 ? issues : undefined,
|
||||
hint:
|
||||
issues.length > 0
|
||||
? `${issues.length} enabled sprite(s) not visible: ${issues[0]}`
|
||||
: enabledCount === 0
|
||||
? "No sprites enabled"
|
||||
: `${enabledCount} sprite(s) enabled, ${visibleCount} visible`,
|
||||
issues: allIssues.length > 0 ? allIssues : undefined,
|
||||
hint,
|
||||
});
|
||||
} catch (error) {
|
||||
return formatError(error as ViceError);
|
||||
|
|
|
|||
|
|
@ -455,13 +455,16 @@ export class ViceClient {
|
|||
// Ensure VICE is stopped before memory write
|
||||
await this.ensureStopped();
|
||||
|
||||
// Build request: side_effects(1) + start(2) + memspace(1) + length-1(1) + data(N)
|
||||
const body = Buffer.alloc(5 + dataBuffer.length);
|
||||
// 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[3] = memspace;
|
||||
body[4] = dataBuffer.length - 1;
|
||||
dataBuffer.copy(body, 5);
|
||||
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);
|
||||
}
|
||||
|
|
@ -509,6 +512,7 @@ export class ViceClient {
|
|||
}
|
||||
|
||||
async step(count = 1, stepOver = false): Promise<ViceResponse> {
|
||||
// Uses AdvanceInstructions (0x71) - there is no separate Step command in VICE
|
||||
const body = Buffer.alloc(3);
|
||||
body[0] = stepOver ? 1 : 0;
|
||||
body.writeUInt16LE(count, 1);
|
||||
|
|
@ -518,6 +522,7 @@ export class ViceClient {
|
|||
}
|
||||
|
||||
async advanceInstructions(count: number, stepOver = false): Promise<ViceResponse> {
|
||||
// Alias for step() - kept for API compatibility
|
||||
const body = Buffer.alloc(3);
|
||||
body[0] = stepOver ? 1 : 0;
|
||||
body.writeUInt16LE(count, 1);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// API Constants
|
||||
export const STX = 0x02;
|
||||
export const API_VERSION = 0x01; // VICE 3.x uses API v1
|
||||
export const API_VERSION = 0x02; // VICE 3.5+ uses API v2 (required for DisplayGet, KeyboardFeed)
|
||||
|
||||
// Command codes (per official VICE manual: https://vice-emu.sourceforge.io/vice_13.html)
|
||||
export enum Command {
|
||||
|
|
|
|||
|
|
@ -178,3 +178,100 @@ export function describeAddress(address: number): string {
|
|||
if (address >= 0xe000) return "KERNAL ROM / RAM";
|
||||
return "";
|
||||
}
|
||||
|
||||
// Severity levels for address validation
|
||||
export type AddressSeverity = "ok" | "warning" | "error";
|
||||
|
||||
// Sprite data address validation
|
||||
// Returns region info and severity for sprite data addresses
|
||||
export interface SpriteDataAddressInfo {
|
||||
region: string;
|
||||
severity: AddressSeverity;
|
||||
warning?: string;
|
||||
}
|
||||
|
||||
export function validateSpriteDataAddress(address: number): SpriteDataAddressInfo {
|
||||
// Zero page - very wrong for sprite data
|
||||
if (address < 0x0100) {
|
||||
return {
|
||||
region: "Zero page",
|
||||
severity: "error",
|
||||
warning: "Sprite data in zero page - likely wrong pointer",
|
||||
};
|
||||
}
|
||||
|
||||
// Stack - definitely wrong
|
||||
if (address < 0x0200) {
|
||||
return {
|
||||
region: "Stack",
|
||||
severity: "error",
|
||||
warning: "Sprite data in stack area - definitely wrong",
|
||||
};
|
||||
}
|
||||
|
||||
// BASIC program area - might conflict with code
|
||||
if (address >= 0x0801 && address < 0x2000) {
|
||||
return {
|
||||
region: "BASIC program area",
|
||||
severity: "warning",
|
||||
warning: "Sprite data in BASIC area - may conflict with program",
|
||||
};
|
||||
}
|
||||
|
||||
// Default screen RAM - usually wrong
|
||||
if (address >= 0x0400 && address < 0x0800) {
|
||||
return {
|
||||
region: "Default screen RAM",
|
||||
severity: "warning",
|
||||
warning: "Sprite data in screen RAM - probably wrong",
|
||||
};
|
||||
}
|
||||
|
||||
// Common sprite data areas - OK
|
||||
if (address >= 0x2000 && address < 0x4000) {
|
||||
return {
|
||||
region: "Common sprite area",
|
||||
severity: "ok",
|
||||
};
|
||||
}
|
||||
|
||||
// VIC bank 1 sprite area - OK
|
||||
if (address >= 0x4000 && address < 0x8000) {
|
||||
return {
|
||||
region: "VIC bank 1 sprite area",
|
||||
severity: "ok",
|
||||
};
|
||||
}
|
||||
|
||||
// VIC bank 2 - has ROM shadow issues
|
||||
if (address >= 0x8000 && address < 0xc000) {
|
||||
return {
|
||||
region: "VIC bank 2",
|
||||
severity: "ok",
|
||||
};
|
||||
}
|
||||
|
||||
// I/O area - cannot store sprite data here
|
||||
if (address >= 0xd000 && address < 0xe000) {
|
||||
return {
|
||||
region: "I/O space",
|
||||
severity: "error",
|
||||
warning: "Cannot store sprite data in I/O space",
|
||||
};
|
||||
}
|
||||
|
||||
// High memory - ROM shadow area
|
||||
if (address >= 0xe000) {
|
||||
return {
|
||||
region: "KERNAL ROM area",
|
||||
severity: "warning",
|
||||
warning: "Sprite data may conflict with KERNAL ROM shadow",
|
||||
};
|
||||
}
|
||||
|
||||
// Other areas
|
||||
return {
|
||||
region: describeAddress(address) || "RAM",
|
||||
severity: "ok",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue