[Today’s random Sourcerer profile: https://sourcerer.io/sergey]

Never Live It Down

Did Bill Gates utter the phrase “640k should be enough for anyone”? History is a bit murky on this. It’s largely attributed to him, though, so he might as well have said it.

He’s been raked over the coals for this for quite some time. The idea of a total memory space including 640k of RAM is quite humorous by today’s standards. The executable size of most install programs won’t even fit into that size.

For the sake of comparison, the calculator in Windows 10 uses 16.2 megabytes of memory at rest, which is almost 26 times the entire possibly usable memory space to DOS programs in the 1980’s.

Stranger Things

Would you believe me if I told you there was an active community that still used and developed software for this antiquated platform?

Your first response might be: “Why?” That’s perfectly understandable. Let’s examine some of the groups that are still interested and their reasons for still investing in DOS.

Legacy Developers

There are still a lot of programs in use today that run on DOS. Industrial control programs, point of sale systems, and scientific tools are just some of the categories of applications still running under DOS as we speak. Over the years, most of these applications have been ported to other systems or rewritten entirely, but a few remain.

Cheap Real Time

DOS is essentially a real-time operating system. It wasn’t specifically designed for this goal, but its minimalist design enables it to be categorized with other systems of this nature.

A real-time operating system is principally characterized by predictable latency in software and hardware requests. Since DOS has a minimal API with no inherent multitasking, the stability of latency across operating system calls is remarkably stable.

While there are more modern examples of RTO systems that have better design and were architected specifically for this purpose, DOS’s wafer-thin interface between an application and its hardware gives it a leg up in this field. Since systems like FreeDOS (which we’ll dive into later in this article) are open source and freely available, it provides a nice alternative to other real-time operating systems.

Nostalgic Video Games

Modern video games are amazing. The 3D power that they demand is intense, but the visuals, sound presentation, and gameplay can be so life-like that you’d be forgiven if you mistake it for reality. VR headsets are driving this forward in an incredible way, enabling players to become fully immersed in game environments.

Yet with all these enhancements, many gamers who were kids during the 80’s and early 90’s recall a time when primitive (by today’s standards) graphics and singsong square and triangle wave tones forced them to fill in the blanks with their imaginations.

Despite hardware having left the limitations of the DOS era far behind, there is still an active DOS gaming scene. Fueled mostly by nostalgia and programming curiosity, developers are writing games that run on old systems, including PCs that originally came with DOS.

If you think this market is limited to a few hundred greybeards trying to relive their technological childhoods, you’d be wrong. David Murray, better known on YouTube as The 8-bit Guy, has well over half a million subscribers who tune in each week to dive deep into the land of nostalgic computer hardware. He even wrote a real-time strategy game for the Commodore 64 titled PlanetX2. The project was such a success that he sold out of his physical stock and is planning a sequel for the DOS platform.

Get Your DOS On

There are multiple ways to run DOS. You may even have a computer in your closet or basement that can boot it right now. But since we’ll be getting our hands dirty in this operating system, we’ll be using a method that uses free software and will run on nearly any computer.

First, a word about licensing. By some accounts, Microsoft has released MS-DOS 6.22 into the public domain, but I can’t verify this. While the software is easily downloaded from reasonably secure online sources, I can’t advocate installing something that may or may not be subject to copyright restrictions. Even if you do legitimately own a copy (and I suspect many of you do), installing it, even on a virtual machine, can prove tricky.

To make our setup time much easier, we’ll be using FreeDOS. FreeDOS is an open source DOS clone by Jim Hall. Jim started development in June of 1994 after Microsoft announced that they would no longer sell or support MS-DOS. Within a few weeks, Pat Villani and Tim Norman joined the project. In just a few short months, version 0.01 was available. Today, we’ll use version 1.2, released Christmas day of 2016.

To get started, head over to the FreeDOS website and download the CD-ROM standard installer ISO image. You can also download a USB version, but given the differences in hardware, BIOS, etc., we don’t have the room to cover that method here. Instead, we’ll be using virtualization.

I have tested FreeDOS with both Oracle’s VirtualBox and VMware Workstation / Player. Both products work well, though VMware provides a bit better emulation of BIOS and emulates the PC speaker used in many DOS games. In the following examples I’ll be using VMware Workstation, but either will work just fine.

The default options for FreeDOS are fine in either VirtualBox or VMware products. You might be surprised at how little RAM and disk space allocations are presented. I usually setup a 2 GB drive for DOS, but 512 MB, the default, is perfectly acceptable as well.

Installing FreeDOS is as simple as booting from the ISO in your virtual machine (you will be prompted either at boot or during virtual machine creation for the ISO file) and following the prompts. After FreeDOS partitions the disk and reboots, select “Install to Hard Disk” again and continue. Choose a “Full Installation”, or “Full Installation with Sources” if you’re the type who likes to see how things work. Since you’re reading this, you probably are, so why not?

After installation, you’ll be prompted to reboot. This time, instead of choosing “Install to Hard Disk” from the CD boot menu, pick “Boot from System Hard Disk”. The default option, Jemmex (with no EMS) is just fine for a standard boot, especially for development purposes.

Your FreeDOS system is technically ready to go, albeit bare. This is a good state to run DOS games or programs, but not ideal for development because we’re missing some important tools.

Cute FDimples

Now it’s time to install some software inside FreeDOS. The system comes with an excellent package management system called “FDimples”. To use it, run:

fdimples

From this screen, you can install a vast array of software. You have two options here — install all of the development tools, or just install everything. The second option may sound like overkill, but remember that all of these programs take up very little space by today’s standard, so you’ll still have plenty of disk space left over should you decide to install it all.

The awesome package manager with the cute name.

To navigate the categories and packages, use the arrow keys. Space selects individual packages, ENTER selects entire categories. Whichever option you pick, make sure that the development category is selected. To begin installation, press TAB until OK is highlighted, then press ENTER.

You Can’t RHIDE From C++

You wouldn’t want to, would you? C++ is awesome, and thanks to DJ Delorie, the performance and functionality of the GNU Compiler Collection is now available for DOS.

What’s more, using the bundled CWSDPMI DOS extender, your programs will use protected mode and be compiled in 32-bits without you having to lift a finger. You’ll be able to blast way beyond the 640k barrier. Take that, Mr. Gates!

To make sure our development environment is working, let’s create a new folder and test project:

mkdir C:\src

mkdir c:\src\hello

rhide c:\src\hello\hello.cpp

This will start the RHIDE development environment. It’s not quite Visual Studio quality, but you’ll find it more than adequate for your development needs. It reminds me of Borland’s Turbo-C environment, one which gives me plenty of fond memories.

This interface will give you the warm and fuzzies all over.

Here’s our obligatory test code:

#include <iostream>

using namespace std; int main() {

cout << “Hello, World!

”;

return 0;

}

You can now run the code using the “Run” menu (or press ALT+R, then R, or CTRL+F9). You’ll notice that you can either use your mouse or the keyboard shortcuts. The mouse might be fine at first, but you’ll want to take note of the keyboard shortcuts listed next to the various menu items. After you spend a bit of time in RHIDE, you’ll have the most important ones memorized, and your productivity will soar!

A More Interesting Example

Let’s do something more interesting. First, let’s create a workspace for it:

mkdir c:\src\example

rhide c:\src\example\example.cpp

In this example, we’ll demonstrate setting video modes, pixel pushing, and color management in the 320x240x256 VGA landscape.

#include <iostream>

#include <dpmi.h>

#include <unistd.h>

#include <go32.h>

#include <sys/farptr.h>

#include <stdlib.h>

#include <dos.h>



#define COLOR_WHITE 15

#define MAX_WIDTH 320

#define MAX_HEIGHT 200 using namespace std; void set_video_mode(int mode) {

// Create structure for registers

__dpmi_regs r;



// Set AX to video mode

r.x.ax = mode;



// Call int 10 (BIOS set mode)

__dpmi_int(0x10, &r);

} void put_pixel(int x, int y, int c) {

// VGA video memory is located at A000, so let’s poke there.

_farpokeb(_dos_ds, 0xA0000+y*320+x, c);

} void clear_pixel(int x, int y) {

// We just poke a 0 pixel here to clear the spot

_farpokeb(_dos_ds, 0xA0000+y*320+x, 0);

} void draw_effect() {

// Current x and y positions

int x = 0;

int y = 0; // For each to y_max, put pixel

for (y = 0; y < MAX_HEIGHT, y++) {

for (x=0; x < MAX_WIDTH, x++) {

put_pixel(x, y, rand()%(15+1));

}

}

} int main() {

// Set video mode to 13h (320x200x256)

set_video_mode(0x13); // Draw the effect

draw_effect(); // Wait for ENTER key

cin.ignore(); // Return to text mode

set_video_mode(3); return 0;

}

An Example with Movement

In this example, we’ll build upon the previous example but add user-controlled movement. You’ll use the arrow keys to move a pixel around the screen. This is the foundation of what could be a video game.

#include <iostream>

#include <dpmi.h>

#include <unistd.h>

#include <go32.h>

#include <sys/farptr.h>

#include <stdlib.h>

#include <dos.h> #define COLOR_WHITE 15

#define MAX_WIDTH 320

#define MAX_HEIGHT 200 #define KEY_ESC 283

#define KEY_UP 18432

#define KEY_DOWN 20480

#define KEY_LEFT 19200

#define KEY_RIGHT 19712 using namespace std; void set_video_mode(int mode) {

// Create structure for registers

__dpmi_regs r;



// Set AX to video mode

r.x.ax = mode;



// Call int 10 (BIOS set mode)

__dpmi_int(0x10, &r);

} void put_pixel(int x, int y, int c) {

// VGA video memory is located at A000, so let’s poke there.

_farpokeb(_dos_ds, 0xA0000+y*320+x, c);

} void clear_pixel(int x, int y) {

// We just poke a 0 pixel here to clear the spot

_farpokeb(_dos_ds, 0xA0000+y*320+x, 0);

} int get_key() {

// Create structure for registers

__dpmi_regs r;

// Set AH to 00h for BIOS call

r.x.ax = 0x0000;

// Call int 16 (BIOS keyboard handler)

__dpmi_int(0x16, &r);

// Return the value of AX (complete scancode)

return r.x.ax;

} int main() { // Set video mode to 13h (320x200x256)

set_video_mode(0x13); // Main loop

int running = 1;

int curkey;

// Start in the middle of the screen

int char_x = MAX_WIDTH / 2;

int char_y = MAX_WIDTH / 2;

while (running) {

// Has a key been pressed

curkey = get_key();

// Whatever happens, we know we need to clear the pixel

clear_pixel(char_x, char_y);

switch (curkey) {

case KEY_ESC:

// ESC was pressed, so exit

running = 0;

break;

case KEY_UP:

char_y--;

break;

case KEY_DOWN:

char_y++;

break;

case KEY_LEFT:

char_x--;

break;

case KEY_RIGHT:

char_x++;

break;

}

// Paint our "character" on screen

put_pixel(char_x, char_y, COLOR_WHITE);

}



// Return to text mode

set_video_mode(3);

return 0;

}

Hopefully this example gives you a basic idea as to how to handle user input to move your character around the screen. It is lacking in a few areas.

For example, to obtain the keyboard press, we’re using INT 16. It works well, but does tie up the program while waiting for input. A more sophisticated example would use INT 16 AH = 01 to check the keyboard state and return to the program execution if no keyboard input was present. This would give you time to draw other graphics. For our purposes here, this works fine, but writing a keyboard event handler with this functionality is important in a more dynamic game.

Also, put_pixel and clear_pixel work well enough, but both aren’t as efficient as creating a framebuffer and writing to that, then, as often as you desire, copying that memory buffer to video memory with memcpy(). An example of that might look something like this:

#include <sys/movedata.h> char buffer[320x200];

int i;



// Modify the buffer array like you would any other array



// Copy buffer to video memory periodically

dosmemput(buffer, 320*200, 0xA0000);

This saves a lot of protected / real mode changes, which will speed up your writes considerably. You can also do screen writes at a time of your choosing, allowing you to do other things in the background like play music, write to the disk, and load graphics.

The Best of Both Worlds

Even though C++ is a high-level language, you’ll notice that in this example I still have to use some pseudo-assembly to get the job done. That’s just the nature of the beast. Early non-BASIC programming in DOS was often done in assembly, and few standard libraries existed.

There is beauty in this approach, though. Rather than writing straight assembly, I can get the power of assembly-language interrupt calls mixed with the power of C++. Not to mention a powerful protected-mode memory management system thanks to the DJGPP runtime.

It could be said that by using this system I’m cheating. I suppose in the strictest sense I am. Before DOS extenders, developers had to struggle to fit everything into 640k. And realistically, they rarely had 640k to work with. Most DOS machines struggled to have 600k free of RAM after drivers and TSRs (terminate and stay resident) programs were loaded.

This was fine for simple programs. But when more complicated data structures were needed, either DOS extenders or EMS (enhanced memory specification) memory was required. Your code may have fit in 600k, but data spaces in complex programs or games needed room to grow, and the 1 MB barrier was extremely limiting. DOS extenders like CWSDPMI were a lifesaver!

Conclusion

I hope you enjoyed our romp through nostalgaland. DOS programming is a lot of fun. It’s hard to get this close to the bare metal with any other operating system, and difficult to get the balance of functionality and flexibility without using DJGPP.

I wish we had time to get into more complex programming examples. The possibilities are truly limitless with this development stack. You get the immediacy of DOS programming, the graphics potential of the VGA graphics system (which, despite its antiquated nature, can still produce some interesting game graphics), and the power of the standard C library.

With C++, you could create a character class for your video game hero, give him variables like current position on the screen, health, and attack power, and have him controlled via arrow keys using the techniques above. Since you have up to 4 GB of addressable space (in theory, anyway), you can load complex tile graphics from disk or even entire JPGs of pre-rendered graphics. Using this setup, it would be entirely possible to create a semi-modern video game.

But it would have an extra feature that no other program can claim today. It can run on the computer you got for Christmas on your twelfth birthday.

Go ahead — your old computer is waiting for you. It’s time you became reacquainted.