Nowadays, Open Watcom v2 seems to be the go-to free software compiler for 16-bit DOS. It can run on DOS natively, but you can also cross-compile from modern operating systems, including 64-bit Linux.

Although there is an official Wiki about building the toolchain, to me it reads more like a reference for those already in the know (and it’s a lot of words anyway!)

Below I reproduce just the steps I had to take to get a fully working toolchain.

  1. git clone git@github.com:open-watcom/open-watcom-v2.git, cd open-watcom-v2
  2. in setvars.sh, I uncommented export OWDOSBOX=dosbox, since I already had DOSBox installed
  3. source setvars.sh
  4. ./buildrel.sh

This will use your system GCC to build the Open Watcom toolchain (for all possible hosts & targets).

Assuming everything went fine, to use the newly-build compiler, some environment variables need to be configured – unlike GCC, where it usually suffices to have it in PATH.

export WATCOM=$(pwd)/rel
export PATH=$WATCOM/binl64:$WATCOM/binl:$PATH
export INCLUDE=$WATCOM/h

Let’s see if we can compile a minimal program…

wcl -bt=dos $WATCOM/src/hello.c

…and run it.

dosbox hello.exe

Using DOS system calls

Open Watcom provides an implementation of the Standard C library; however, if you prefer to indulge in the quirkiness of DOS system calls, you can also do that, using Watcom’s funky #pragma aux syntax. The following program illustrates a call to the INT 21h AH=09h function (display string):

void print(char *string);
#pragma aux print = \
        "mov ah, 09h"   \
        "int 0x21"      \
        parm    [dx]    \
        modify  [ax];

int main(void) {
    /* string must be terminated with '$' */
    print("hello, world\n$");
    return 0;
}

For portability, though, it might be better to include <dos.h> and use the wrappers found there (such as _dos_write), which should still be a lighter abstraction than <stdio.h>.


UPDATE: This post contains many more useful examples and links related to OW2 on Linux.