WonderSwan
The WonderSwan is Bandai's handheld games console, designed by Gunpei Yokoi's Koto Laboratory and launched in Japan in 1999, followed by the colour WonderSwan Color. It is built around a low-power NEC V30MZ, a 16-bit CPU compatible with the Intel 80186. This online version runs the Mednafen WonderSwan core (a descendant of Dox's Cygne) compiled to WebAssembly, wired to a live V30MZ debugger with registers, memory, single-step, breakpoints and watchpoints.
Runs on: Web browser
WonderSwan Online Emulator
Play WonderSwan using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| WonderSwan (test ROM) | WonderSwan | Bandai WonderSwan | open | Open ⛶ |
Machines emulated
Chips
Notes
Embedding
Beetle WonderSwan is the Mednafen WonderSwan core (a descendant of Dox's Cygne) in its libretro form. We compile the core to WebAssembly with Emscripten and drive it from a small frontend, wasmfront.c, that replaces RetroArch with the smallest thing that runs the core in a browser and hands its framebuffer, audio and input to the page.
Build (reproducible). In a scratch clone of github.com/libretro/beetle-wswan-libretro, with Homebrew's emscripten on PATH:
emcc -O2 -I. -Imednafen -Imednafen/include -Ilibretro-common/include \
-DWANT_16BPP -DWANT_STEREO_SOUND -DMEDNAFEN_VERSION_NUMERIC=931 \
mednafen/wswan/*.c mednafen/sound/Blip_Buffer.c \
mednafen/mempatcher.cpp mednafen/state.c mednafen/settings.c libretro.c \
wasmfront.c -o wswan.js \
-sMODULARIZE=1 -sEXPORT_NAME=WSWAN -sENVIRONMENT=web \
-sALLOW_MEMORY_GROWTH=1 -sINVOKE_RUN=0 \
-sEXPORTED_FUNCTIONS=_malloc,_free \
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,HEAPU8
Boot. The module is a factory; the frontend wires the libretro callbacks, then the cart is handed to retro_load_game through the frontend's ws_load:
const Module = await WSWAN({ locateFile: f => base + f });
Module.ccall('ws_init'); // wire callbacks + retro_init
const p = Module._malloc(rom.length); Module.HEAPU8.set(rom, p);
Module.ccall('ws_load', 'number', ['number','number'], [p, rom.length]);
(function loop(){ Module._ws_run(); blit(); requestAnimationFrame(loop); })();
Each ws_run() runs exactly one video frame; the frontend converts the core's 0RGB1555 buffer to RGBA for a <canvas>. Input is fed with ws_set_button(id, down), where id is a libretro joypad button, the WonderSwan's two D-pads (X1-X4 and Y1-Y4) plus A, B and Start.
Debugger integration
The interesting part is giving a compiled wasm core the SAME live debugger the JavaScript emulators have, without touching the hot path. The V30MZ's registers live in the core's own C globals (the I struct in v30mz.c), reachable through the core's v30mz_get_reg() / v30mz_set_reg(); RAM is the flat wsRAM[]. wasmfront.c adds a handful of EMSCRIPTEN_KEEPALIVE functions that, when called, read those globals; nothing is polled per cycle:
EMSCRIPTEN_KEEPALIVE int ws_pc(void) { return ((get_reg(NEC_PS) << 4) + get_reg(NEC_PC)) & 0xFFFFF; }
EMSCRIPTEN_KEEPALIVE int ws_reg(int n) { return v30mz_get_reg(n); }
EMSCRIPTEN_KEEPALIVE int ws_ram(int a) { return wsRAM[a & 0xFFFF]; } // side-effect free
EMSCRIPTEN_KEEPALIVE int ws_step(void) { v30mz_step_one(); return ws_pc(); }
The only change inside the core is a single new function next to v30mz_execute() that runs exactly one instruction (prefixes fold into it) so the debugger can single-step:
void v30mz_step_one(void) { WSwan_InterruptCheck(); if(InHLT) return; DoOP(FETCHOP); }
With that, ws_step() advances exactly one V30MZ instruction. The machine plug-in (wonderswan-debug.js) reads the registers through these hooks each refresh, disassembles with the shared x86 decoder (the V30MZ is an 80186-class 8086), and the boot loop implements breakpoints (step instruction-by-instruction, halt when the physical PC matches the set) and watchpoints (snapshot a watched RAM byte each step, halt on change) host-side. All reads are on-demand, so the emulator runs full speed with the debugger closed.
Architecture
The WonderSwan is built around a NEC V30MZ - a low-power 16-bit CPU that is software-compatible with the Intel 80186 (an 8086 with a few extra instructions), running at 3.072 MHz. It is paired with a Bandai/NEC "SPHINX" system-on-chip that provides the display controller, the sound hardware and the timers. Beetle WonderSwan models each piece:
v30mz.c- the V30MZ interpreter: the 16-bit register file (AW/CW/DW/BW, IX/IY, BP/SP, the four segment registers and the x86 flags) and the ModR/M instruction decoder.gfx.c- the display controller: two tile-map layers (background and foreground), 128 sprites and the mono shade-pool / colour palettes, rasterised into the 224×144 framebuffer.wswan-memory.c- the bus: 64 KB of internal RAM, the cartridge ROM banking (segments through I/O ports $C0-$C3) and the I/O port window.sound.c,rtc.c,eeprom.c,interrupt.c- the four-channel sound, the cartridge real-time clock, the internal/cartridge EEPROM and the seven hardware interrupts.
The debugger's memory views expose the full 20-bit CPU address space (disassembled as x86, so the ROM at the top of memory is readable at the reset vector) and the 64 KB internal RAM, where the tile maps, tile data and colour palettes all live.