Running Windows Games on a Hobby OS with Wine
Executing Windows Games on a Custom Hobby OS via Wine
Astral Home | About | Blog
A few months back, I shared an update regarding Astral—a hobbyist operating system I've spent years developing—specifically showcasing it running Minecraft. Since that post, the community has successfully booted modern Minecraft versions and Factorio (by utilizing a glibc compatible libc).
While those titles are relatively portable, the vast majority of gaming software is not. Most commercial games are closed-source and compiled specifically for Windows, making a compatibility layer like Wine an absolute requirement.
My primary motivation was Cogmind, one of my favorite roguelikes. Because it is a 32-bit Windows-exclusive title, getting it to function on Astral became my main objective.
The existing Wine port for Astral was in a rudimentary state; it was so incomplete that even notepad.exe failed to operate correctly. To achieve my goal, I had to finish the port and implement the ability to execute 32-bit instructions on an OS designed exclusively for 64-bit architecture.
Establishing Core Wine Functionality
The initial hurdle was the compilation of PE DLLs. To solve this, I downloaded MinGW and integrated it into the Wine build process.
The Result: notepad.exe is now functional, and the "Save as" dialogue no longer triggers a system crash!
The EGL and Graphics Hurdle
Despite the progress, a major roadblock remained: Wine was compiling without OpenGL support. While Astral supports OpenGL, Wine specifically requires EGL to bridge the gap between rendering APIs and the windowing system.
Initially, I attempted to simply enable EGL within Mesa. However, I discovered that Mesa does not support EGL when using the xlib backend. This forced a pivot to the DRI (Direct Rendering Infrastructure) backend, which allows the app to bypass the X server and communicate with the GPU more directly.
This transition led me down a complex path of patching Mesa so the X.org server could initialize even in the absence of /dev/dri. Once resolved, I successfully launched Deltarune.
Implementing WoW64 and 32-bit Support
Since Cogmind is a 32-bit application and Astral is a 64-bit OS, I needed a way to bridge the architecture gap. I utilized Wine's WoW64 (Windows-on-Windows 64-bit) mode.
Instead of requiring a full 32-bit Unix userspace, WoW64 runs 32-bit Windows binaries within a 64-bit process, translating data structures and system calls on the fly. The memory address space shift is essentially moving from to bytes.
To make this work, I had to implement LDT (Local Descriptor Table) support in the kernel. In x86-64 "long mode," 32-bit code can run if 32-bit segment descriptors are provided. The LDT allows these descriptors—which define memory access and instruction execution—to be configured per-process.
// Conceptual snippet of the register handling fix
void __wine_unix_call_dispatcher() {
pushq %rax; // Ensure registers are preserved during transition
// ... handle syscall ...
popq %rax;
}
After refining the "glue" code in Wine's syscall and signal handling and polishing the kernel, Cogmind finally booted! The game was fully playable, with only two minor issues: game news and scoresheet uploads were broken.
Debugging the Scoresheet Upload
The upload bug was strange: a TCP connection to the servers would open and immediately close without sending data.
I initially suspected a failure in the network stack. However, I noticed that the Wine debug function __wine_dbg_write was completely silent in WoW64 mode. After a deep dive into the source, I found the culprit: I had forgotten to save a specific register within the __wine_unix_call_dispatcher function. Once fixed, the uploads worked.
Software Compatibility Matrix
I have tested several other programs to gauge the stability of the port:
| Program | Status | Notes |
|---|---|---|
| FTL | ✅ Works | Fully playable. |
| Steam | ⚠️ Partial | Installs/updates work; crashes on Chromium launch via GetInterfaceAddresses(). |
| iexplore.exe | ⚠️ Partial | Simple pages work; complex ones crash (same cause as Steam). |
| Factorio | ⚠️ Partial | Window opens, but hangs at the loading screen. |
| Spooky's Mansion | ⚠️ Partial | Boots, but performance is too low for play. |
| Zombies | ❌ Broken | Blocked by Steam DRM. |
| Firefox/Chrome | ❌ Broken | Installer fails to reach a runnable state. |
| SCP: Containment | ❌ Broken | Fails to start; cause unknown. |
| Unity Games | ❌ Broken | wine-mono issues; hangs at MonoManager ReloadAssembly. |
Final Reflections
Porting Wine was an exhilarating challenge. It serves as a proof-of-concept that hobbyist operating systems can support a much wider array of software than previously assumed, moving us closer to the dream of a hobby OS as a daily driver.
While there are still performance bottlenecks and occasional crashes, the foundation is solid. This project provided deep insights into the PE-to-Unix transition and Wine's internals.
Future Goals:
- Fix
GetInterfaceAddresses()to get Steam and Chromium working. - Optimize kernel performance.
- Develop new hardware drivers.
- Squash remaining stability bugs.