This blog post explains how to use Digital Mars C Compiler 8.57 on Linux with Wine to cross-compile to EXE files of 16-bit DOS, 32-bit DOS and 32-bit Windows (Windows 95 -- Windows 10). The actual program compiled is quite dumb (it doesn't matter), the focus is on installation and command-line flags.
Please note that Digital Mars C Compiler hasn't been ported to Linux, so we need to run it in emulation: we will be running the Win32 version in Wine. (Another option would be running the DOS version in DOSBox.) Digital Mars C Compiler is now open source, you may want to contribute porting it to Linux here.
Digital Mars C Compiler is very small: version 8.57 is less than 13 MiB, including C and C++ compiler, linker, DOS extender, #include
files and libc for 16-bit DOS, 32-bit DOS and 32-bit Windows. If compressed with 7z (LZMA2), it's about 2.28 MiB. It's also impressive that all of it (except for the DOS extender) was written by a single person. See also discussion 1 and duscussion 2 on Hacker News, with replies from the author of the compiler.
All commands below are to be run in a Linux terminal window without the leading $
.
Check that Wine is installed:
$ wine --version
wine-4.0.3 (Debian 4.0.3-1)
If you get an error message instead of a version number, then install Wine first using your package manager.
Download and extract the compiler:
$ mkdir digitalmarsc
$ R=http://ftp.digitalmars.com/Digital_Mars_C++/Patch/
$ wget -O digitalmarsc/dm857c.zip "$R"/dm857c.zip
$ (cd digitalmarsc && unzip dm857c.zip && rm -f dm857c.zip)
$ wget -O digitalmarsc/dm850dos.zip "$R"/dm850dos.zip
$ (cd digitalmarsc && unzip -n dm850dos.zip && rm -f dm850dos.zip)
$ wget -O digitalmarsc/dm831x.zip "$R"/dm831x.zip
$ (cd digitalmarsc && unzip -n dm831x.zip && rm -f dm831x.zip)
$ KR=https://github.com/Olde-Skuul/KitchenSink/raw/master/sdks/dos/x32/
$ wget -O digitalmarsc/dm/lib/cx.obj "$KR"/cx.obj
$ wget -O digitalmarsc/dm/lib/x32.lib "$KR"/x32.lib
$ wget -O digitalmarsc/dm/lib/zlx.lod "$KR"/zlx.lod
Please note that cd857.zip contains additional files and it overlaps with dm850dos.zip (e.g. dm/lib/sds.lib).
Create the test program source code:
$ cat >myprog.c <<'END'
#include <stdio.h> /* Code below works without it. */
const int answer = (int)0x44434241; /* dd 'ABCD' */
int mul(int a, int b) {
return a * b + answer;
}
int main(int argc, char **argv) {
(void)argv;
return mul(argc, argc);
}
END
There is no need to set up environment variables, because Digital Mars C Compiler can its library files nearby its own directory. But we make the shorthand wine dmc
work for convenience:
$ export WINEPATH="$(winepath -w digitalmarsc/dm/bin)"
Do this to prevent Wine from displaying GUI windows:
$ unset DISPLAY
Compile to all targets:
$ wine dmc -ml -v0 -odcprog.dosl.exe myprog.c
$ wine dmc -ms -v0 -odcprog.doss.exe myprog.c
$ wine dmc -mx x32.lib -v0 -odcprog.dos32.exe myprog.c
$ wine dmc -mn -v0 -odcprog.win32.exe myprog.c
$ wine dmc -c -ml -v0 -odcprog.dosl.obj myprog.c
$ wine dmc -c -ms -v0 -odcprog.doss.obj myprog.c
$ wine dmc -c -mx -v0 -odcprog.dos32.obj myprog.c
$ wine dmc -c -mn -v0 -odcprog.win32.obj myprog.c
$ wine dmc -c -mf -v0 -odcprog.os232.obj myprog.c
More information about running Digital Mars C Compiler: https://digitalmars.com/ctg/sc.html . Please note that the dmc and sc commands were equivalent, but only dmc works in recent versions.
Check the file type of the created executable (*.exe) files:
$ file dcprog.*.exe
dcprog.dos32.exe: MS-DOS executable, MZ for MS-DOS
dcprog.dosl.exe: MS-DOS executable
dcprog.doss.exe: MS-DOS executable
dcprog.win32.exe: PE32 executable (console) Intel 80386, for MS Windows
Check the file type of the created object (relocatable, *.obj) files:
$ file dcprog.*.obj
dcprog.dos32.obj: 8086 relocatable (Microsoft), "myprog.c"
dcprog.dosl.obj: 8086 relocatable (Microsoft), "myprog.c"
dcprog.doss.obj: 8086 relocatable (Microsoft), "myprog.c"
dcprog.os232.obj: 8086 relocatable (Microsoft), "myprog.c"
dcprog.win32.obj: 8086 relocatable (Microsoft), "myprog.c"
More information about the OMF OBJ file format (*.obj above): https://pierrelib.pagesperso-orange.fr/exec_formats/OMF_v1.1.pdf
The *.obj files contain an memory model comment with code 0x9d which depends on the target and operating system:
dosl has 0lO
,
doss has 0sO
,
dos32 has 3fOpd
,
dosx has 7xO
,
os232 has fnO
,
win32 has 7nO
.
Alternatives for cross-compiling C code to some of these EXE targets on Linux:
- OpenWatcom ((16-bit and 32-bit) * (DOS, Windows and OS/2) targets, see blog post for details)
- Digital Mars C compiler (on Linux needs Wine, 16-bit DOS, 32-bit DOS, 32-bit Windows targets, plus 32-bit OS/2 object target (no linking), see above for details)
- mingw-w64 (win32 and win64 targets)
- gcc-ia16 (doss and dosl targets, i.e. 16-bit DOS EXE with various memory models)
- djgpp-linux32 (dosx target, needs cwsdpmi.exe or pmodstub.exe (PMOED/DJ) as separate downloads)
- You may be able to run C compilers released for Windows (e.g. the Digital Mars C compiler) using Wine.