/** * GBA System Bus * * Implements MemoryBus and dispatches reads/writes to the * appropriate subsystem based on address ranges. * * Memory map: * 0x00000000-0x00003FFF BIOS (16 KB) * 0x02000000-0x0203FFFF EWRAM (256 KB) * 0x03000000-0x03007FFF IWRAM (32 KB) * 0x04000000-0x040003FE I/O Registers (MMIO) * 0x05000000-0x050003FF Palette RAM (1 KB) * 0x06000000-0x06017FFF VRAM (96 KB) * 0x07000000-0x070003FF OAM (1 KB) * 0x08000000-0x09FFFFFF Game Pak ROM (up to 32 MB) * 0x0E000000-0x0E00FFFF Game Pak SRAM (64 KB) */ import type { MemoryBus } from '@gba-kit/arm-emulator'; import type { Apu } from './apu/apu.js'; import type { DmaController } from './dma.js'; import type { InputController } from './input.js'; import type { InterruptController } from './interrupts.js'; import type { SystemBusSnapshot } from './savestate.js'; import type { TimerController } from './timers.js'; import type { WriteOrigin } from './write-source.js'; /** A committed write reported to a data watchpoint. */ export interface WatchpointWrite { /** The watched byte that was written (within the access, clamped to the watch range). */ address: number; /** Value committed, masked to `size` bytes. */ value: number; /** Access size in bytes (1, 2 or 4). */ size: number; /** Active DMA channel (0-3) if a DMA performed the write, else -1 (a CPU/BIOS store). */ dmaChannel: number; /** The DMA's start instruction when `dmaChannel >= 0`, else null. */ dmaOrigin: WriteOrigin | null; } /** A read reported to a data watchpoint. */ export interface WatchpointRead { /** The watched byte that was read (within the access, clamped to the watch range). */ address: number; /** Value the load returned, masked to `size` bytes — the whole access, not just the watched bytes. */ value: number; /** Access size in bytes (1, 2 or 4). */ size: number; /** Active DMA channel (0-3) if a DMA performed the read, else -1 (a CPU/BIOS load). */ dmaChannel: number; /** The DMA's start instruction when `dmaChannel >= 0`, else null. */ dmaOrigin: WriteOrigin | null; } /** The kinds of battery-backed save a cartridge can declare. */ export type SaveType = 'eeprom' | 'sram' | 'flash512' | 'flash1m'; /** What a cartridge's ROM says about its save, from the SDK string the build embeds. */ export interface CartridgeSave { /** null when the ROM declares nothing, as homebrew usually does */ type: SaveType | null; /** the string as it stands in the ROM (`EEPROM_V121`, `FLASH1M_V103`), so a message can name it */ id: string | null; } export declare class GbaSystemBus implements MemoryBus { #private; /** External Work RAM (256 KB) */ readonly ewram: Uint8Array; /** Internal Work RAM (32 KB) */ readonly iwram: Uint8Array; /** Palette RAM (1 KB) */ readonly palette: Uint8Array; /** Video RAM (96 KB) */ readonly vram: Uint8Array; /** Object Attribute Memory (1 KB) */ readonly oam: Uint8Array; /** Game Pak SRAM (64 KB) */ readonly sram: Uint8Array; /** Display control registers (written via MMIO, read by PPU) */ readonly mmioRegisters: Uint8Array; /** Callback when BG2/BG3 reference point registers are written (for PPU ref point reload) */ onBgRefPointWrite?: (bgIndex: 2 | 3, isX: boolean) => void; /** Observer for every write into the I/O register file (an event log's MMIO rows). */ onMmioWrite: ((address: number, value: number, size: 1 | 2 | 4) => void) | null; /** Attribute subsequent committed writes to a DMA channel (called by the DMA controller). */ setDmaSource(channel: number, origin: WriteOrigin): void; clearDmaSource(): void; /** * Register a write watchpoint over [address, address+length); returns a disposer. * `length` is clamped to >= 1. */ addWriteWatchpoint(address: number, length: number, onWrite: (info: WatchpointWrite) => void): () => void; /** Remove every registered write watchpoint. */ clearWriteWatchpoints(): void; /** * Register a read watchpoint over [address, address+length); returns a disposer. * Fires after the load, with the value it returned. Every load through the bus * counts, the CPU's instruction fetch included; a debugger's `peek` does not. */ addReadWatchpoint(address: number, length: number, onRead: (info: WatchpointRead) => void): () => void; /** Remove every registered read watchpoint. */ clearReadWatchpoints(): void; /** Whether any write watchpoint is registered (hot-path gate). */ hasWatchpoints(): boolean; /** Whether any read watchpoint is registered (hot-path gate). */ hasReadWatchpoints(): boolean; /** Wire up subsystem references */ connect(parts: { interrupts: InterruptController; timers: TimerController; dma: DmaController; input: InputController; apu: Apu; }): void; /** Load BIOS ROM data */ loadBios(data: Uint8Array): void; /** Write a 32-bit value to the BIOS region (for installing HLE stubs) */ writeBios32(address: number, value: number): void; /** Load Game Pak ROM data */ loadRom(data: Uint8Array): void; /** What the cartridge's ROM declares about its battery-backed save. */ get save(): CartridgeSave; /** * How many bytes the cartridge's EEPROM holds — 512 for 4 Kbit, 8192 for 64 Kbit — * or 0 while nothing has said which of the two it is. */ get eepromSaveBytes(): number; /** * The cartridge's battery-backed memory, whole, in the byte order a `.sav` file uses: * the EEPROM for an EEPROM cartridge, the SRAM window for every other kind. A copy — * unlike a read through the bus, this clocks no serial protocol. Null when the ROM * declares no save, because then there is no chip to read. */ readBackup(): Uint8Array | null; /** * Install a `.sav` as the cartridge's battery-backed memory, filling what `bytes` does * not reach with the value an erased chip holds. Which files belong in which chip is * settled before here: this refuses only what it cannot hold at all. */ writeBackup(bytes: Uint8Array): void; /** * Debugger read: `length` bytes starting at `address`, taken from the backing * arrays without any of the bus's side effects (an EEPROM read through the bus * clocks its serial protocol; this never does). `readable` counts the leading * bytes that map to something; the rest of `data` is zero and must not be shown * as memory contents. Mirrors resolve to their canonical bytes. MMIO is decoded * the way a CPU read would see it, which for the registers modelled here is * side-effect free. */ peek(address: number, length: number): { data: Uint8Array; readable: number; }; /** * Debugger write: store `bytes` at `address` in the backing arrays, bypassing the * hardware's write rules (a byte write to OAM is dropped by the bus, to VRAM it is * duplicated; a hex editor means the byte it typed) and without notifying data * watchpoints. MMIO goes through the bus so the register's side effects apply. * BIOS, ROM and EEPROM are refused. Returns how many leading bytes were written. */ poke(address: number, bytes: Uint8Array): number; /** * The region this bus decodes `address` to, or `null` when it decodes nothing. * * The `read*` methods below are the HARDWARE interface: they answer every address, * because the console does — an undecoded one reads as open bus, which on real * silicon is a value, not a fault. That is correct for the CPU and wrong for a * human or an agent, who gets a plausible number back from a question that had no * answer. Debug-facing callers use this first so they can refuse instead * (see `ScriptingEngine.read16`/`read32`/`readBytes`). * * "Decoded" is the test, not "distinct". The RAM regions mirror a small store * across their whole 16 MB window, and a read from a mirror is a real read — so * this reports the region rather than claiming the address is a mistake. What it * does catch is space nothing answers for: the holes in the BIOS and I/O regions, * region 0x01, everything from 0x10 up, and an offset past the end of the * cartridge actually loaded. * * Side-effect free — unlike a read, which can advance the EEPROM serial state. */ describeAddress(address: number): { region: string; } | null; read8(address: number): number; read16(address: number): number; read32(address: number): number; write8(address: number, value: number): void; write16(address: number, value: number): void; write32(address: number, value: number): void; /** Serialize to a plain snapshot (excludes bios and rom). */ serialize(): SystemBusSnapshot; /** Restore from a snapshot. BIOS and ROM must already be loaded. */ deserialize(snap: SystemBusSnapshot): void; /** Reset all memory and registers */ reset(): void; } //# sourceMappingURL=system-bus.d.ts.map