23

I have a simple C program, and I would like to compile it targeting MS-DOS. Can this be achieved with Clang?

I would like to produce the following formats:

  1. COM executable
  2. 16-bit MZ executable
  3. 32-bit protected mode MZ executable (for use with extender)

To my understanding, the biggest problem with these formats comes from the lack of support by the linker (lld in my case). Can an alternative linker be used? What other challenges can possibly be faced?

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
tpimh
  • 424
  • 2
  • 9
  • Wouldn't linking to ELF format and using 'strip' leave the raw a raw binary aka a .COM? Otherwise, why not using GNU linker which should be able to output COM or EXE. Ofc, this only works if you're using fitting compiler producing the right DOS-Calls or at least a CLIB made for DOS. – Raffzahn May 22 '23 at 08:03
  • 9
    "Code for MS-DOS" is a broad field ranging from simple tiny-model .COM applications on 16-bit CPUs to stuff like DJGPP that assume at least a 32-bit CPU and have to link half an OS into the binary. You should definitly be more specific. – tofro May 22 '23 at 09:34
  • 4
    @tofro I specifically mentioned the 3 targets that I am trying to build for. – tpimh May 22 '23 at 12:07
  • 1
    ... which cover the complete field of view. Implementations of C compilers not specific to MSDOS might be able to cover the MSDOS use cases that assume a linear address space: The tiny COM model and the linear 32-bit model. But nothing in-between. – tofro May 22 '23 at 13:33
  • 3
    Are no actual MS-DOS compilers available? – dave May 22 '23 at 13:35
  • Possibly related: https://github.com/skeeto/w64devkit – D. Ben Knoble May 22 '23 at 23:19
  • Can you get your hands on a copy of Microsoft's VC++ v1.51 (the last MSFT 16-bit toolset)? It was part of the MSDN DVD set for probably more than a decade. See if someone you know has an old MSDN subscription lying around. – Flydog57 May 24 '23 at 04:31

2 Answers2

23

There’s more to it than the linker, especially if you want to support real mode. You need a DOS C library, and a compiler which can output 16-bit code. If you want to produce binaries in anything other than the tiny model, you also need a compiler that’s aware of segmentation.

As far as I’m aware, clang doesn’t have the necessary features. There are a few other possibilities:

  1. Open Watcom can produce 16-bit real-mode and 32-bit protected-mode DOS programs (if you’re interested in Open Watcom, you probably want the unofficial v2 fork)

  2. DJGPP produces 32-bit protected-mode DOS programs

  3. gcc-ia16 produces 16-bit real-mode DOS programs

For the latter two, I recommend jwt27’s build scripts which will install complete ia16 and DJGPP toolchains, including a DOS C library.

Most modern C compilers targeting DOS 8086, running on DOS 8086 (16-bit) gives a comprehensive list of DOS-targeting compilers; most of them require a DOS or Windows host.

Stephen Kitt
  • 121,835
  • 17
  • 505
  • 462
  • 1
    Thank you! jwt27's toolchain seems like a very good starting point for me. From this thread I get that ia16 support in clang is very limited, nevertheless projects like this and this exist. – tpimh May 22 '23 at 12:26
  • 3
    Right, clang has an -m16 flag but what you end up with is “16-bit” binaries that are really 32-bit binaries with 32-bit prefixes emitted everywhere necessary (effectively, 32-bit real-mode programs). – Stephen Kitt May 22 '23 at 12:35
  • You should be able to build small model executables without knowing about segmentation just so long as the compiler is new enough to know about .rodata (merge .data and .rodata at link time and keep .text separate). – Joshua May 23 '23 at 02:37
  • "effectively, 32-bit real-mode programs" — do you mean "32-bit unreal-mode programs"? Like, with a 32-bit code segment and CR0.PE=0? – Ruslan May 23 '23 at 07:37
  • 3
    @Ruslan I think they meant programs that run in Real or Virtual 86 Mode but use 32-bit operand size, using the o32 or a32 prefixes to access 32-bit features in the 16-bit cs. – ecm May 23 '23 at 07:44
  • 2
    @Ruslan: clang -m16 is like x86 GCC's -m16 - they emit the same asm as -m32 in all(?) cases, except they slaps a .code16gcc at the top. e.g. https://godbolt.org/z/eecb3Ed6M shows them using the same stack offsets, because .code16gcc (unlike .code16) gets it to assemble call foo with a 66h prefix to make it push a 4-byte return address, making stack offsets the same as in 32-bit code (first arg at [esp+4]). The result ends up in a 32-bit .o so Godbolt's "binary mode" disassembles it wrong. All of this is different for gcc-ia16, which isn't just a quick & dirty hack. – Peter Cordes May 23 '23 at 15:19
3

Digital Mars claims to compile for MS-DOS. I have used it for Windows and it is quite good and generate small and fast executables.

This page might help too.

user10191234
  • 1,945
  • 2
  • 8
  • 29