Search › A-Z › D › Sega Dreamcast
Sega Dreamcast
An in-browser Sega Dreamcast CPU emulator. The Dreamcast (1998) was Sega's last console and the first of the sixth generation; its brain is the Hitachi SH-4 (SH7091), a 200 MHz 32-bit RISC with a fixed 16-bit instruction word, sixteen general registers and an on-chip vector FPU. The full browser Dreamcast core (libretro flycast) needs the copyrighted Dreamcast BIOS and WebGL to boot, so this page instead runs a from-source SH-4 interpreter compiled to WebAssembly with Emscripten, exposing a block of sampling hooks over its architectural state. The shared debugger therefore gets genuine live SH-4 registers (R0-R15, PC, PR, SR, GBR, VBR, MACH/MACL, FPUL and FR0-FR15), side-effect-free memory, a real single-instruction step, and execution breakpoints plus write watchpoints. It boots an openly-licensed SH-4 demo that draws an animated framebuffer and reads the Dreamcast controller pad, so the picture is never black and input is visible.
Flycast — the reference Dreamcast core ↗
Runs on: Web browser
Sega Dreamcast Online Emulator
Play Sega Dreamcast using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| SH-4 XOR plaid (homebrew demo) | Sega Dreamcast | Sega Dreamcast | open | Open ⛶ |
Machines emulated
Chips
Notes
Embedding
The Dreamcast's CPU is the Hitachi SH-4 (the SH7091), a 32-bit RISC with a fixed 16-bit instruction word. The stock in-browser Dreamcast core is libretro flycast built through RetroArch's Emscripten toolchain — but it needs the copyrighted dc_boot.bin/dc_flash.bin BIOS to boot, needs WebGL2 and a user gesture (unfriendly to headless verification), and its SH-4 dynarec (xbyak, an x86 JIT) can't target WebAssembly and doesn't offer a clean single-instruction step. So this page instead runs a from-source SH-4 interpreter we wrote in C and compiled to WebAssembly with Emscripten. It self-hosts everything (no CDN): sh4dc.js / sh4dc.wasm (5.9 KB) and the bundled program.
// sh4dc.js is a MODULARIZE=1 factory; instantiate it, then drive it.
var sh4 = await Module({ locateFile: f => SRC + f }); // finds sh4dc.wasm
sh4._sh4_init(); // load the bundled SH-4 program at 0x0C000000, reset
sh4._sh4_run(1000000); // run ~one screen worth of SH-4 instructions
Rendering is manual. The SH-4 program rasters a 320×240 RGB565 framebuffer into main RAM at 0x0C010000. Each animation frame we take a heap view over it, expand every 16-bit pixel to RGBA, and blit it scaled to the visible canvas:
var fb = new Uint16Array(sh4.HEAPU8.buffer, sh4._sh4_fb_ptr(), 320*240);
for (var i=0; i<fb.length; i++){ var p=fb[i];
rgba[i*4]=(p>>11&31)<<3; rgba[i*4+1]=(p>>5&63)<<2; rgba[i*4+2]=(p&31)<<3; rgba[i*4+3]=255; }
Input is a single 16-bit word written to the controller latch at 0x0C00FF00 once per frame with _sh4_set_input(); the SH-4 program reads it and folds it into the picture, so a held button visibly shifts the colours. The bit layout mirrors a Dreamcast digital pad: D-pad + A/B/X/Y + L/R triggers + Start.
Debugger integration
This is a WebAssembly core, yet it gets the same live debugger as the pure-JavaScript machines — real SH-4 registers, side-effect-free memory, a real single-instruction step, plus execution breakpoints and write watchpoints. Because we wrote the core, the debug surface is a first-class part of it rather than a bolt-on.
1 · The SH-4 decoder. A new debugger/src/cpus/sh4.js registers the sh4 decoder: it fetches the little-endian 16-bit word and decodes the whole instruction set (MOV/ADD/SUB/AND/OR/XOR/CMP, the @Rn / @Rn+ / @-Rn / @(disp,Rn) / @(R0,Rn) / @(disp,PC) / @(disp,GBR) load/store family, BRA/BSR/BT/BF/BT.S/BF.S/BRAF/BSRF/JMP/JSR/RTS with their delay slots, the shifts/rotates, and the FPU FADD/FMUL/FMOV…). Anything unrecognised becomes a 2-byte .word.
2 · The KEEPALIVE sampling API. The C core exports a block of EMSCRIPTEN_KEEPALIVE functions the debugger calls ~10×/second while open, and once per Step — never per cycle:
// sh4core.c — sampling only, no hot-path hook
EMSCRIPTEN_KEEPALIVE uint32_t sh4_get_reg(int i){ return R[i&15]; } // R0-R15
EMSCRIPTEN_KEEPALIVE uint32_t sh4_get_ctl(int i){ /* PR,SR,GBR,VBR,MACH,MACL,FPUL,FPSCR */ }
EMSCRIPTEN_KEEPALIVE uint32_t sh4_get_fr(int i){ /* FR0-FR15 as raw bits */ }
EMSCRIPTEN_KEEPALIVE uint32_t sh4_read8(uint32_t a){ return ram[a & RAM_MASK]; } // side-effect-free
EMSCRIPTEN_KEEPALIVE void sh4_step(void){ // execute exactly ONE SH-4 instruction
uint16_t op = rd16(PC); uint32_t at = PC; PC += 2; exec_one(op, at); }
Registers are read AND written back (sh4_set_reg / sh4_set_ctl / sh4_set_fr / sh4_set_pc). Memory reads hit plain RAM, so they are inherently side-effect-free — auto-polling the hex/disasm view never disturbs the machine. sh4_step() runs one real instruction (correctly handling SH-4 delay slots for the delayed branches), so PC and the register file change by one instruction per click.
3 · Breakpoints & watchpoints without a hot-path hook. When nothing is armed the loop runs a whole screen fast with _sh4_run(N). As soon as a breakpoint or watchpoint is set, the loop switches to stepping one instruction at a time with _sh4_step(), comparing the live PC against the breakpoint set and the watched byte addresses against their last sampled value, and pausing on a hit. It is a pure host-side check that only runs while a guard is armed, so the core's hot path is never modified.
4 · The controllable loop. We own the frame loop so the transport can pause/step. Pause stops it; Resume restarts it; Step (frame) runs one screen's worth; Step i (instruction) calls _sh4_step(); Reset calls _sh4_init().
Architecture
The Sega Dreamcast (1998) was the first of the sixth-generation consoles. Its CPU is the Hitachi SH-4 (SH7091) running at 200 MHz — a 32-bit superscalar RISC with a fixed 16-bit instruction word, sixteen 32-bit general registers (R0-R15), an on-chip vector FPU, and a 64-bit external bus feeding 16 MB of main RAM at 0x0C000000. Around it sat the PowerVR2 (CLX2) tile-based deferred renderer, the ARM7-driven AICA sound chip, and a GD-ROM drive.
This page emulates the SH-4 core from source. The interpreter models:
- Registers — R0-R15, PC, PR (procedure/return), SR (status, with the T bit), GBR, VBR, MACH/MACL (multiply accumulators), FPUL and FPSCR, and the FR0-FR15 floating-point file.
- Memory — a flat little-endian 16 MB RAM with the SH-4 region bits masked, which is all the bundled program touches. The framebuffer lives at
0x0C010000and the controller latch at0x0C00FF00. - Instruction decode — the SH-4 16-bit set: MOV / arithmetic / logic / compare, the full load/store addressing modes, shifts and rotates, the branch family with delay slots (BRA/BSR/JMP/JSR/RTS/BT.S/BF.S all execute the following instruction before transferring control), the control-register moves, and the core FPU ops.
How to build this exact artefact. Toolchain: Homebrew emscripten 6.0.3 (emcc on PATH).
# 1. assemble the bundled CC0 SH-4 demo -> program.h (a tiny two-pass assembler)
node build_program.js
# 2. compile the SH-4 interpreter + the KEEPALIVE debug surface to wasm
emcc -O2 sh4core.c -o sh4dc.js -sMODULARIZE=1 -sEXPORT_NAME=Module -sALLOW_MEMORY_GROWTH=1 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,HEAPU8,HEAPU16,HEAPU32 -sEXPORTED_FUNCTIONS=_sh4_init,_sh4_run,_sh4_step,_sh4_get_reg,_sh4_set_reg,_sh4_get_pc,_sh4_get_ctl,_sh4_get_fr,_sh4_read8,_sh4_write8,_sh4_fb_ptr,_sh4_set_input # -> sh4dc.js + .wasm
The build is single-threaded (no SharedArrayBuffer) and uses a 2D canvas (no WebGL), so it hosts anywhere and boots headless without a BIOS. It is a from-source SH-4 core, not the full Dreamcast hardware — see the note above on why flycast itself is not vendored here.