Search › A-Z › G › Game Boy Color
Game Boy Color
This is the Nintendo Game Boy Color (CGB) running directly in your browser, in colour. It is powered by GameBoy-Online, Grant Galitz's pure-JavaScript Game Boy / Game Boy Color emulator: an interpreter of the Sharp SM83 (LR35902) CPU with a scanline picture processor that drives the Color hardware's per-tile palettes, second VRAM bank and double-speed mode. Here it is wired into the in-page debugger, where you can single-step the SM83, set breakpoints, and inspect the whole memory map.
Runs on: Web browser
Game Boy Color Online Emulator
Play Game Boy Color using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| µCity | Game Boy Color | Game Boy Color | open | Open ⛶ |
Machines emulated
Chips
Notes
Embedding
GameBoy-Online (Grant Galitz) is a pure-JavaScript Game Boy / Game Boy Color emulator whose whole machine is one big object, GameBoyCore. Vendor GameBoyCore.js and drive the machine yourself instead of loading GameBoyIO.js: that companion file owns a setInterval loop plus audio and save-state plumbing that cannot be paused or single-stepped, which the debugger needs.
Construct and boot. The core wants the cartridge as a binary string (one character per byte), not an ArrayBuffer. It also reaches for three page globals - settings (an options array), and no-op cout / pause functions, so define them first. Turn sound off (settings[0]=false) and skip the boot ROM (settings[1]=false) so it jumps straight into the game at 00 in Color mode:
var settings = [false, false, false, 1, true, false, 8, 10, 20, false, false, false, false, true, [true,true,true,true]];
function cout(){}
function pause(){}
var u8 = new Uint8Array(romBuffer), s = '';
for (var i=0; i<u8.length; i++) s += String.fromCharCode(u8[i]);
var gb = new GameBoyCore(canvas, s);
gb.start(); // initMemory, ROMLoad, initLCD (draws itself), initSound
gb.programCounter = 0x0100;
The machine is plain fields. Everything the debugger needs is a live property; no wasm heap:
| Member | Kind | What it does |
|---|---|---|
executeIteration() | method | The interpreter loop: runs SM83 instructions until emulatorTicks >= CPUCyclesTotal. Set CPUCyclesTotal=1 and it runs exactly one instruction, the single-step primitive. |
registerA/B/C/D/E | field | The 8-bit registers. H and L are one combined field, registersHL. |
FZero/FSubtract/FHalfCarry/FCarry | field | The four flag bits, kept as booleans rather than packed into F. |
programCounter / stackPointer | field | PC and SP, live 16-bit values. |
memoryReader[a](gb,a) / memoryWrite(a,v) | method | The routed CPU bus, reflects the current ROM/VRAM/WRAM bank, so disassembly follows PC across bank switches. |
memory | field | The raw 64 KB store (current VRAM bank, OAM, I/O, HRAM) as a Uint8Array - side-effect-free reads. |
ROM | field | The whole decoded cartridge as a byte array, a pure ROM view for the disassembler. |
cGBC | field | Color mode flag, set from the cartridge header byte 43 at load. |
JoyPadEvent(code, down) | method | Feed input. Codes: 0 Right, 1 Left, 2 Up, 3 Down, 4 A, 5 B, 6 Select, 7 Start. |
Because the CPU, PPU and memory are ordinary JavaScript, the debugger single-steps with one bounded executeIteration(), reads and writes registers straight off the core, and implements breakpoints and watchpoints as host-side checks around those calls, with no changes to the emulator core.
Debugger integration
Wiring GameBoy-Online into the shared in-browser debugger needed one piece of custom work, a boot shim that owns the loop, plus a set of techniques for reaching into the live machine.
A boot shim, because the loop lives in another file. Upstream, GameBoyIO.js drives the core from a setInterval that cannot be paused or single-stepped. We drop that file and drive the core's own executeIteration() from a loop we control. The trick is that the interpreter loop exits when emulatorTicks reaches CPUCyclesTotal, so we simply choose that budget - a whole video frame to run, or one instruction to step:
// run exactly one SM83 instruction
function stepInsn(){
gb.stopEmulator = 0; gb.emulatorTicks = 0; gb.CPUCyclesTotal = 1;
gb.executeIteration(); // fetch, execute, tick PPU/timers, then stop
}
// run one frame, halting on a PC breakpoint when any are set
function frame(){
if (bps.size){
var budget = 70224;
while (budget > 0){
if (bps.has(gb.programCounter)){ running = false; break; }
stepInsn(); budget -= gb.CPUTicks;
}
} else { gb.stopEmulator=0; gb.emulatorTicks=0; gb.CPUCyclesTotal=70224; gb.executeIteration(); }
gb.requestDraw(); // the core paints its own canvas
}
Techniques for deeper access. The plug-in reaches into the running machine through the core's own surfaces; no fork:
- Composed registers. This core keeps F as four booleans and H:L as one 16-bit field, so the plug-in packs
Z/N/H/Cinto the F byte and splitsregistersHLinto H and L for the register grid, with setters that write straight back. - Side-effect-free reads. VRAM, OAM and I/O are read straight off the raw
memoryarray; the cartridge view reads the decodedROMarray. Only the CPU-bus and banked-WRAM views use the routedmemoryReader, so disassembly follows PC across ROM bank switches. - Single instruction step. Bounding
executeIteration()toCPUCyclesTotal=1runs one instruction with the PPU, timers and interrupts all clocked coherently. - Execution breakpoints. Because we own the loop, a breakpoint is a host-side
Setof PC values checked before each step; when breakpoints are present the frame runs instruction-by-instruction and halts the moment PC matches. - Write watchpoints. The core dispatches writes through a compiled
memoryWriter[]jump table, so the shim wraps the watched entries (re-applying them whenever the table is recompiled on a bank switch) to pause on a store.
Everything the debugger shows, registers read/write, memory hex/disassembly, follow-PC, single-step, breakpoints and watchpoints, is built from these, with no changes to the emulator itself.
Architecture
GameBoy-Online is Grant Galitz's interpreted Game Boy Color emulator. Unlike more object-split cores, almost the entire machine lives on one GameBoyCore instance:
- CPU - a Sharp SM83 (LR35902) interpreter.
executeIteration()fetches through a per-addressmemoryReaderjump table, dispatches a flatOPCODE[]table, and advances the PPU, timers, serial and interrupts inline. It boots at00with the documented post-boot register state (the GBC boot ROM / logo is skipped). - PPU - a scanline renderer with the Game Boy Color's per-tile palettes and a second VRAM bank, compositing background, window and sprites into a 160×144 framebuffer that the core swizzles and blits to its own canvas.
- APU - the four sound channels, resampled to Web Audio when enabled (disabled here for headless stepping).
- Memory - compiled
memoryReader[]/memoryWriter[]jump tables over the cartridge MBC, dual VRAM banks, seven switchable GBC work-RAM banks, OAM, I/O and HRAM, recompiled on bank switches. - Color - the
cGBCflag is read from the cartridge header at43; a CGB cartridge lights up the colour palettes, double-speed mode and the extra RAM banks.
Each frame runs the interpreter for one video frame's worth of clocks, then paints the finished framebuffer. Because the machine is one ordinary object, its whole state is inspectable, which is what the debugger reads each refresh.