FlashEm is an experimental emulator for the VTech V.Flash, also sold as the V.Smile Pro, educational console (2006), as a libretro core and as a standalone program (SDL2), which also serves as the development tool (debugger, headless runs, screenshots).
The emulator runs the console's original boot ROM and each disc's own ARM code.
The ROM boots µMORE, mounts the disc through the emulated CD hardware, and loads
the game's 0SYSTEM/BOOT.BIN. Graphics, audio, and movies come from the emulated
devices; the original movie-decoder firmware runs on a ZSP400 interpreter.
On 64-bit hosts (the libretro cores for Linux, Windows x64, macOS arm64 and
Android arm64-v8a/x86_64, and the standalone emulator) the ARM9 runs on the
dynarmic JIT recompiler, with the interpreter for what it gives back (mode
changes, CP15, exceptions); the core option CPU Emulation, or --cpu and
flashem.ini's cpu in the standalone, switches back to the interpreter.
There is no high-level replacement for the operating system or games.
Games now boot, menus respond to input, and 3D gameplay renders. The project is still under active reverse engineering; these results do not establish complete playability across every title, revision, or game mode.
- Boot and disc access: original ROM startup, game-kernel boot, CD servo and decoder, seeking, sector DMA, and streaming-ring protection are implemented.
- Graphics: tile and colour layers, textured sprites and triangles, skeletal animation, lighting, depth, blending, near-plane clipping, and surface copies are implemented in a software renderer. Dingo Rallye reaches its menus and renders the race, kart, and HUD. Other tested titles include Cars, Bratz, Spider-Man, The Incredibles, Disney Princess, Scooby-Doo, SpongeBob, Shrek, Wacky Race, and Multisports.
- Movies: native DSP firmware decodes MJP video into planar YUV, which the video engine composites with game graphics. Validation includes Multisports playback and movie samples from Cars, Spider-Man, SpongeBob, Wacky Race, Shrek, and Scooby-Doo, plus complete-movie and streaming-ring tests.
- Audio: BIOS sounds, PCM music, pitched/looped sample playback, Apple IMA4 effects and movie streams, and the CDDA DMA path are implemented. Both frontends output 44.1 kHz stereo audio.
- Frontends: the libretro core has been run on Linux (x86_64 and aarch64) and Windows. Releases also carry a macOS arm64 build, and .gitlab-ci.yml builds the core with libretro's buildbot templates for Windows, Linux, macOS, Android and iOS; those builds compile, which does not imply equivalent runtime validation.
- Compatibility testing consists of captured scenes and scripted routes, not complete playthroughs. Rendering and controller-register details remain partially inferred.
- CPU/device timing, DSP scheduling, and some CD behavior are approximate. Performance varies by scene and host; full speed is not guaranteed.
- Audio envelopes, finite-duration master-volume ramps, precise gain and interpolation, and sound interrupt generation remain incomplete.
- There are no save states or serialization-based features such as rewind, run-ahead, or netplay. The libretro core also has no reset implementation, disk swapping, cheats, or persistent save-memory interface; its core options are Boot ROM, Console Language and CPU Emulation (JIT or Interpreter). Reload the content to restart it.
- The libretro core currently advertises 60 Hz/NTSC timing even though framebuffer dimensions follow the video-engine registers, including PAL-sized rasters.
The latest release
has the libretro core for Linux (x86_64, aarch64), macOS (arm64), Windows
(x86_64) and Android (arm64-v8a, armeabi-v7a, x86_64, x86; name the one for
your device flashem_libretro_android.so), and flashem_libretro.info. Put the core in your frontend's cores
directory and the info file in its info directory, then follow
Required files.
It also has the standalone emulator for Linux (x86_64,
aarch64, .tar.gz), macOS (arm64, .tar.gz, with SDL2) and Windows (x86_64,
.zip, with SDL2). On Linux it uses the system's SDL2 (libsdl2-2.0-0 on
Debian and Ubuntu).
Supply your own boot ROM (2 MiB) and disc image. Two are known:
70004.bin, the German console's (md5 f19e090723accb3353a7c4bef342af71, menu
in German), and vflash_us.bin, the US console's (md5
60b1ef08a7f3a795d66f2cf41140901b, menu in English; a dump of the console's
A29L160ATV NOR flash - rename it). ROMs, games, and
extracted DSP firmware are not supplied. Normal gameplay loads the DSP firmware
from the game through the emulated hardware; no separate DSP file is needed.
The boot ROM is searched for in this order, 70004.bin first and then
vflash_us.bin at each step, or only the one chosen with the core option Boot
ROM (--boot-rom or flashem.ini's boot_rom in the standalone):
<system>/flashem/<name>, then<system>/<name>, when a libretro frontend supplies its system directory; for the standalone emulator, the directory the program is in.- The path in the
FLASHEM_BIOSenvironment variable. <name>in the current working directory.
Use a raw BIN image with its CUE sheet and open the .cue. Keep the referenced
BIN beside the CUE. The loader reads tracks from a single-file CUE; multi-file
CUE layouts are not implemented. Extract ZIP archives before loading content.
The loader also accepts .bin and .iso paths, but a 2048-byte-sector ISO lacks
the raw EDC/ECC bytes used by the guest's software checks, and the emulator does
not reconstruct them. An ISO opening successfully does not mean it will boot.
Opening a BIN directly also loses the CUE's track metadata.
Run these commands from this repository's directory. The core needs a C compiler with C11 atomics, GNU Make, and the math library, nothing else. Only the standalone development frontend also needs SDL2 development files; normal emulation does not require a host JPEG library.
The core alone, on any platform with GCC or Clang:
make -f Makefile.libretro -jThe sections below also build the standalone frontend and the tools.
On Debian/Ubuntu:
sudo apt install build-essential libsdl2-dev
make -j
make -f Makefile.libretro platform=unix -jmake builds flashem and the command-line tools. The second command builds
flashem_libretro.so. To build only the standalone emulator, use make flashem.
In an MSYS2 MinGW64 shell:
pacman -S --needed make mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2
make platform=win SDL2_PREFIX= -j
make -f Makefile.libretro platform=win -jThe outputs are flashem.exe, the tools as .exe files, and
flashem_libretro.dll. For standalone use outside MSYS2, keep the matching
SDL2.dll beside flashem.exe (available in MSYS2's mingw64/bin directory).
The libretro core does not use that DLL.
sudo apt install build-essential gcc-mingw-w64-x86-64 curl
bash tools/get_sdl2_mingw.sh
make platform=win -j
make -f Makefile.libretro platform=win -jThe SDL helper downloads the MinGW development package to ~/vfg/deps, matching
the standalone Makefile's default. Set SDL2_PREFIX to the package's
x86_64-w64-mingw32 directory to use a different location. This build copies
SDL2.dll alongside the standalone executable.
Standalone Linux and Windows objects use separate suffixes. Libretro objects
all use .lr.o, so run make -f Makefile.libretro platform=<target> clean
before switching core target platforms. Header dependencies are only partially
listed; use a clean rebuild after changing shared structures in headers.
Put the boot ROM (70004.bin or vflash_us.bin) beside flashem (flashem.exe) and start it. With no disc on
the command line it asks for one with the system's file picker (on Linux zenity
or kdialog; without either, give the disc on the command line); cancelled, the
console starts into its menu with the drive empty. A disc image dropped on the
window, or one chosen with O, restarts the console with that disc, as when a
disc is changed with the power off.
The same program is the development frontend - the debugger, headless runs and scripted screenshots below:
./flashem "path/to/game.cue"
./flashem --scale 3 "path/to/game.cue"
./flashem --dbg "path/to/game.cue" # debugger paused at boot
./flashem --dbg-run "path/to/game.cue" # debugger runningWindows PowerShell, with an explicit ROM path:
$env:FLASHEM_BIOS = 'C:\path\to\70004.bin'
.\flashem.exe 'C:\path\to\game.cue'--scale accepts 1–4 (default 2). --help lists the command-line options.
Accelerated boot is enabled by default and discards audio while active. Set
VFLASH_FASTBOOT=0 to run startup at normal pacing. The debugger disables the
standalone frontend's accelerated pacing.
| Keyboard | V.Flash | RetroPad (port 1) |
|---|---|---|
| Arrow keys | Joystick directions | D-pad |
| Z | Red | A |
| X | Yellow | B |
| C | Green | X |
| V | Blue | Y |
| Enter | Enter / OK | Start |
| Backspace | Exit | Select |
| Q / W | Left / right shoulder | L / R |
| Space | Stick button | L3 |
| H / B | Question / book | L2 / R2 |
| 1-4 | A-D | keyboard 1-4 |
| 5 / 6 / 7 | Console: play / stop / forward | keyboard 5 / 6 / 7 |
| 8 / 9 | Console: volume down / up | keyboard 8 / 9 |
| Home / End | Console: power on / off | keyboard Home / End |
The console's power and play LEDs go to RetroArch's LED driver (Settings > LEDs) as LEDs 0 and 1 and to the log; the standalone frontend prints them when they change.
With RetroArch's default keyboard mapping of the RetroPad, that is: arrow keys, X for red, Z for yellow, S for green, A for blue, and Enter.
In the standalone emulator, Tab runs it unthrottled while held (turbo, without
sound), O opens another disc, F5 saves screenshot.bmp, F11 toggles fullscreen,
and Esc quits. F2 pauses/resumes when started with --dbg or --dbg-run.
Some game prompts require a colour button rather than Enter.
The standalone emulator also takes gamepads (SDL GameController), laid out as the
RetroPad column above, with the left stick as the V.Flash stick too and R3 as
turbo. Keys and gamepad buttons, hotkeys included, are rebound in flashem.ini
beside the program, which it writes with the defaults on its first start; delete
it to get them back. Its [console] section sets the console's language, the
boot_rom and the cpu (jit or interpreter), as --language, --boot-rom
and --cpu do for one run. The debugger always runs the interpreter.
Load the built core in a libretro frontend, put the boot ROM (70004.bin or
vflash_us.bin) in its system directory, and load the game's .cue. The accompanying
flashem_libretro.info provides frontend metadata.
The core opens content by filesystem path (need_fullpath) and requires a disc
path. It supplies software-rendered XRGB8888 video at a 4:3 display aspect and
44.1 kHz stereo audio. Framebuffer dimensions follow the guest's video settings,
up to 512×288; games commonly use 512×240. Input currently comes from the first
RetroPad's D-pad and buttons, as mapped above.
The V.Flash uses an LSI Logic ZEVIO 1020 with an ARM926EJ-S CPU at 150 MHz, 16 MiB SDRAM, and CD-ROM media. Its boot ROM and games run the µMORE RTOS.
| Source | Responsibility |
|---|---|
| src/arm9.c, src/cp15.c | ARM/Thumb interpreter and MMU |
| src/jit.cpp, tools/dynarmic.patch | The ARM9 on dynarmic (external/dynarmic, built by tools/build_dynarmic.sh, make -f Makefile.libretro JIT=1) |
| src/hw.c | Memory map, timers, interrupts, GPIO, UART/controller reports, CD decoder, and video scanout |
| src/cdrom.c, src/cdsp.c | Disc images, track/subcode data, and CD servo model |
| src/ge.c | Command-list graphics engine and software rasterizer |
| src/zevio_dsp.c, src/zsp400.c | DSP DMA/mailboxes and ZSP400 instruction execution |
| src/midi.c, src/cdda_dma.c | Sound voices, PCM/IMA4 playback, and CDDA transfers |
| src/audio.c | Audio buffering and frontend output |
| src/vflash.c | Frontend-facing machine API and ROM lookup |
| src/main.c, src/flashem_libretro.c | SDL2 and libretro frontends |
The graphics command engine is at 0xA8000000; display composition is handled
by the video engine at 0xB8000000. Other major devices include sound at
0xB0000000, memory DMA at 0xBC000000, DSP DMA at 0xC0000000, and the CD
decoder/servo interface at 0xC4000000.
Reverse-engineering evidence and unresolved register behavior are documented next to the device models. CLAUDE.md contains more detailed working notes, including historical findings and references to local research fixtures.
For a bounded headless run on Linux/WSL:
FLASHEM_BIOS=/path/to/70004.bin \
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
VFLASH_EXIT=3601 VFLASH_SHOT_FRAME=1800,3600 \
VFLASH_SHOT='frame-%d.ppm' \
./flashem --headless /path/to/game.cue > run.log 2>&1--headless skips the window but still initializes SDL video/audio; the dummy
drivers allow runs without display or audio devices. Choose a writable screenshot
path explicitly, especially on Windows. Headless runs continue until an exit
condition is supplied.
| Variable | Effect |
|---|---|
VFLASH_LOG=<file> |
Redirect standalone stdout after initialization; shell redirection also captures startup output and stderr |
VFLASH_EXIT=N |
End the process at the specified emulated frame |
VFLASH_SHOT_FRAME=N[,N...] |
Headless screenshot frames in ascending order; default 50 |
VFLASH_SHOT=<path> |
Headless PPM path; %d inserts the frame number; default /tmp/vflash_screen.ppm |
VFLASH_FASTBOOT=0 |
Disable accelerated boot |
VFLASH_JIT=0 |
Run the ARM9 on the interpreter (as the core option or --cpu interpreter) |
VFLASH_RTC=<unix-seconds> |
Fix the RTC's starting time for reproducible runs |
VFLASH_INPUT=<hex>@<from>-<to>[;...] |
Script frontend button masks; e.g. 100@3100-3110 presses Enter |
VFLASH_KEYS=<hex>@<from>-<to> |
Force raw key-register bits, distinct from controller reports |
VFLASH_RAMDUMP=<file> |
Dump SDRAM and palette RAM (<file>.sram) at VFLASH_RAMDUMP_FRAME (default 600) |
VFLASH_TRACEPC=<hex>[:<hex>...][,N] |
Trace registers at selected instruction addresses, with an optional hit limit |
VFLASH_IOHIST=<from>,<to>[,page] |
Collect MMIO access counts by address and PC |
VFLASH_CDLOG=1 |
Trace CD servo/decoder activity |
VFLASH_UART0_LINK=<path> / VFLASH_UART1_LINK=<path> |
Tie that UART to a pseudo-terminal on the host, raw both ways, and make a link to it at <path> (1: no link, the name is only printed). Not on Windows |
VFLASH_GELOG=1 |
Report unhandled graphics commands |
VFLASH_GECAP=<file>,<frame>[,<frames>] |
Capture graphics state, RAM, and command-list inputs for replay |
VFLASH_INPUT uses the button masks in src/vflash.h: directions
01/02/04/08, red/yellow/green/blue 10/20/40/80, and Enter 100 (hex).
Other device-specific tracing switches are documented in the source.
A program that talks to a host over the console's UART - a debug stub, a monitor - can be reached from the host as if over a serial cable. With VFLASH_UART0_LINK=/tmp/vflash-uart0 (or VFLASH_UART1_LINK for the second UART) the emulator makes a pseudo-terminal, ties the UART to it and links it at that path; the terminal's own name (/dev/pts/N, different every run) is printed as [UART0] link: .... Point the host tool at the path where it would take /dev/ttyUSB0:
VFLASH_UART0_LINK=/tmp/vflash-uart0 ./flashem --load program.elf &
picocom /tmp/vflash-uart0The link is raw: every byte the program sends goes out, every byte that comes in is received, and the baud rate set on either side is ignored. Bytes from the host wait in the terminal until the UART has room, so a program that polls loses none. The controller, which normally sits on UART0, is disconnected from a linked UART. The lines the program prints are still logged as without the link. Linux and macOS only.
Start with --dbg or --dbg-run, then enter commands in the terminal:
s [N] step N instructions c continue
n step over b <addr> set breakpoint
bc <a>|all clear breakpoint(s) bl list breakpoints
r registers pc current instruction
d <a> [N] disassemble m <a> [N] memory dump
bt stack dump setreg r0=1 write register
q quit
The default standalone build includes disc/asset utilities and a graphics replay tool:
./disc_analyze game.iso
./disc_compare first.iso second.iso
./mjp_extract game.iso frames/
./ptx_extract game.iso images/
python tools/output_bin_extract.py OUTPUT.BIN out/
./gereplay capture.bin output.ppmThe disc/asset utilities above operate on ISO payload images; they do not share
the emulator's full CUE/BIN loader. MJP extraction is a research utility, separate
from native DSP playback. tools/output_bin_extract.py unpacks a game's OUTPUT.BIN
(a table of sector, size and path, then the files: pictures, voices, music, movies),
and with ffmpeg on the PATH turns its music, AIFF-C in Apple's IMA4 codec, into WAV.
testrom_gen is also built; make testrom.bin generates its synthetic test ROM.
Focused C tests live in tools/, covering DSP instructions and DMA,
firmware integration, video composition, graphics depth, CD streaming guards,
PCM/IMA4 audio, volume, and frontend buffering/pacing. There is no unified
make test target. Some tests need user-supplied firmware, movie files, captured
RAM, or independent decoded references; read each test's source for its inputs.
For a small test without proprietary fixtures:
cc -std=c11 -O2 tools/test_zsp400.c src/zsp400.c -o /tmp/test_zsp400
/tmp/test_zsp400To check a Linux libretro build without a full frontend:
cc tools/retro_smoke.c -Isrc -o /tmp/retro_smoke -ldl
/tmp/retro_smoke ./flashem_libretro.so /path/to/game.cue /path/to/system 600 /tmp/retro.ppmretro_smoke also supports Windows DLLs. The audit/capture shell scripts in
tools/ are research helpers with local paths and fixture assumptions; adapt
them before use. Graphics captures depend on the current ge.h layout and must
be recreated when that layout changes.
MIT — see LICENSE. src/libretro.h comes from the libretro project
and retains its own MIT notice.