Caution
CERF v1 IS DEPRECATED — no further development.
Why v1 was abandoned. v1 reimplemented Windows CE's userspace and kernel in host C++ — coredll exports
thunked through to Win32, gwes.exe rehosted on host GDI/USER, kernel objects mapped onto host kernel handles.
That approach hit a hard architectural ceiling: a single host process cannot hold the resources of an entire guest
OS. Per-process Win32 limits (GDI handle table, atom table, kernel handle table, address space) saturate long
before a real CE workload settles, because v1 mocked CE at the user-API layer — above CE's own per-process
isolation — instead of below it, forcing every guest process to share one host process's quotas. The codebase was
also overengineering hell that grew exponentially: every new app surfaced a new untested API path that demanded
another thunk, another fake handle type, another quirk in the rehosted GWES. There is no incremental path from v1
to something that works.
Important
CERF v2 is in active development. v2 is a completely different project: a
virtual ARM hardware platform that boots unmodified ROM binaries (kernel + OAL + drivers + userspace) on x64
Windows. Instead of thunking CE APIs, v2 emulates the SoC silicon — DRAM, MMU, interrupt controller, GPIO, UART,
LCD, timers, ADC — and runs the device's own nk.exe, coredll.dll, gwes.exe, filesys.exe, device.exe and
driver DLLs as their original ARM machine code through a JIT. Per-SoC strategies make it
universal: one emulator is designed to target S3C2410, PXA, OMAP, SA-1110, Poseidon and more, across every CE
generation from Pocket PC 2000 / Windows CE 3 through Windows Mobile 6.x and Windows CE 7 — including platforms
that have no other working ARM emulator today.
Ambitions on the v2 roadmap: universal SoC coverage (SMDK2410 / OMAP3530 / Poseidon / SA-1110, possibly Zune and Windows Phone hardware later), CERF-exclusive guest-side features (guest additions maybe?)
v2 will be published only once it runs well. When the first SoC reliably boots a full ROM to a usable shell, the v2 repository will appear publicly. Until then, this v1 tree is here for historical reference only.
Boot real Windows CE and Windows Mobile ROMs on modern x64 Windows. Run unmodified ARM PE binaries — shells, drivers, apps — with the original operating system brought up around them.
Warning
Early stage. Most things don't work yet. Expect crashes, missing APIs, and broken apps. Treat CERF as an experiment, not a usable emulator.
Successor to wcecl.
A full Windows CE system emulator. Under the hood: an ARM CPU emulator, a reimplementation of the CE kernel and its process/MMU model, and a rehosted windowing subsystem — wired together so real ROM binaries boot end-to-end on top of host Windows.
The entire CE backend — the kernel-mode side plus the system services that coredll.dll, gwes.exe, and device.exe sit on — is reimplemented inside cerf.exe. Init sequence, registry, filesystem, handle table, process/MMU model, driver host: all of it lives in CERF. Unmodified ROM binaries — shells, drivers, apps — run on top exactly as they would on a real device. Windowing and driver-facing surfaces ultimately reach host Win32 primitives, but the CE semantics that sit on those primitives live in CERF itself.
- ARM CPU emulator (block JIT). Every
.exeand.dllon the device runs as its original ARM machine code, translated to x64 on the fly by dynarmic. - Full
gwes.exereplacement. The Graphics/Windowing/Events Subsystem is rehosted on native Win32. Host Win32 windows, messages, and input are bridged back into ARM WNDPROCs through a callback executor — the guest sees real CE gwes; the host sees a well-behaved Win32 app. - Pixel-accurate control rendering and theming.
Button,Static,Edit,ListBox,ScrollBar,Dialog, andMenuship as native fallback WNDPROCs that mirror CE5 gwes down to default-message handling and owner-draw quirks. CE themes render. Renderers are strategies; swapping in a Windows Mobile look or a custom theme is a matter of registering another one. - Kernel, process, and MMU emulation. Per-process 32 MB
ProcessSlotoverlays replicate CE's slot-based address space. Multiple CE kernel versions are supported via pluggable strategies — CE5 and CE6 ship different PSL dispatchers, different TCP-stack plumbing, different shell-folder lookups. More can be added the same way. - PSL (Protected Server Library) cross-process dispatch. When an app calls into a driver, CERF switches address spaces, marshals pointer arguments between caller and callee slots, and runs the handler — exactly as real CE does. Nested
MapCallerPtrworks. device.exemock as a real driver host. ARM driver DLLs (AFD, tcpstk, and friends) load into an emulated driver process with its own slot.DeviceIoControlfans out through PSL with correct slot switching.- Hardware-layer thunks with access levels. Calls that would normally talk to silicon are intercepted at the kernel/COREDLL boundary, with privilege and access enforced inside the thunks themselves rather than faked at a higher layer.
- DLL loader that behaves like
kern.exe. Loads ARM PEs into emulated memory, patches IATs, runs per-processDllMain, tracks module refcounts via per-DLL bitmasks the way real CE does. - Emulated registry, virtual filesystem, handle table, critical sections, thread registry. Enough real kernel state for a real boot.
- Init hive boot sequence. Processes come up from
HKLM\initin dependency order, the same wayfilesys.exebrings up a real device. - OOP rewrite (in progress, almost done). No globals, no statics — every subsystem lives on a
CerfEmulatorinstance and is resolved through a service locator. The payoff: twoCerfEmulators inside onecerf.exeshare nothing and can boot different devices side by side.
Device behaviour is selected at boot from a profile under bundled/devices/<name>/ — a directory containing the ROM filesystem, registry hive, and a device-specific cerf.ini. The .ini picks named strategies for subsystems that differ between CE versions:
psl_dispatch = ce5_psl | ce6_psl
sh_thunk = ce5 | wm5_psl
Adding a new device is a config-and-assets drop — no code changes are needed when the strategies it requires already exist.
| Profile | Status |
|---|---|
wince5 |
Largely supported; the primary development target. IE works. Many APIs still missing. MMU and process isolation may have bugs. |
wm5 |
Bootable. WIP. Anything past the today screen is broken. Expected to reach CE5-level stability over time since both share the same base OS. Reconstructed from a B000FF baked-memory ROM via extract-wince-rom into regular PE files with IATs. |
wince6 |
Bootable without drivers. Needs its own PSL/MMU/isolation strategy (the CE7 strategy is likely a drop-in fit). Only basic apps are expected to work at present. |
wince7 |
Bootable without drivers. Needs its own PSL/MMU/isolation strategy. Only basic apps are expected to work at present. Boots with the gwe_api_sets_ce7_service.cpp workaround enabled. |
cerf.exe # Boot default device (WinCE 5 desktop)
cerf.exe --device=wm5 # Boot Windows Mobile 5
cerf.exe --device=wince6 # Boot WinCE 6.0
cerf.exe --device=wince7 # Boot WinCE 7.0
cerf.exe --flush-outputs # Force-flush logs (avoid truncation on crash)
Logs are written to cerf.log by default. On a fatal crash, register state and a top-of-stack snapshot for every other thread is dumped to cerf.crash.log next to it through a lock-free emergency writer (no ucrt, no mutexes) — useful when the reader thread crashes but a different thread is the actual writer. Run cerf.exe --help for the full CLI.
Tip
For fastest runtime, pass --log=none (or at minimum --no-log=trace). CERF ships with every log category on by default because the project is early-stage and logs are the primary debugging tool — every category on the hot path writes to cerf.log through a serialized critical section, which dominates a real workload. Disabling logging is the main per-run performance lever; turn it back on (or narrow it to --log=EMU,API,...) when diagnosing a crash or investigating behaviour.
Requires Visual Studio 2022 or 2026 with the C++ desktop development workload.
Note
First build on a fresh machine takes 1+ hour. libslirp, glib, and their transitive dependencies are compiled from source by vcpkg before CERF itself 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 assuming it's stuck; watch the msbuild output for vcpkg restore progress.
Initialise submodules:
git submodule update --init --recursive
Build via the helper script (preferred — locates MSBuild via vswhere, kills stale build processes, and reports the produced binary):
powershell -ExecutionPolicy Bypass -File build.ps1
Or invoke msbuild directly:
msbuild cerf.sln /p:Configuration=Release /p:Platform=x64
Unit and integration tests use gtest and live under tests/, grouped by subsystem (e.g. tests/mmu/). They build into a single cerf_tests.exe alongside cerf.exe:
build/Release/x64/cerf_tests.exe
Tests share one in-process CerfEmulator brought up with --ephemeral-registry --disable-network, so the suite runs in well under a second and does not touch the device tree.
python e2e_tests/run_all.py
Each test writes its own log file; see the test sources for paths.
Each device profile lives in bundled/devices/<name>/. To target a new platform build, drop in the ROM filesystem and registry hive, write a cerf.ini that picks the matching strategies, and boot. Strategies self-register into the service locator and are selected by name from cerf.ini, so adding one touches no existing files. The sh_thunk subsystem (cerf/coredll/sh_thunks_ce5_service.cpp, cerf/coredll/sh_thunks_wm5_service.cpp) is the reference example to follow when implementing a new strategy.
No formal docs yet. Start with CLAUDE.md and agent_docs/subsystems.md.
CERF depends on two external runtime libraries:
- dynarmic — ARM → x64 JIT that executes the guest's machine code. CERF tracks its own fork of azahar-emu/dynarmic.
- libslirp — user-mode TCP/IP stack behind the virtual NDIS miniport (DHCP, DNS, TCP, UDP, IPv4, IPv6 via SLAAC). No admin or driver install required; pulled in automatically via vcpkg on first build.
CERF is an experimental project. Active development is expected to wind down once these milestones are reached:
- Windows Mobile 5 boots and is usable.
- Networking works in CE5 and WM5, ideally across the other device profiles too.
- DOOM runs.
The entire codebase was generated by Claude via Claude Code with no human-written code. It is not production-grade: there are likely bugs, shortcuts, and possibly fundamental issues in the emulation layers. Style and patterns also drift between files at this scale, with load-bearing invariants that live in prompts rather than asserts — debugging is rougher than a human-written codebase of comparable size. The project is nonetheless held to emulator-grade quality standards: faithful CE behaviour is the point, not a tech demo. The work began as an experiment in what Claude can produce in a real systems-programming setting and as a way to revive the wcecl concept end-to-end.

