@ta-torres/gbc-core
v0.2.0
Published
Emulator core for the Game Boy and Game Boy Color systems.
Readme
@ta-torres/gbc-core
Framework-independent emulation core for Game Boy and Game Boy Color systems. It loads ROM bytes, runs the CPU and hardware subsystems, and exposes the framebuffer, audio samples, input, cartridge saves, and emulator state through a TypeScript API.
The core does not depend on React or browser APIs. File loading, frame scheduling, canvas rendering, persistent storage, and audio playback are the responsibility of the framework using it.
@ta-torres/gbc-react provides the interface with features built in.
Install
npm install @ta-torres/gbc-coreUse
import { GBCEmulator } from "@ta-torres/gbc-core";
const emulator = new GBCEmulator();
const loaded = emulator.loadROMBytes(
await fetch("/game.gb").then((res) => res.arrayBuffer()),
);
if (!loaded) {
throw new Error(emulator.getErrorMessage() ?? "Unable to load ROM");
}
emulator.start();
// Call from your application's frame loop.
emulator.stepFrameCycle();
const framebuffer = emulator.getFramebuffer();Use pressButton() and releaseButton() for input. Applications can consume
audio with consumeAudioSamples(), inspect cartridge metadata with
getCartridgeHeader(), and persist SRAM or save states with the snapshot
methods.
Framebuffer and emulation loop example
This is a draft that copies
the stable framebuffer into an ImageData buffer and advances emulation at the
Game Boy's clock rate:
const canvas = document.querySelector("canvas");
const context = canvas?.getContext("2d");
if (!canvas || !context) throw new Error("Canvas is unavailable");
canvas.width = 160;
canvas.height = 144;
context.imageSmoothingEnabled = false;
const image = context.createImageData(160, 144);
const pixels = new Uint32Array(image.data.buffer);
const frameMs = 1000 / (4194304 / 70224); // Total cycles / Cycles per frame
let previousTimestamp: number | null = null;
let accumulatedTime = 0;
const loop = (timestamp: number) => {
if (previousTimestamp === null) previousTimestamp = timestamp;
const elapsed = timestamp - previousTimestamp;
previousTimestamp = timestamp;
accumulatedTime += elapsed;
while (accumulatedTime >= frameMs) {
emulator.stepFrameCycle();
accumulatedTime -= frameMs;
}
if (emulator.hasFrameReady()) {
pixels.set(emulator.getFramebuffer());
context.putImageData(image, 0, 0);
}
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);Public API
Lifecycle and execution
| Method | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| loadROMBytes(data) | Load an ArrayBuffer or SharedArrayBuffer, detect the hardware mode, and reset the emulator. Returns false on invalid ROM data. |
| start() / pause() / stop() | Start, toggle pause, or stop emulation. |
| reset() | Reset the CPU and all hardware to the post-boot state. |
| stepInstruction() | Execute one CPU instruction and advance the peripherals. |
| stepFrameCycle() | Execute approximately one frame of emulation at the configured speed. This is the method that gets called from the frame loop |
| setSpeedMultiplier(value) | Set speed from 0.25 (quarter speed) to 4 (quadruple speed). |
| getSpeedMultiplier() | Read the current speed multiplier. |
| isRunning() / isPaused() | Read the current lifecycle state. |
| isCGBMode() | Check whether the loaded ROM is running in Game Boy Color mode. |
| getTicks() | Read the cumulative base-cycle counter since the last reset. |
| getErrorMessage() | Read the most recent ROM-loading error, if any. |
| getCPUState() | Get a formatted CPU register and execution-state string for debugging. |
Video and input
| Method | Description |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| getFramebuffer() | Return the stable 160 × 144 Uint32Array framebuffer. |
| hasFrameReady() | Return true once when a new frame is available. |
| getTileViewerData() / getSpriteTileViewerData() | Return raw tile data for debugging or tooling. |
| setDMGColorPalette(palette) / setCGBColorPalette(palette) | Change the output color palette. |
| pressButton(button) / releaseButton(button) | Press or release right, left, up, down, a, b, select, or start. |
Audio, cartridge data, and state
| Method | Description |
| ------------------------------------------------------------- | ----------------------------------------------------------------- |
| setAudioEnabled(enabled) | Enable or disable audio output. |
| consumeAudioSamples(frameCount) | Drain audio samples as a Float32Array. |
| setAudioConfig(config) | Update audio settings, including per-channel mute options. |
| getCartridgeHeader() | Read parsed cartridge metadata. |
| getSaveKey() | Get the storage key associated with battery-backed SRAM. |
| getSRAMSnapshot() / loadSRAMSnapshot(data) | Read or restore battery-backed cartridge RAM. |
| hasSRAMBeenWrittenTo() / clearSRAMWriteFlag() | Detect and clear pending SRAM changes. |
| takeSnapshot() / restoreSnapshot(snapshot) | Capture or restore a complete emulator save state. |
| getSaveStateKey() | Get the storage key associated with the loaded ROM's save states. |
| hasRTC() / getRTCSnapshot() / loadRTCSnapshot(snapshot) | Read or restore MBC3 real-time clock state when supported. |
| advanceRTCTime(elapsedMs) | Advance a supported MBC3 real-time clock by elapsed milliseconds. |
| getRTCSaveKey() | Get the storage key associated with MBC3 real-time clock state. |
Build
To build the packages from the source repository:
git clone https://github.com/ta-torres/GBC-TS.git
cd GBC-TS
npm install
npm run build:packages
# Pack both the emulation core and UI after making changes to the source code
npm run packTo build only this package, run npm run build from packages/gbc-core.
Repository
Source code is available in the GBC-TS repository.
