Files
Crystal/src/game/rom/reader.ts
T
2026-09-23 09:23:29 +02:00

55 lines
1.5 KiB
TypeScript

/** Bounds-checked access to the user's cartridge. No CPU or hardware state. */
export class RomReader {
constructor(readonly bytes: Uint8Array) {}
byte(offset: number): number {
this.check(offset, 1);
return this.bytes[offset];
}
word(offset: number): number {
return this.byte(offset) | (this.byte(offset + 1) << 8);
}
slice(offset: number, length: number): Uint8Array {
this.check(offset, length);
return this.bytes.slice(offset, offset + length);
}
address(bank: number, address: number): number {
if (
!Number.isInteger(bank) ||
bank < 0 ||
bank >= this.bytes.length / 0x4000 ||
!Number.isInteger(address) ||
address < 0 ||
address >= 0x8000
) {
throw new Error("Invalid ROM bank or address.");
}
const offset =
address < 0x4000 ? address : bank * 0x4000 + address - 0x4000;
this.check(offset, 1);
return offset;
}
near(pointer: number, bank: number): number {
return this.address(bank, this.word(pointer));
}
far(pointer: number): number {
return this.near(pointer + 1, this.byte(pointer));
}
bank(offset: number): number {
this.check(offset, 1);
return Math.floor(offset / 0x4000);
}
private check(offset: number, length: number) {
if (
!Number.isInteger(offset) ||
!Number.isInteger(length) ||
offset < 0 ||
length < 0 ||
offset + length > this.bytes.length
) {
throw new Error(
"Cartridge data is truncated or contains an invalid pointer.",
);
}
}
}