Updated 2020-10-31: Moved the CWSDPMI bundling instructions to an appendix on the post about bundling Open Watcom DPMI extenders.
See Also: Resources for Reverse-Engineering 16-bit Applications for DOS and Win16 debuggers and the rest of the Retrocomputing category for other useful resources like installer builders and unit test frameworks.
While I was poking around the web, looking for information on the most liberally licensed way to put together some utilities for the copies of MS-DOS 6.22 and Windows 3.11 for Workgroups on my retro-gaming PC, I ran across a thread on Computing.Net titled win3.x compatable compilers.
Now, given that it was opened in 2007 and the most recent post was as new as April of 2016, I though I might as well help to fill in the gaps in their knowledge but, much to my dismay, after creating a post (in Leafpad) and creating an account, it seems it’s been locked for being too old. (I can only assume the threshold is something more than 9 years and less than 10.)
…so I decided to post what I put together here instead, so it won’t go to waste.
Tip 1: Current versions of Open Watcom C/C++ can definitely be used to target Windows 3.x.
Not only have I successfully used it to cross-compile basic test binaries on Linux and then run them in Windows 3.11 for Workgroups under DOSBox, I found a Building Win16 GUI Applications in C tutorial which includes an OpenWatcom project file in its example code archive.
Beyond that, it also includes a “windows extender” called Win386, which is essentially a DPMI extender for Windows 3.x applications, providing both a 32-bit flat address space and API thunks to make it more comfortable to call the Win16 API from 32-bit code.
From a deployment standpoint, Win386 has the advantage that it gets bundled into your executable rather than being installed separately like Win32s, so users need never know it even exists. (In fact, for that exact reason, there’s not much information on the web about it today outside the Watcom documentation.)
Tip 1.5: A Single OpenWatcom Install Can Compile To All Targets
build.sh script I put together for generating test files in one of my projects, I generate
.com, 16-bit DOS EXE, DOS/4GW EXE, OS/2 1.x, OS/2 2.x, Win16, Win386, Windows 95, and Windows NT binaries all from the same
hello.c file using the same OpenWatcom install.
(Also note that the linked script cross-compiles using other compilers as well, such as DJGPP, and includes instruction on how to get prebuilt Linux cross-compile binaries for them.)
Tip 2: Open Watcom C/C++ is the same compiler used by many classic games.
Watcom C/C++ was used in quite a few applications back in the day, because they offered the best value for your money if you wanted to do 32-bit protected-mode development. (The compiler was competitively priced, it was known for producing the tightest code, and it came with royalty-free licenses for both a DPMI extender and a precursor to Win32s at no extra charge.)
I’ll go into more detail on all of that…
When DOS games like DOOM say “DOS/4GW Protected Mode Runtime” on startup (or don’t, but include a copy of
DOS4GW.EXE), that means they were compiled using Watcom C/C++ because DOS/4GW is the special Watcom bundle edition of DOS/4G.
(It’s even still included in OpenWatcom, though more advanced, less nostalgic extenders like PMODE/W, DOS/32A and Causeway are also now included. In fact, it’s creator was planning to dig out and provide the source for a more recent version for Open Watcom 2.0 but, sadly, he passed away in 2018.)
I forget which, but at least some of them can be merged into the binary to remove the need for a separate DPMI EXE file.
Win32s came out relatively late in Windows 3.1’s lifetime, so Watcom included a product called Win386 which did basically the same thing. (Providing a a 32-bit protected-mode environment with thunking wrappers to call back into Win16 code.)
Unlike with DOS/4GW, there’s no optional-but-on-by-default splash screen to tell you when Win386 is in use, but things like the Windows version of the Sierra SCI runtime use it.
(That’s actually why some Win16 programs crash on startup on modern CPUs. There’s a bug in older versions of Win386 that gets triggered on encountering CPUs faster than about 300MHz which can be fixed in any application by the “international” version of the KB240982 patch for Microsoft FoxPro 2.6.)
Information sourced from http://www.os2museum.com/wp/watcom-win386/
Performance of Resulting Binaries
According to Paul Hsieh’s WATCOM C/C++ Programmer’s FAQ, PC Magazine benchmarks indicated that “WATCOM C/C++ version 9.5 had the tightest and
fastest code among such compilers as Visual C++, Borland C/C++, Metaware Hi
C, Zortech and Symantec (by a wide margin)”.
According to that same FAQ, Borland’s strength was always their IDE and compile speed and, better code generators or not, Watcom’s approach to supporting embedding bits of assembly language is superior.
Tip 3: Older Versions of Free Pascal Can Produce Win32s-Compatible Applications and Win16 Support is Being Worked On
That said, the user “watler” on the aforementioned Computing.Net thread claims to have confirmed that Free Pascal 1.9.6 could produce Win32s binaries. However, aside from these pages saying to use
-WR with Free Pascal 1.0.10 and avoid console functions in order to target Win32s, I haven’t yet found any further information on the DOs and DON’Ts.
According to this thread, Lazarus 1.6.x and Free Pascal 2.6.4 are the last versions to support Windows 9x with Free Pascal 2.4.x being the last to support it officially, so that’s where you’d want to start looking if you need to develop a Win32s application right now.
Tip 4: Open-Source Installer Creators for Old OSes
Tip 5: WINFILE.EXE is MIT-licensed
Remember how Microsoft included tools like Program Manager in 32-bit Windows up until Windows XP SP1 in order to allow companies to delay retraining?
Well, in early 2018, they MIT-licensed and GitHub’d the version of the Windows 3.1 File Manager that had been included in Windows NT 4 with the
original_plus branch containing only minimal changes to allow it to build as a native 64-bit application using Visual Studio 2015 and 2017.
To quote the README…
The most significant changes are:
- converted to Visual Studio solution; works on VS 2015 and 2017
- compiles and runs on 64-bit Windows (e.g., GetWindowLong -> GetWindowLongPtr, LONG -> LPARAM)
- added a few header files which were stored elsewhere in the NT source tree (e.g., wfext.h)
- deleted some unused files (e.g., winfile.def)
- converted 64-bit arithmetic from internal libraries to C
- converted internal shell APIs to public APIs (the primary reason the old version would not run)
That means it shouldn’t be too difficult to port things back if you want to borrow things now considered standard (like the Tree View widget) for your own projects or if you just want to read the code comments and examine the architectural decisions made by official Microsoft programmers.
As for those “internal shell APIs”, that’s why we have used/library copies of books like Undocumented Windows and Windows Internals by Schulman, Maxey, and Pietrek. (If you came here trying to figure out how to write a shell extension of the Windows Explorer variety, see this article.)
Since I’m doing this as a standalone blog post, I might as well throw in a few other related bits of knowledge I’ve had kicking around:
- Awesome DOS is another list of resources like this. When I can find the time, I want to go through my list and contribute anything that’s missing.
- While not specifically for DOS, this list of single-file public-domain/open source libraries with minimal dependencies should probably be a good place to look for stuff that either works with Open Watcom C/C++ or isn’t hard to port.
- Custom DOS Stub: The Windows executable format must begin with a DOS executable. Normally, it just displays that message about it not being a DOS program, but both the Watcom and Microsoft linkers allow you to specify a
STUB=filename.exeoption to provide a custom DOS EXE file. For example, you could follow QEMM 97‘s lead in having a single
INSTALL.EXEwhich launches either a DOS or a Windows installer depending on what you run it under, rather than having separate
- Other Libre DOS C Compilers: I’ve already written a StackOverflow answer listing all of the ones I know, but DJGPP and GCC IA16 are the only one I’d consider worthwhile.
While not as native/storied/historic as Watcom and only capable of protected-mode DOS output, DJGPP is a GCC port has a big ecosystem and, according to this comp.os.msdos.programmer post, produces DPMI executables which require no extender under NTVDM or dosemu, unlike OpenWatcom.
On the other hand, GCC IA16 is quite young, but, if it’s implemented what you need, its optimizers are going to be more advanced than Open Watcom’s. (eg. It’ll have better dead code elimination.)
- Bundling the DPMI stub: Moved to its own post.
- Supplemental Libraries: Here are some which look promising:
- Argument Parsing: Argtable 3.x seems to place a pretty big emphasis on portability and argtable 2.x explicitly lists DJGPP and Open Watcom as known to build it successfully.
- zlib/libzip: Archlinux’s AUR contains patches to build zlib and libzip for OpenWatcom’s DOS targets. (As well as one for zlib on win16, though I don’t see why there would be a difference unless it’s for static libraries vs. an LE-format DLL.)
- Extended standard library: iMatix’s legacy repositories on GitHub provide a Standard Function Library (online docs) which offers useful stuff for C such as various string- and filesystem-related functions now standard in modern languages and INI and XML reading/writing. The docs list Watcom and DJGPP among the supported compilers for targeting DOS.
- TUI (ie. text-mode GUI) Development: There are ports of Turbo Vision for DJGPP (instructions) and Free Pascal.
- DOS Game Development: Allegro provides API abstractions and a library of ready-made, optimized utility routines to allow easy writing of portable games. Version 4.2 is the last release which supports targeting DOS.
- Pattern Matching: Ozan Yigit’s website has single-file public domain implementations of a robust glob matcher and 4.3 BSD regex syntax.
- Executable Compression: UPX can pack pretty much any kind of executable you’re likely to want to pack except Win16 or .NET.
- JSON Parsing: While it doesn’t explicitly list compatibility with compilers like Open Watcom, microjson is a likely candidate, being a very compact, low-footprint, malloc-free parser for a subset of JSON intended for resource-constrained environments.
- Useful Misc.: You can find lots of useful bits in the Wayback Machine on Troels K. and Japheth‘s sites, such as Win16-compatible CEditView / CRichEditView implementations and the HX DOS extender (which provides limited support for Win32 applications in DOS), but be sure to double-check for license compatibility since I think at least one component on the list is an improvement on an EPL-licensed component and the EPL is GPL-incompatible.
- More Useful Misc.: Also, check out x2ftp and Sunsite mirrors for useful libraries and tutorials. (eg. the watcom folder on x2ftp)
- BASIC: If QBasic/QuickBASIC was more your thing, FreeBASIC supports targeting 32-bit protected-mode DOS and offers a
- Python: It’s ancient, protected-mode, and I’ve heard people use the phrase “buggy old …” to describe it, but a port of Python 2.4.2 for DOS does exist.
I also have a bunch of useful links for building things with Pascal and DOS batch files, but this is already getting really long, so I’ll leave that for another day.
- 2017-12-29: Add a mention of the installer for Open Watcom C/C++, which is in the same source repository and can be built to run as a DOS or Win32 (possibly more) application using a single codebase and
- 2017-12-27: Merry slightly overdue Christmas. I tracked down the installer builder from the Windows 3.1 SDK, discovered that it’s effectively freeware, and added it to the table. (see
LEGAL.TXTinside the self-extracting archive.)
- 2017-12-19: Added mentions of x2ftp and sunsite mirrors as good places to look for old programming resources.
- 2017-12-17: Added instructions for generating retro-styled Windows 9x installers using either an InnoSetup release or NSIS plugins. (And completed the table up to the present day for the heck of it. Speaking of which, for Linux, use Flatpak. It’s good enough for software pirates!)
- 2017-12-10: Updated information on Free Pascal’s real-mode support and added instructions for embedding the DPMI stub used by DJGPP and Free Pascal.
- 2017-12-08: Added “Tip 4: Open-Source Installer Creators for Old OSes” and a link to microjson.
- 2018-02-16: Moved list of installer creators to its own post and moved changelog to the bottom of the post.
- 2018-12-12: Added mention of WinFile being open-source.
- 2019-01-11: Added link to tutorial and documentation for Watcom’s
- 2019-04-10: Added mention of efforts to backport the open-sourced
WINFILE.EXEcode and a little more info on how to target Win32s with Free Pascal.
- 2020-05-21: Added mention of GW-BASIC being released under the MIT license and added a link to The Complete Idiot’s Guide To Writing Shell Extensions for lack of a better place.
- 2020-10-31: Moved the CWSDPMI bundling instructions to an appendix on the post about bundling Open Watcom DPMI extenders.