SearchA-ZG › GBA.js

GBA.js

2013 Open source · BSD-2-Clause Online

GBA.js is a Game Boy Advance emulator by endrift (Vicki Pfau) built entirely in JavaScript using HTML5 Canvas and Web Audio. It runs GBA games directly in a web browser without plugins and predates the author's later mGBA project.

Visit the official site ↗

Runs on: Web browser

GBA.js Online Emulator

Play GBA.js using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Mode 3 Bitmap DemoGBA.jsGame Boy AdvanceopenOpen ⛶
Mode 3 Bitmap DemoGBA.jsGame Boy Advance SPopenOpen ⛶

Machines emulated

Chips

Notes

Embedding

GBA.js is a set of plain-global JavaScript modules (no bundler). Vendor js/** plus the open BIOS replacement resources/bios.bin, load the scripts in dependency order, then drive the machine yourself instead of calling runStable() - its internal setTimeout loop cannot be paused or stepped from outside, which the debugger needs.

Boot. Construct the machine, point it at a 240×160 <canvas>, install the BIOS and a ROM, then run your own loop on advanceFrame() (emulate one video frame):

var gba = new GameBoyAdvance();
gba.setCanvasDirect(canvas);                 // draw straight into a 240x160 canvas
gba.setBios(biosBuffer, false);            // open BIOS replacement (not Nintendo's)
gba.setRom(romBuffer);                       // resets the CPU; entry is 0x08000000
(function loop(){ gba.advanceFrame(); requestAnimationFrame(loop); })();

The machine is plain objects. Every part the debugger needs is a live field; no wasm heap to reach into:

MemberKindWhat it does
gba.advanceFrame()methodEmulate one video frame; steps the ARM core until vertical blank, then draws.
gba.cpu.step()methodExecute exactly one ARM7TDMI instruction. The single-step primitive.
gba.cpu.gprsfieldThe 16 general registers as an Int32Array: R0R12, SP (R13), LR (R14), PC (R15).
gba.cpu.packCPSR() / unpackCPSR(v)methodRead / write the CPSR word (the N Z C V flags, I F masks, T state and mode bits).
gba.mmu.memory[r]fieldThe address space as region views (BIOS, EWRAM, IWRAM, I/O, palette, VRAM, OAM, cart), each with loadU8(off) - a side-effect-free byte read.
gba.mmu.store8/16/32(a, v)methodWrite the bus by full 32-bit address; wrapping these is how watchpoints are implemented.
gba.keypad.currentDownfieldThe 10-bit key state (a clear bit means pressed): A, B, Select, Start, Right, Left, Up, Down, R, L.
gba.freeze() / gba.defrost(s)methodSerialise / restore the whole machine, a save state.

Because the CPU, bus and RAM are ordinary JavaScript, the host page can inspect and control the whole machine at runtime.

Debugger integration

The plug-in gbajs-debug.js reads the live machine from window.EMU_BOOT and hands the shared debugger a description of it; no changes to the emulator core.

Own the loop. Rather than runStable(), the boot script runs its own requestAnimationFrame loop. With no breakpoints set it takes the fast path (advanceFrame() per frame); once a breakpoint exists it single-steps with cpu.step(), checking the program counter before each instruction:

while (!gba.seenFrame) {
  var pc = (cpu.gprs[15] - cpu.instructionWidth) >>> 0;  // undo the prefetch
  if (breakpoints.has(pc)) { running = false; break; }
  cpu.step();
}

Registers. cpu.gprs is read live each refresh and written back through each register's set(). The displayed PC subtracts one instruction width because GBA.js keeps gprs[15] a prefetch ahead. The CPSR is shown as a word plus individual N Z C V I F T flag toggles.

Watchpoints. The boot wraps mmu.store8/16/32 so a write to a watched address pauses the loop. Memory is exposed as one chip per region, each reading through mmu.memory[r].loadU8 (side-effect-free, the I/O region is deliberately left out).

Disassembly uses the shared arm7 decoder, which handles the 32-bit ARM state only. Thumb code (when CPSR.T is set) is not disassembled here; the register panel still shows the T flag so you can see when the core has switched state.

Architecture

GBA.js is a readable, interpreted Game Boy Advance. Each hardware block is its own object hanging off the top-level GameBoyAdvance:

  • cpu - an ARMCore interpreter of the ARM7TDMI, with both the 32-bit ARM (arm.js) and 16-bit Thumb (thumb.js) instruction sets and the banked register file.
  • mmu - the memory map: BIOS, 256 KB EWRAM, 32 KB IWRAM, I/O, palette, 96 KB VRAM, OAM and the cartridge, each a typed-array view behind load/store.
  • video - the LCD controller; a software renderer rasters the tile, bitmap and sprite modes into a 240×160 canvas once per frame.
  • audio - the two PSG channels and two DMA sound FIFOs, mixed through Web Audio.
  • irq, io, keypad, sio, gpio - interrupts and timers, the memory-mapped I/O registers, the buttons, the serial port and cartridge GPIO (e.g. a real-time clock).

The BIOS shipped here is endrift's own open-source replacement (assembled from bios.S, BSD-2-Clause), not Nintendo's - on reset it jumps straight to the cartridge entry point at 0x08000000. Because every component is a plain object, the whole machine is inspectable, steppable and serialisable, which is what makes it a good debugging target.