pvbemu/web/debugger/ISX.js

178 lines
4.8 KiB
JavaScript

// Debug manager for Intelligent Systems binaries
class ISX {
///////////////////////// Initialization Methods //////////////////////////
// Throws on decoding error
constructor(data) {
// Configure instance fields
this.data = data;
this.offset = 0;
this.ranges = [];
this.symbols = [];
this.codes = [];
// Skip any header that may be present
if (data.length >= 32 && this.readInt(3) == 0x585349)
this.offset = 32;
else this.offset = 0;
// Process all records
while (this.offset < this.data.length) {
switch (this.readInt(1)) {
// Virtual Boy records
case 0x11: this.code (); break;
case 0x13: this.range (); break;
case 0x14: this.symbol(); break;
// System records
case 0x20:
case 0x21:
case 0x22:
let length = this.readInt(4);
this.offset += length;
break;
// Other records
default: throw "ISX decode error";
}
}
// Cleanup instance fields
delete this.data;
delete this.decoder;
delete this.offset;
}
///////////////////////////// Public Methods //////////////////////////////
// Produce a .vb format ROM file from the ISX code segments
toROM() {
let head = 0x00000000;
let tail = 0x01000000;
// Inspect all code segments
for (let code of this.codes) {
let start = code.address & 0x00FFFFFF;
let end = start + code.data.length;
// Segment begins in the first half of ROM
if (start < 0x00800000) {
// Segment ends in the second half of ROM
if (end > 0x00800000) {
head = tail = 0;
break;
}
// Segment ends in the first half of ROM
else if (end > head)
head = end;
}
// Segment begins in the second half of ROM
else if (start < tail)
tail = start;
}
// Prepare the output buffer
let min = head + 0x01000000 - tail;
let size = 1;
for (; size < min; size <<= 1);
let rom = new Uint8Array(size);
// Output all code segments
for (let code of this.codes) {
let dest = code.address & rom.length - 1;
for (let src = 0; src < code.data.length; src++, dest++)
rom[dest] = code.data[src];
}
return rom;
}
///////////////////////////// Private Methods /////////////////////////////
// Process a code record
code() {
let address = this.readInt(4);
let length = this.readInt(4);
let data = this.readBytes(length);
if (
length == 0 ||
length > 0x01000000 ||
(address & 0x07000000) != 0x07000000 ||
(address & 0x07000000) + length > 0x08000000
) throw "ISX decode error";
this.codes.push({
address: address,
data : data
});
}
// Process a range record
range() {
let count = this.readInt(2);
while (count--) {
let start = this.readInt(4);
let end = this.readInt(4);
let type = this.readInt(1);
this.ranges.push({
end : end,
start: start,
type : type
});
}
}
// Process a symbol record
symbol() {
let count = this.readInt(2);
while (count--) {
let length = this.readInt(1);
let name = this.readString(length);
let flags = this.readInt(2);
let address = this.readInt(4);
this.symbols.push({
address: address,
flags : flags,
name : name
});
}
}
// Read a byte buffer
readBytes(size) {
if (this.offset + size > this.data.length)
throw "ISX decode error";
let ret = this.data.slice(this.offset, this.offset + size);
this.offset += size;
return ret;
}
// Read an integer
readInt(size) {
if (this.offset + size > this.data.length)
throw "ISX decode error";
let ret = new Uint32Array(1);
for (let shift = 0; size > 0; size--, shift += 8)
ret[0] |= this.data[this.offset++] << shift;
return ret[0];
}
// Read a text string
readString(size) {
return (this.decoder = this.decoder || new TextDecoder()
).decode(this.readBytes(size));
}
}
export { ISX };