chgame_emulator

CHGame Emulator

An emulator for the CHGame handheld — a WCH CH32X035G8U6 (QingKe V4C RISC-V, 48 MHz) driving a 128x128 ST7735S — written in C with SDL3. It runs natively and in the browser (Emscripten).

It is an emulator, not a simulator: it executes the real RISC-V machine code of a compiled game (.bin, .hex or .elf, as the Arduino IDE builds them for the CHGame board) and models the chip’s peripherals and the hardware wired to them. Nothing in it knows anything about any particular game.

Made with the help of Claude (Anthropic).

Not an official CHGame project

This is an independent, unofficial piece of software, not affiliated with or endorsed by the makers of the CHGame. Where the emulator and the hardware disagree, the hardware is right and the emulator has a bug.

What is emulated

USB is present as registers only (the core’s CDC code runs, no host ever enumerates it, so Serial output is dropped exactly as on a board with no PC attached). The microSD slot is not emulated.

Building

Needs CMake and SDL3 (on Windows, MSYS2’s mingw-w64-x86_64-SDL3 works; the exe is then linked statically).

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build

Web

With the Emscripten SDK:

emcmake cmake -S . -B build_web -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build_web

Serve build_web/ over http (browsers do not allow fetch from file://). tools/make_web.sh serve does all of it: builds, copies roms/ beside the page with a games.json for its Games menu, and serves it on http://127.0.0.1:8000/CHGame_Emulator.html with Python. Programs load from:

Saves go to the browser’s IndexedDB.

Running

./build/CHGame_Emulator path/to/game.bin

or drop a file on the window, or press F3.

   
D-pad Arrow keys or WASD, gamepad d-pad or left stick
A / B X or Space / Z — gamepad east / south button
START / SELECT Enter / Right Shift or Backspace — gamepad Start / Back
Help F1
Reset F2
Open F3
Pause / fast forward P / hold Tab
Stats overlay (game fps, speed, MIPS, host load) F9
Screenshot F10
Fullscreen F11 or Alt+Enter
Volume + / -

The game fps in the overlay is counted at the display: a new frame is a write window that starts higher up the screen than the one before it.

Tools

Calibration

tests/sketches/chg_cal is the one the cycle model is built from: 51 assembly loops (bench.S), each once in flash and once in RAM where it matters, whose layout is fixed to the byte — sequential 32- and 16-bit code of two lengths, misaligned branch targets, loads, stores and load-use against RAM, flash and peripherals, store and load pairs, divides, multiplies, branches, and writePixels’ byte swap as gcc builds it. It prints cycles per iteration (x100) over USB serial and keeps them in cal_results[], which chg_headless chg_cal.bin 3 NUL --mem <address of cal_results> 51 dumps from the emulator, so the two can be compared line by line.

tests/sketches/chg_bench is the quick check: nine loops (SPI by hand from flash and from RAM, SPI by DMA, ALU loops in flash and in RAM, divides, flash table loads, the pixel byte swap, delay(100)) timed in microseconds and shown on the screen. chg_bench.bin is prebuilt. On a real CHGame and in the emulator they agree to within 1%.

tests/sketches/chg_flashtest exercises the flash page write the games use for saves: green is a pass, and a white bar is added per run.

Layout

   
src/rv32.c the CPU: decoder, execution, traps, interrupts, HPE
src/bus.c memory map and peripherals
src/st7735.c the display controller and panel
src/audio.c the buzzer, turned into samples
src/machine.c reset and the run loop
src/loader.c .bin/.hex/.elf loading, save files
src/main.c SDL3 front end
web/shell.html the page around the web build

Licence

MIT, see LICENSE. The embedded CHGame bootloader (src/bootloader_image.c) is Kevin Bates’ CH32SerialBoot, also MIT, with its notice in that file. SDL3 is Zlib licensed.