SearchA-ZP › Pokémon Mini

Pokémon Mini

2001 Open source · GNU GPL v3 Online

The Nintendo Pokémon Mini (2001) is the smallest cartridge-based game system Nintendo ever made. This is JustBurn's PokeMini emulator, whose portable C core is compiled to WebAssembly so it runs the machine directly in the browser. It is wired into the in-page debugger, where you can single-step the Epson S1C88 (Minx) CPU, set breakpoints and watchpoints, and inspect the whole memory map. It boots the open FreeBIOS and an openly-licensed homebrew demo, so no copyrighted ROM is needed.

Visit the official site ↗

Runs on: Web browser

Pokémon Mini Online Emulator

Play Pokémon Mini using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Bouncing (PokeMini demo)Pokémon MiniNintendo Pokémon MiniopenOpen ⛶

Machines emulated

Chips

Notes

Embedding

PokeMini is JustBurn's Pokémon Mini emulator (GPLv3). Its emulation core is portable C with no SDL dependency, so we compile only that core to WebAssembly with Emscripten and reach it through a tiny hand-written shim (wasm_main.c) that exposes a handful of EMSCRIPTEN_KEEPALIVE entry points — never a per-cycle callback across the JS/wasm boundary.

Boot and load a ROM. The module is built with MODULARIZE, so loading pokemini_core.js defines a PokeMiniModule() factory. Instantiate it, cwrap the shim, copy the .min image into the wasm heap and hand it over:

const Mod = await PokeMiniModule();
const pm_init = Mod.cwrap('pm_init', 'number', []);
const pm_load = Mod.cwrap('pm_load', 'number', ['number', 'number']);
const pm_frame = Mod.cwrap('pm_frame', 'void', []);
pm_init();                                // PokeMini_Create + FreeBIOS + palette
const rom = new Uint8Array(await (await fetch(romUrl)).arrayBuffer());
const p = Mod._malloc(rom.length);
Mod.HEAPU8.set(rom, p);
pm_load(p, rom.length);                    // PokeMini_SetMINMem + hard reset

The shim surface. Everything the debugger needs is a plain C function; the wasm holds all machine state:

ExportWhat it does
pm_frame()Run one whole video frame (PokeMini_EmulateFrame) and blit the 96×64 LCD into an RGBA buffer.
pm_step()Execute exactly one S1C88 instruction (MinxCPU_Exec) then re-sync the timers and PRC. The single-step primitive.
pm_fb()Pointer to the 96×64 RGBA framebuffer inside the wasm heap, painted to a 2D canvas.
pm_read(a) / pm_write(a,v)Side-effect-free linear bus access over BIOS / RAM / I/O latches / ROM — reads never trigger I/O.
pm_pc()The physical fetch address of PC, honouring the S1C88 bank (V) register.
pm_getreg(i) / pm_setreg(i,v)Read / write the register file: A, B, BA, HL, IX, IY, SP, PC, F, N and the bank bytes.
pm_key(k,down)Press or release a Pokémon Mini key (A/B/C, D-pad, Power, Shock) via PokeMini_KeypadEvent.

Debugger integration

Wiring the wasm core into the shared in-browser debugger needed one boot shim that owns the loop, plus a new S1C88 disassembler.

A host-owned loop. Rather than call any built-in frontend, the boot drives the core itself so pause / step / breakpoints work. With no breakpoints or watchpoints it runs a whole frame at once; otherwise it steps one instruction at a time and checks state between instructions:

function runFrame(){
  if (!bps.size && !wps.size) { pm_frame(); return; }   // fast path
  let cyc = 0;
  while (cyc < 55634) {                            // one frame of cycles
    if (bps.has(pm_pc())) { running = false; return; }
    const before = snapshotWatched();
    cyc += pm_step();                             // one S1C88 instruction
    if (watchedChanged(before)) { running = false; return; }
  }
  pm_render();
}

Techniques for deeper access.

  • Side-effect-free reads. The hex and disassembly views read through pm_read, which indexes BIOS, RAM, the I/O latches and ROM directly instead of routing through the hardware read path, so auto-polling a view never disturbs timers or I/O.
  • Direct register state. pm_getreg/pm_setreg read and write the live MinxCPU struct; the flag byte is surfaced as clickable Z/C/V/S/BCD/NIB/I/ID chips.
  • Single instruction step. pm_step executes one instruction and re-syncs the timers and PRC, so one call is one coherent step.
  • Execution breakpoints. Because the loop is host-owned, a breakpoint is a JavaScript Set of physical PC values compared against pm_pc() before each instruction.
  • Write watchpoints. With no per-write hook across the wasm boundary, watched addresses are snapshotted before each instruction and compared after; a change pauses the loop — the same visible behaviour as a store watchpoint.

A new S1C88 decoder. The Pokémon Mini CPU had no shared disassembler, so debugger/src/cpus/s1c88.js was added: the primary opcode page plus the two 0xCE / 0xCF extended pages, ported faithfully from PokeMini's own instruction tables, with .byte fall-back for raw data.

Architecture

The Pokémon Mini (2001) is the smallest Nintendo cartridge system. Its heart is an Epson S1C88-family core (the "Minx"), an 8-bit variable-length CISC CPU running at about 4 MHz, with a 96×64 monochrome LCD, a piezo buzzer, a rumble motor, a real-time clock and a shock detector.

  • S1C88 CPU — registers A/B (paired as BA), the 16-bit index registers HL, X and Y, SP and a PC extended by a bank register; a one- or two-byte opcode encoding with two extended pages.
  • PRC — the Program Rendering Chip draws a tile-map background and sprites into the LCD framebuffer each frame.
  • Timers & IRQ — a bank of hardware timers and a priority interrupt controller drive audio, video and the keypad.
  • Memory map — 4 KB BIOS at $000000, 4 KB RAM (plus I/O latches) at $001000, and up to ~2 MB cartridge ROM from $002100.

This build boots the open FreeBIOS (bundled with PokeMini) so no copyrighted Nintendo BIOS is needed, then loads an openly-licensed homebrew demo. The whole machine lives inside the WebAssembly module, and the debugger reads it each refresh through the shim.