A universal Windows CE emulator: a virtual ARM hardware platform that boots real CE and Windows Mobile ROMs on modern Windows.
Warning
Early stage. There are some bugs and boards are just MVP implementations. Some boards lack proper clocks, timings, caches, etc. - take into account. Today this is rather proof-of-concept. Contributions are welcome!
Tip
Stock touch input is misbehaving in some devices/requires some additional effort. If your clicks do not register, try holding the left button and wiggling the cursor a bit.
The easiest way to run CERF is launcher.exe — a GUI app shipped next to cerf.exe that downloads publicly available ROM bundles and boots them. Pick a device from the list, tweak launch options (resolution, logging, network) if you want, click Launch CERF.
For direct invocation without the launcher:
| Command | Action |
|---|---|
cerf.exe |
Boot default device (cerfos) |
cerf.exe --device=devemu_ce6 |
Boot specific device |
cerf.exe --log=ALL |
Enable every log channel |
cerf.exe --flush-outputs |
Force-flush logs (avoid truncation on crash, extremely slow) |
Logs are written to cerf.log next to the executable. On a fatal crash, every other thread's register state and a top-of-stack snapshot is dumped to cerf.crash.log next to it. Run cerf.exe --help for the full CLI.
Note
cerf.log is quiet by default — only critical CERF / CAUTION lines are written. Pass --log=ALL (or a channel list, e.g. --log=BOOT,JIT,MMU) to turn channels on.
Warning
Experimental and unstable. Guest Additions are opt-in (--guest-additions), off by default. Expect per-device rendering glitches and reduced stability — some guest OSes behave better than others.
Guest Additions mechanism injects pre-built ARM driver, replacing the matching ROM's video driver. The library is fully OS version-agnostic and orchestrates entire set of features along with regular video driver responsibilities.
Pass --guest-additions (or tick the matching launcher option) to enable them.
- 32bpp custom screen resolution (boot CE3 into 4K!)
- Host-accelerated blitting - the driver routes blits to host which performs the full set of graphical operations in native code
- Dynamic screen resolution (CE 4+)
- Shared storage with host
- Mouse pointer driver:
- required to avoid stock touch limitations on custom resolutions
- guest OS cursor shape translated directly into host graphics
- scroll wheel support on newer CE
Warning
Touch breaks at non-native resolution. The board's touch peripheral still uses the device's original input driver, which expects the original screen dimensions. With guest additions enabled, the main default input is the regular mouse cursor emulator that every (maybe) OS supports. In case if you need to go back to original touch interface, use the runtime switcher in Actions menu or in status bar. However it might be really corrupted on custom resolutions. E.g. iPaq H3600 devices seem to allow you to run calibration app only through stock stylus - the single app ignores the mouse pointer input.
Each device under devices/<name>/ contains a Windows CE ROM image (*.nb0 or *.bin) and each device declares an optional cerf.json describing itself and (optionally) overriding board / network / rom defaults:
{
"meta": {
"device_name": "Microsoft Device Emulator (Windows Mobile 5 Pocket PC)",
"board_name": "Device Emulator",
"soc_family": "Samsung S3C2410 (ARM920T)",
"os": { "name": "Windows Mobile", "ver_major": 5, "ver_minor": 0 },
"device_year": 2005
},
"board": {
"configurable_screen_width": 800,
"configurable_screen_height": 600
},
"rom": {
"primary": "NK.bin",
"extensions": "EXT.bin",
"recovery": "Recovery.bin"
}
}meta is informational (device identification for the launcher / status displays). board is only honoured by BSPs with a configurable screen resolution (today only Device Emulator boards). rom is only needed when a device ships more than one partition; single-ROM devices auto-detect the *.nb0 / *.bin.
See device_config.h for the full schema.
To determine what is the board, CERF looks inside of ROM and performs heuristic search by module names or binary blobs. CERF also replaces entire bootloader, therefore e.g. Zune 30 can boot OS without HDD (tho OS actually will hang without HDD), but in reality it seems that the bootloader spins the HDD and boots NK.BIN from HDD. Our synthed Zune 30 HDD lacks NK.BIN entirely.
Requires Visual Studio 2026 with the C++ desktop development workload.
Note
First build on a fresh machine takes 1+ hour. vcpkg compiles dependencies from source before CERF starts linking. This happens once per machine — subsequent builds reuse the cached vcpkg_installed/ tree and finish in a few minutes. Do not interrupt the first build.
Initialise source/dependency submodules:
git submodule update --init --recursive
Build via the helper script:
powershell -ExecutionPolicy Bypass -File build.ps1
Or invoke msbuild directly:
msbuild cerf.sln /p:Configuration=Release /p:Platform=Win32
- QEMU
- The Linux kernel
- nlohmann-json
- libslirp
- JIT studied/inspired by Microsoft's Device Emulator (Shared Source Academic License, 2006)
See launcher's boards details database for per-board issues.
| CERF Version | Changes |
|---|---|
| v3.21 |
|
| v3.20 |
|
| v3.11 (For Workgroups) |
|
| Previous versions — see the full changelog. | |
Note
CERF v1 reimplemented CE userspace + kernel in host C++ - coredll exports thunked, rehosted on Win32. It hit a hard ceiling: per-process host resources (GDI handles, atom tables, kernel handles) couldn't hold an entire guest OS. v1 was overengineering hell that literally grew exponentially. v2 is a completely different project. v1's source lives at cerf-v1-obsolete.
Caution
DO NOT USE CERF CODEBASE AS REFERENCE FOR SoCs, BOARDS, PERIPHERALS - AI WRITTEN CODE CAN'T BE TRUSTED!
100% generated by Claude via Claude Code — no human-written code. Not production-grade.


