First post, by Pling
- Rank
- Newbie
Hi everyone,
I would like to introduce a project that has been in the making for nearly three decades: GALAXEUS. It is a custom arcade engine written entirely in pure 16-bit x86 Assembly, specifically optimized to push low-end vintage hardware to its absolute limits.
The main goal was to achieve an uncompromising arcade feel on the slowest 386 system I could get my hands on: a cache-less AMD Am386SX-33 system with a narrow 16-bit bus and using only 8-bit ISA cards. The engine runs at a rock-solid, fixed 67 Hz / 67 FPS with zero frame drops, driving a standard 8-bit ISA VGA card at a highly customized native resolution of 544x480 in 16 colors.
It delivers an authentic, industrial-grade arcade experience straight out of the early '80s - not just from a technical standpoint, but in its presentation. The engine features a fully functional attract mode that cycles through different gameplay demos, a high-score player TAG input, an arcade-accurate test and diagnostics screen, dedicated coin impulse handling, and full 1 or 2 player game modes. You could drop this setup into a physical arcade cabinet and hardly anyone would notice the difference from an original early-'80s machine, except for the significantly sharper screen resolution and higher refresh rate. 😉
To prove it runs on real iron, I just uploaded a gameplay video showing the current state of the engine:
https://www.youtube.com/watch?v=UHV5c2CAAtw
(Please excuse the amateur video production; it's my first YouTube video ever, and I am definitely no 'stage animal'. I am not active in any forums, and let's just say: I don't go out very often 😄)
Technical Details & Cycle-Squeezing Tricks
Getting this level of performance out of an un-cached 386SX required bypassing standard BIOS/DOS interrupts. I used rather aggressive Assembly techniques, many of them developed by myself, e.g.:
- Extensive Self-Modifying Code (SMC): Used to eliminate slow branches and minimize the binary footprint.
- Opcode Misalignment: Jumping directly into the middle of instructions for dual-purpose execution.
- Data Embedding: Embedding variables directly into the code stream to save critical memory bandwidth.
- Register and Flag Abuse: Heavy use of the somehow nearly forgotten Parity Flag and the undocumented SALC opcode to replace conditional branches with pure logic.
- PIT Manipulation: Driving the PIT (Programmable Interrupt Timer) up and down like crazy.
- Alignment Detection: Custom algorithms to optimize data structures.
- CPU Halting: Completely halting the CPU (HLT) when there is nothing to do on faster systems.
- Stack Hijacking: The slooow VRAM over the also slooow ISA BUS was the biggest bottleneck. I solved it by hijacking the system stack to handle high-speed bookkeeping for dirty VRAM bytes.
- VRAM Optimization: The game works with 8-bit VGA Cards @ 67 FPS on an 8.33 MHz ISA Bus. I implemented a very fast alignment detection for word reads and writes from the VRAM. I estimate that with fast 16-bit ISA VGA cards and a higher ISA bus frequency, the engine could easily handle over 100 to 120 active sprites at 67 FPS. I will run a test in the coming days.
- Custom CRT Timings: Completely avoiding low-res Mode X, manipulating VGA registers of the standard hi-res planar modes directly for the 544x480 output at 67 Hz.
The entire monolithic binary is currently only 57.2 KB. This includes all game logic, graphics, and full 11 kHz 8-bit digital audio samples for the Sound Blaster. I opted out of FM synthesis, as I always found its specific sound slightly weird and depressing — digital samples just bring that authentic, high-energy arcade vibe!
Hardware Integration & Cabinet Readiness
The engine is completely tailored for real hardware and arcade cabinet deployment. It features coin impulse support using the MAME layout, parallel port output for arcade cabinet LEDs (missile status/credits), and an attract mode that mimics actual gameplay sessions.
One of my favorite features during development was implementing a dual-monitor setup: while the main game runs on the VGA screen, real-time hardware telemetry and live debugging info are pushed to a secondary 8-bit ISA MDA monochrome card.
The engine runs completely bare-metal (bootable from a floppy disk or via a 64KB EPROM in a network card socket) and stores high scores directly in the system's battery-backed CMOS RAM in the ROM version. However, for convenience, the codebase also compiles natively into a standard DOS executable (GALAXEUS.COM) that works flawlessly under real DOS, while DOSBox just works "okay-ish".
My experience during development: real hardware absolutely loves Self-Modifying Code (SMC), but DOSBox hates it. I guess the DOSBox Just-In-Time (JIT) compiler simply cannot handle the stress of hotfixing its pre-compiled guts at 50,000 Hz, 🤣.
Furthermore, standard DOSBox, DOSBox-X, and DOSBox Staging all seem to struggle heavily with non-standard VGA timings. I actually had to implement a DOSBox detection routine and patch in a few custom workarounds just to get it running satisfyingly under emulation. It was quite a headache, as one emulator variant does X better but sucks at Y, while another variant does well at Y but completely sucks at X! I spent so much time fiddling around with different DOSBox forks and tweaking their config parameters that I actually started dreaming in INI files, 🤣.
The 28-Year Journey
This project originally started back in the late 1990s when I was a student on a 486. It sat completely untouched in a backup folder from 1998 until the COVID lockdowns in 2020. Over the last few years, after exactly 809 code revisions, I finally stripped it down to its bare essentials and got it running perfectly on my test bench.
The current test setup consists of:
- Motherboard: Ocean Information Systems (OPTi chipset)
- CPU: AMD Am386SX-33 (No cache)
- RAM: 2MB of low-performance DRAM
- VGA: OTI-VGA Bi034D card
- Mono: Winbond HGC monochrome clone (for dual-monitor debugging)
- Sound: Media Vision Thunder Board (Sound Blaster 1.5 compatible) on an 8.33 MHz ISA bus.
There is no public download link available yet as I am still polishing the final build. If the community here is interested, I would love to do some technical deep-dives in this thread, sharing the source code procedure by procedure, discussing the ROM burning process, and showing my custom physical cabinet.
I would highly appreciate your technical feedback, questions, or suggestions!
A note on AI
This project is old, very old indeed. The core logic was written way before AI was anything more than science fiction or procedural tech-babble. There is absolutely NO AI involved in the programming, algorithms, ideas, or optimization tricks. It would have been completely paradoxical to use AI to code anyway, because the entire hate-fun was in the thinking and the making of it — I became totally addicted to that emotional up-and-down rollercoaster during the last few years, as I had to solve new problems on old hardware and invent completely new methods of debugging bare-metal binaries. Plus: AI hates inventing anything truly new, it hates going off the rails, and it absolutely hates Assembly going off the rails.
However, there are exactly two minor exceptions where I used a bit of AI magic:
- I originally commented the entire source code mixed in German language, with some passages in horrible English, 🤣. I used ChatGPT to abracadabra those comments into clean English.
- I used it to help me wrestle with variable lengths. In 16-bit assembly, names need to be short to save sanity, but still "speaking" enough to be readable.
Aside from translating comments and brainstorming short variable names, every single line of assembly code and cycle-squeezing trick is 100% pure human engineering.
(And Google AI helped me to make me look smart in this text, 🤣 😉)
Greetings,
Pling