NOTE: Scroll to the appendix at the bottom If you’re using DJGPP, Free Pascal, or Free Basic. They use a different and incompatible DPMI host.
Sooner or later, everyone who plays around with 32-bit DOS programming using Open Watcom C/C++ is probably going to see one of these errors when they try to run their programs:
Stub exec failed:
\dos4gw.exe
No such file or directory
Can't run DOS/4G(W)
I don’t know if there are other causes for the second version, but the first one is pretty clear about what went wrong. You forgot to copy dos4gw.exe
into the same folder as your EXE file… but why is this necessary?
DOS Extenders
If you’re a novice to the world of DPMI extenders, this comp.os.msdos.programmer post provides a good overview of how it all fits together and this other post lists noteworthy alternative DPMI hosts.
The important thing to understand is that DOS/4GW is a special bundle version of DOS/4G. It was used so widely back in the day because it was hundreds if not a thousand dollars cheaper than the alternatives, but it was designed to upsell programmers with artificial restrictions… and one of those restrictions was having dos4gw.exe
in a separate file.
Luckily, Open Watcom C/C++ supports other extenders which can be bundled into the EXE file. One of them (DOS/32A) is even drop-in compatible with DOS/4GW to the point that you can swap it out without recompiling. (Good for lifting restrictions and resolving bugs in closed-source software.)
The extenders which are bundled with Open Watcom C/C++ (and the meanings of their names) are:
- DOS/4GW by Tenberry Software, Inc. (DOS on 4Gig, Watcom Bundle Edition. The original site is gone now that the author has passed away, and the Watcom manual was always a paid add-on, but it’s API-compatible with DOS/32A and there are bundled and third-party docs.)
- DOS/32A by Narech K. (DOS on 32-bit, Advanced Version)
- PMODE/W by Thomas Pytel and Charles Sheffold (Protected Mode, Watcom Edition)
- CauseWay by Devore Software & Consulting (I couldn’t find an explanation of the name’s origin. The original site is gone, but there’s a manual bundled with Open Watcom C/C++.)
(DOS/4G was named after the memory limit for a 386, in contrast to Tenberry’s other product, the 286-compatible DOS/16M.)
Other extenders people have successfully used with Open Watcom C/C++ include:
- D3X by Daniel Borca (DOS 32-bit Extender, The original site is gone, but it’s in the Wayback Machine including the download)
- DPMIONE (GitHub) by Sudley Place Software (DPMI 1.0 implementation. In fact, it claims to be the only DPMI 1.0 implementation complete enough that it can substitute for Windows 3.1x’s internal DPMI extender… though functionality will still be reduced.)
- HX DOS Extender (mirror, Dr-DOS Wiki) by Andreas “Japheth” Grech (Best option for writing Win32 console applications or making limited use of the Win32 GUI APIs and having your program run under plain old DOS.)
- PMODE/WI by Thomas Pytel and Charles Sheffold (A cut-down version of PMODE/W for demoscene applications where compactness is more important than features and it’s acceptable for problems during development to be more cryptic.)
- WDOSX by Michael Tippach (Wuschel’s DOS eXtender)
I haven’t tried it, but, as described, it sounds like an explicit design goal of HX DOS Extender is to allow you to write a Win32 console binary (which would run on 64-bit Windows 10), and then stub it with HX and have it simultaneously be compatible with platforms like FreeDOS, MS-DOS 6.22, and DOSBox.
Note that, for WDOSX, if you want to have the stub bundled in automatically as part of your linker script rather than using its stubit.exe
, you’ll need to run stubit -extract
to extract the requisite wdosxle.exe
file. (See DOC/README.TXT
in the WDOSX distribution archive for more details.)
One other bit of trivia: If you look in the Open Watcom manuals and see mention of Phar Lap… that’s the company that co-designed VCPI with Quarterdeck and produced the expensive proprietary DPMI extender that Microsoft licensed for Microsoft C/C++… it’s named after a famous race horse.
Before I demonstrate how to use them, I’d first like to address a few potential tripping points I encountered while preparing this:
Potential Errors
undefined system name: ...
System names are defined in the wlink.lnk
file in the bin*
folder containing the wlink
or wcl386
command you’re running. and the version of wlink.lnk
bundled with Open Watcom v2 has a few more of them than Open Watcom v1.9, such as the one for PMODE/WI.
Check wlink.lnk
to see what’s available and either choose one of the ones listed in there, edit it to add one, or upgrade Open Watcom. (Note that I’ve had Open Watcom v2 builds produce broken .com
files, so don’t upgrade blindly.)
This is a ... executable
when trying to run the EXE
If you saw one of these messages when trying to run your EXE…
This is a CauseWay executable
This is a DOS/32 Advanced DOS Extender (LE-style) executable
This is a DOS/32 Advanced DOS Extender (LX-style) executable
This is a DOS/32A DOS Extender w/ Standard stub (LE-style) executable
This is a DOS/32A DOS Extender w/ Standard stub (LX-style) executable
This is a DOS/32A DOS Extender w/ Configurable stub (LE-style) executable
This is a DOS/32A DOS Extender w/ Configurable stub (LX-style) executable
This is a PMODE/W executable
This is a PMODE/WI executable
… it probably means that your compiler emitted one of the following warnings:
Warning! W1093: cannot open cwstub.exe
Warning! W1093: cannot open dos32a.exe
Warning! W1093: cannot open stub32a.exe
Warning! W1093: cannot open stub32c.exe
Warning! W1093: cannot open pmodew.exe
Warning! W1093: cannot open pmodewi.exe
…and fell back to linking the default stub for non-MZ .exe
files.
To solve the problem for all the listed stubs except pmodewi.exe
, you need to either add your binw
folder to the PATH
(but not before binl
) or copy or symlink them from the binw
folder to the binl
folder. (I chose the latter since I didn’t want to throw an arbitrary pile of files into my PATH
and wlink
‘s manual doesn’t mention any other variable it check for a search path.)
This bit of shell script should do the trick when run from inside binl
:
for stub in dos32a.exe stub32a.exe stub32c.exe pmodew.exe; do
ln -s ../binw/$stub .
done
As for PMODE/WI (the cut-down demoscene version of PMODE/W), having a predefined system
for it doesn’t mean that it’s bundled with Open Watcom C/C++, so you’ll need to download it yourself and unpack it into binw
or binl
as appropriate. (Renamed to all-lowercase if you’re on Linux.)
Stub Tricks
An interesting side note is that the This is a ... executable
messages in Open Watcom’s default EXE stub are actually dynamically generated from the osname
values in the wlink.lnk
file in your binw
or binl
folder. (“This is a Windows executable” made general across all outputs that must have a DOS MZ-format EXE at the beginning.)
That means that, if you edit the appropriate wlink.lnk
file to make a copy of the entry for an extender that supports taking the binary as an argument (eg. dos32a
):
system begin joke
option osname='high-security process. You do not have permission to run this'
libpath %WATCOM%/lib386
libpath %WATCOM%/lib386/dos
libpath %WATCOM%/lib386/l32
format os2 le
end
Then wcl386 -l=joke whatever.c
will produce a whatever.exe
file that says “This is a high-security process. You do not have permission to run this executable” if you try to run it in DOSBox, but runs as expected if you run it as dos32a whatever.exe
instead.
…and since the op stub=dos32a.exe
line uses the same mechanism as /STUB
for setting the EXE stub in PE executables. That means that the same “does one thing in DOS and another in Windows” behaviour of PE EXEs is also available to DPMI EXEs with a slight twist.
(Not that you need to edit wlink.lnk
to change the stub. The proper way is to create a file with a name like whatever.lnk
, put your op stub=decoy.exe
or option stub=decoy.exe
in there, and then call wcl386
or wlink
with @whatever.lnk
as one of the arguments.)
If you pick a DPMI extender that supports taking the EXE as an argument, like DOS/32A, then you can compile decoy.c
as a real-mode DOS EXE with wcl -l=dos decoy.c
, specify op stub=decoy.exe
in your linker script, and have a DOS EXE which behaves one way when executed directly and another when executed as dos32a whatever.exe
. (Though, since the DPMI extender uses LE for the 32-bit part, I think it’ll ignore the decoy and run the real thing every time if you run it under OS/2.)
QEMM 97 actually uses this mechanism to put the DOS installer and the launcher for the Windows installer in the same EXE file, so the installation instructions can be simpler.
The Point (Finally)
DOS/4GW is actually the only DPMI extender listed in wlink.lnk
that doesn’t support being bundled and, in fact, the only other one that even supports relying on an external helper is DOS/32A… the extender explicitly designed to be usable by end-users as a drop-in replacement for closed-source DOS/4GW applications.
Under typical circumstances, where you’re using the wcl386
build driver, all you need to do is specify the correct target system via the -l=whatever
option to wcl386
and you’re good to go.
The following -l=
values produce an EXE file which does not require an external DPMI extender EXE:
…while the following ones do require an external helper:
- dos4g
- stub32a
- stub32x
- stub32ac
- stub32xc
Don’t believe me? Create a temporary DOSBox profile with a conf file that mounts C: and launches hello.exe
, create a hello.c
containing a “Hello, World!” in it, source owsetenv.sh
, and then run this bit of shell script:
for EXTENDER in dos4g causeway dos32a dos32x stub32a stub32x stub32ac stub32xc pmodew pmodewi; do
print " === $EXTENDER === "
rm hello.exe 2>/dev/null
wcl386 -q -l=$EXTENDER hello.c
dosbox -conf dosbox-0.74.conf &>/dev/null
done
For more advanced situations, you’ll want to read the Open Watcom manuals, but the following example should get you started:
wcc decoy.c
wlink file decoy.o system dos # produces decoy.exe
wcc386 hello.c
wlink file hello.o system dos32a # produces hello.exe
wlink file hello.o system dos32a option stub=decoy.exe name decoyed.exe
wlink
takes a list of un-quoted directives like you’d put in the aforementioned whatever.lnk
and/or an @whatever.lnk
file reference like can be passed to wcl386
. To invoke wlink
‘s help, the command to use is wlink -?
.
As for other DPMI extenders, from looking at the system
definitions, it seems pretty simple to add definitions for them. Just make a copy of an existing definition and set the appropriate format
and stub
values.
Appendix: CWSDPMI/GO32v2
GNU-ecosystem DOS compilers like DJGPP, Free Pascal, and Free Basic handle DPMI differently enough from Open Watcom C/C++ that their DPMI hosts are not inter-compatible and the DPMI host they come with is called either CWSDPMI or GO32v2, depending on who you ask. (Though everything I could find on the matter says they’re the same thing.)
Like DOS/4GW, the default setup for CWSDPMI requires an external DPMI host binary and, like with DOS/4GW, it’s possible to replace it with a fully embedded stub… but CWSDPMI offers that stub for you. I assume it’s about saving disk space by putting a single copy of the actual CWSDPMI binary in your path to be shared by all the separate EXE files that make up your compiler’s toolchain.
In this case, the process is quite simple:
- Use the
exe2coff
binary from DJGPP (which is also included with DJGPP cross-compiler builds) to strip the stub off your EXE file. - Concatenate
cwsdstub.exe
onto the beginning of the resulting bare binary:- POSIX:
cat cwsdstub.exe my_exe.bin > my_exe.exe
- DOS/Windows:
copy /B cwsdstub.exe+my_exe.bin my_exe.exe
…and yes, that COPY /B
command should work on any old DOS. I have a copy of The MS-DOS Encyclopedia, from 1988 (Microsoft’s complete and unabridged must-have book for DOS 3.3 developers) and it doesn’t say anything about /B
or +
being added after the command debut’d in DOS 1.0.
There aren’t as many alternative DPMI extenders for this ecosystem… probably because it came about after there was no longer a market pressure for everyone to try to sell DPMI extender licenses for money, but there is a port of PMODE/W called PMODE/DJ.