A custom virtual machine for the Stars 4X game
Engineering a Custom Virtual Machine for Stars!
Originally released in 1995, Stars! is a classic 4X (explore, expand, exploit, exterminate) title designed for 16-bit Windows 3.1. Having first encountered it roughly 28 years ago, I found that while Windows is generally praised for its backward compatibility, actually running Stars! today is easy notoriously difficult.
Because modern x64 Windows cannot execute 16-bit code, players are usually forced to rely on retro hardware or complex emulation layers like otvdm or DOSBox, occasionally layered with Wine.
The Solution: Stars!VM
To solve this, I developed a custom Virtual Machine that integrates an 80286 emulator with a specialized Win16-to-Win32 bridge. The result is a native Win32 experience that preserves the original feel while adding modern luxuries.
Comparison: Original vs. VM Version
| Feature | Original 16-bit Version | Stars!VM (32-bit) |
|---|---|---|
| OS Compatibility | Windows 3.1 / 32-bit Win | Windows x64 / x86 |
| Scaling | Fixed Resolution | 4K Scaling |
| File Dialogs | Legacy Win16 | Modern Win32 |
| Installation | Manual Setup | Single-EXE (Out-of-the-box) |
| I/O Performance | Unbuffered (Slow) | Buffered (Fast) |
The signed GitHub releases bundle a compressed version of the game inside the executable. I opted for 32-bit builds (requiring SSE2) because 64-bit offers no tangible benefit here, and 32-bit ensures maximum compatibility across almost all platforms except 16-bit Windows.
Key Improvements
- No more
Stars.iniclutteringC:\WINDOWS. - Proper integration with the Windows taskbar.
- Neutralized copy protection via the OS bridge.
Pro Tip: If you are new to the game, utilize the comprehensive built-in tutorial, the community wiki, the official strategy guide, or the
AutoHostplay-by-email service. (Note: The game's name is unfortunate given the rise of modern search engines!)
For those wishing to modify the VM, I highly recommend using w64devkit as your build environment.
Implementation Details
The Architecture
The emulator relies on x86 or x86-64 hardware because it doesn't simulate x87 (80-bit floating point) in software; instead, it executes these operations directly on the host's x87 hardware.
To ensure accuracy, I used a differential fuzzer. The logic follows this mathematical comparison: The fuzzer generates random 16-bit instructions, emulates them, runs them via JIT on the host, and flags any discrepancies.
The Win32 Bridge & AI Integration
Since Windows handles are pointer-sized, the bridge maps 16-bit handles to their 32-bit host equivalents, marshaling different struct layouts and servicing required DOS interrupts. This architecture is conceptually similar to a WebAssembly (Wasm) instance.
Interestingly, the bridge supports the Model Context Protocol (MCP). This allows AI agents to:
- "See" the UI "DOM" as it is constructed.
- Drive the game by injecting synthetic events directly into the event pump.
Example: I watched Opus 5 play a full game via MCP; it only required my help twice when it encountered a UI hurdle.
Performance Optimizations
The game's UI is incredibly snappy because the original developers couldn't afford wasteful design. However, "turn generation" (computing the next turn) was a major bottleneck.
Pseudo-JIT Optimization
I used the emulator's tracing capabilities to identify "hot" routines during turn generation. I then used Fable 5.1 to reverse engineer these routines.
The Process:
- Trace execution Identify hot routine.
- Rewrite routine in
C. - Map the
Cfunction as a new, custom 80286 instruction. - Patch the emulator to use this instruction on load.
This provides JIT-like performance without the overhead of a full JIT compiler.
I/O and Audio
The original game used unbuffered I/O, resulting in numerous tiny read/write operations. By implementing a buffer, I reduced I/O overhead from of turn generation time to nearly zero.
Regarding audio, the game used WaveMix.dll for its "battle VCR" sounds. Instead of bundling the DLL, the VM implements the DLL's interfaces natively.
Compression
The original executable contained uncompressed art. I developed a custom LZ-based compression algorithm specifically for this data, reducing the final size to .
Bypassing Copy Protection
The original game required an 8-character alphanumeric serial code. The protection was surprisingly devious:
- Check 1: A loud failure notification.
- Check 2: A silent penalty that sabotages your gameplay.
"Your people suspect you are a usurper!"
This "usurper" penalty also triggered in Play-by-Email (PBEM) saves if the hardware signature (derived from C and D drives) didn't match the serial code. Since official codes are no longer for sale, the VM:
- Injects a fixed serial code on the first run (can be disabled with
--prompt-serial). - Provides a consistent hardware signature to prevent key-reuse penalties.
While I've cracked the system, I found some of the original valid serials to be quite amusing:
CLONEMUM, CROSSNUT, EGGSWAIN, GHOSTKIN, GONKSHOW,
GRIPMIME, SEEKCAPS, SIFTBOLD, SIRBUOYS, SLIMFAZE,
SLOTMOPS, SPAWNELK, SUNBLOND, WANTNEAR, WRONGPOX
I am incredibly excited about the possibilities this VM opens up for the Stars! community.