The trials and tribulations of deploying a C++14 game for 486 PCs running DOS

I would like to take this time to share some of the hard earned knowledge that came from the road that lead into the release of my game. This will be a post mortem of sorts, but not near as complete as it should be. This game has a *lot* of history and it would be too much to tell.

Why C++14?

Well, I work with modern C++ on a daily basis and I need to stay sharp on how things are made today. Not only that, but considering the game was originally written for Android, it's to be expected that it wouldn't be compatible with ancient C compilers. Not to mention the many compiler optimizations we have today.

Sure, "compilers are very good at applying mediocre optimizations repeatedly" and that gave birth to my effort to write a custom renderer to handle such class of machines. Compare the first implementation (which I find surprising it is capable of running, given how little was changed):

To what we have now (or rather, "not long ago", given this was from Beta 6):

The biggest lesson to learn (and still very applicable to modern machines) is: memory is expensive to use

When I began my ports to DOS and 486s, I was under the impression that 25Mhz was too weak to perform competently on a 3D game. On the other hand, I had some good 20MB of RAM - so my natural reaction was to save clock cycles by caching as much computation as possible. That has proven to be a bad mistake. If you think about it, when in DOS, I own the whole 25Mhz piece of silicon - and that should be plenty enough. On the other hand, memory accesses still mean a lot of negotiation between many components on the motherboard. Thus, make sure to have everything on cache all the time!

The second lesson here is: floating point is expensive - even if you have a FPU!

Integer operations are incredibly simple to perform and the processor is really good at it, since it's a integral part of it. Even if you throw a couple of shifts every now and then. Also, after studying the source code of Marathon 2, I was convinced that this was the way to go. Keep things integer as much as possible and use fixed-point when needed.

To this measure, I employed the very cool library sg14::fixed_point - and thus my need for Modern C++ (sure, I've written a fixed point math library in the past, so I felt confident enough to use this one):

Another important piece to keep in mind (it's obvious, but hard to stop and think about it while in the middle of everything): the fastest code is the one you don't have to run. Also, YAGNI. When discussing the qualities of fixed_point with it's author, there was the point of GLM not supporting it. But do I really need GLM? I found out that no, I didn't. Transforming the view to the correct point in space was achieved with simple integer math, just like I did it some good 15 years ago, with QBasic (for the same class of machines). This strikes a good balance between memory and CPU usage and help keeping things in cache.

One final mention: beware of code bloat - and Modern C++ gets a special mention here. During the development of the game, I began noticing how people appreciate when the game fits on a single floppy. I even experienced this myself when carrying some floppies and getting angry for now being able to demo my newest stuff. Since then, I began hunting as much code bloat as possible - and even more to look into code can be replaced with simpler stuff. That said, there is no good reason to use iostream in production code. It's slow and bloated.

During the early days of the project, removing ifstream and cout got me some good 200kb back on disk, finally allowing the game to fit uncompressed on a floppy. Later on, the day before the final release, removing a single use of stringstream got me another whopping 400kb! If only I had this since the beginning, I would add more content into the game...

And after all that mountain of text, I leave you the final game. I've also been working to get it to run well on Linux, OSX, Emcripten and Windows - with mixed results. Linux was kind of easy, given that I had a internal build for debugging purposes, but OSX and Emscripten still are facing roadblocks in regards to the still text screens (gameplay is fine). Hopefully, I will sort this out very soon.

In regards to mobile versions, I will probably hold those and save it for the enhanced version of the game engine (the one from the first video). Under OpenGL ES 2, it works like a beauty, with smooth moving, lightning and all bells and whistles. I would love to have this on DOS as well, but there was no time!

Get Dungeons of Noudar 3D

Leave a comment

Log in with your account to leave a comment.