OpenWatcom may be underappreciated today, but it was the premier compiler of years past. Even now, it is still very useful as a compiler that can process relatively modern source code into executables for many 16-bit or otherwise discontinued platforms.
This tutorial will cover the basics of setting up OpenWatcom under GNU/Linux and compiling a simple program for several platforms.
For starters, you need the OpenWatcom compiler. This can be found at the OpenWatcom website. You may also want SDL and GLUT for OpenWatcom and/or Allegro 4 for OpenWatcom, if you use those libraries.
For convenience's sake, we have also added some custom-build OpenWatcom debian packages to our Codeberg package repositories. Please click on one of those and follow the instructions to install the apt keyring if you wish to use those. They may require adding i386 multilib support to your Debian installation.
Once OpenWatcom is installed, you may note the directories under its installation path. Directories of particular interest include:
binl binw binnt lib286 lib386 hThe watcom compiler is split into wcc for 16-bit programs and wcc386 for 32-bit programs. Please note that most target operating systems supported by OpenWatcom can run either. For example: Windows 3.x can run 16-bit Windows programs, and DOS can run 32-bit programs that use a DOS extender.
Once the individual code translation units are compiled, they can then be linked together with wlink. Some programs for Windows or OS/2 may need to use a resource file to provide icons, localized strings, or other resources. This resource file must be compiled from a .rc script with wrc before being included in the linking process. Writing .rc scripts is beyond the scope of this article, but many other resources on writing Windows resource files are available.
The next section includes some examples for common target platforms, along with an explanation of how they work. These examples are in GNU make format, and assume $WATCOM is defined in your shell as the path to your OpenWatcom installation (e.g. /opt/watcom).
Makefiles allow for multi-stage compiling of complicated programs, compiling only the pieces that have changed since the last time they were compiled. OpenWatcom comes with its own "wmake" utility for this, but if you only use it on Linux then you may be better off using GNU make, instead, as this has a more portable and modern syntax with more features.
Again, these are just some common examples. You can find more information on building for other systems using the OpenWatcom Linker User Guide, the system linker blocks defined in $WATCOM/binl/wlink.lnk, and the command help viewable by running either wcc or wcc386 with no arguments.
Building for 16-bit Windows 3.x has a number of pitfalls, especially related to memory usage. These are beyond the scope of this article, but you may find the .map file generated with the "option map" option to be useful in finding globals to shift into other segments (e.g. with __based( __segname( "xxx" ) )). The system type for Windows 3.x is "windows" and the header files are located in $WATCOM/h/win.
# = Compile Invidiual Source Files = | |
# -zm and -ml should ease memory pressure a 16-bit Windows program will face. | |
# Also note that we chop the .c off of the filename, as wcc assumes the .c. | |
%.o: %.c | |
wcc -zm -ml -bt=windows -i=$(WATCOM)/h -i=$(WATCOM)/h/win -fo="$@" "$(<:%.c=%)" | |
# | |
# = Compile Resource File = | |
resrc.res: resrc.rc | |
wrc -r $< -bt=windows -fo=$@ | |
# | |
# = Link Compiled Files = | |
# Use the | to make sure resrc.res is built, but not include it in $^. | |
# We can then pull in the resource file manually. | |
# Additionally, option map will generate a .map file to help debug segment usage. | |
program.exe: $(subst .c,.o,$(C_FILES)) | resrc.res | |
wlink name $@ option dosseg option map fil {$^} resource resrc.res |
This target is somewhat less common, but can be less frustrating to write and build if you wish to support Windows 3.x and know your program will only ever run on a 386 or newer. It is somewhat analogous to using a DOS extender, but for Windows programs. Win386 programs use a flat memory model, but do not require a separate runtime like Win32s. This alleviates a lot of issues with memory usage and segmentation. These use the same headers as Windows 3.x 16-bit programs, along with the "win386" system type..
# = Compile Invidiual Source Files = | |
# -zm and -ml should ease memory pressure a 16-bit Windows program will face. | |
# Also note that we chop the .c off of the filename, as wcc assumes the .c. | |
%.o: %.c | |
wcc -zm -ml -bt=windows -i=$(WATCOM)/h -i=$(WATCOM)/h/win -fo="$@" "$(<:%.c=%)" | |
# | |
# = Compile Intermediate File = | |
# The linker stage here is only an intermediate stage. | |
# It creates a .rex file which then must be bound with the built-in runtime to create a .exe file below. | |
program.rex: $(subst .c,.o,$(C_FILES)) | |
wlink name $@ system win386 fil {$^} | |
# | |
# = Link Compiled Files = | |
# Note that binding the .exe file uses the .rc file directly. wrc is not used! | |
program.exe: program.rex | |
wbind "$<" -s "$(WATCOM)/binw/win386.ext" -R resrc.rc "$@" |
Building for Windows NT is slightly simpler, as you don' have to worry so much about the memory model and debugging segment usage, while also not having to tack a runtime onto the front of the program. Note that 32-bit Windows programs built this way can also run in Windows 9x (provided they only use API functions available in those operating systems). The system type for Windows NT is just "nt" and the header files are located in $WATCOM/h/nt.
# = Compile Invidiual Source Files = | |
# Note that we are using the 32-bit wcc, wcc386. | |
# Again, note that we chop the .c off of the filename, as wcc assumes the .c. | |
%.o: %.c | |
wcc386 -bt=nt -i=$(WATCOM)/h -i=$(WATCOM)/h/nt -fo="$@" "$(<:%.c=%)" | |
# | |
# = Compile Resource File = | |
resrc.res: resrc.rc | |
wrc -r $< -bt=nt -fo=$@ | |
# | |
# = Link Compiled Files = | |
# Use the | to make sure resrc.res is built, but not include it in $^. | |
# We can then pull in the resource file manually. | |
program.exe: $(subst .c,.o,$(C_FILES)) | resrc.res | |
wlink name $@ fil {$^} resource resrc.res |
This target is very simple. It uses the bare headers in $WATCOM/h and generates a map file for segment debugging. There are a number of DOS extenders you can use to write 32-bit DOS programs, but those are beyond the scope of this article (for now). You can find more information on them in the wlink user guide and linker block definition file $WATCOM/binl/wlink.lnk.
# = Compile Invidiual Source Files = | |
# Note that we just use the headers in $(WATCOM)/h. | |
# Again, note that we chop the .c off of the filename, as wcc assumes the .c. | |
%.o: %.c | |
wcc -bt=dos -i=$(WATCOM)/h -fo="$@" "$(<:%.c=%)" | |
# | |
# = Link Compiled Files = | |
# Again, option map will generate a .map file to help debug segment usage. | |
program.exe: $(subst .c,.o,$(C_FILES)) | |
wlink name $@ option dosseg option map fil {$^} |
Once again, we must emphasize that this is a quick and basic tutorial on getting up and running with OpenWatcom. OpenWatcom supports many more options, and even operating systems like RDOS or OS/2 which we have not covered here. Hopefully this is enough information to get you up and running for now, but please feel free to let us know if you have questions or suggestions for more details! Thanks for reading!