• Please review our updated Terms and Rules here

Making 32 bit binaries for MSDOS

hjalfi

Experienced Member
Joined
Feb 11, 2017
Messages
265
Location
Zürich, Switzerland
I have a compiler which I would like to port to produce DOS 32-bit executables (the Amsterdam Compiler Kit). (It already produces 16-bit DOS executables but that's not quite good enough to make it self hosting.) I've been looking at how to do 32-bit code on DOS and yikes.

AFAICT, the simplest way is to require the use of a DPMI host to do all the annoying 32-bit stuff. My understanding is that I will end up with a 16-bit DOS executable containing a stub program that initialises DPMI, switches to 32-bit protected mode, and then loads the bulk of the program from the tail of EXE file. (And, presumably, manually relocates it.) The DPMI host then takes care of all the mode switching so that I can just call normal DOS interrupts from 32-bit mode and everything should Just Work.

Does this pass sanity checking? I haven't worked with 32-bit DOS before. Sadly I have to write all my own tools, including the linker backend. As a model I'm intending to totally rip off DJGPP's bootstrap loader; if anyone knows of any better (i.e. simpler) source to look at I'd love to know.
 
Perhaps a couple of simplifications:

No need to relocate the binary. Assuming you are just creating a binary blob to execute (no dynamic libraries) you can just link it to a hard address.

Why write your own tools? Doesn't ACK have its own that generate an a.out binary?
or
Have the ACK output COFF object files. Rip off DGJPP's linker, too.
 
The ACK doesn't generate a.out or COFF. It can generate ELF and MachO (and V7 a.out, but I don't think this is what you meant).

Regarding linking to a hard address: looking at the API I should be able to allocate a memory block, assign it to a segment descriptor, and then the local address relative to the segment will always start at 0. Is that what you meant? If so, that's so much easier than what I was originally thinking! Do I need a separate executable descriptor or can I just use the same one for CS, DS and SS?

I'm also guessing that as the DOS interrupts all take 16-bit parameters, I need to allocate a specific 64kB linear memory block for exchanging data with int 31 func 0100. I forsee a lot of memcpys through ES here.
 
Can the ACK generate a linear 32-bit (i.e. 32 bit "flat" mode) executable, or does it produce only segmented output? Libraries matter, of course.

I routinely run some 32-bit executables on DOS using Hiren's HXDOS utilities. It doesn't work for all Win32 executables, but enough of them to be useful.
 
Last edited:
I'll admit to not knowing a great deal about the ACK. It appears that it has platform support for a number of OSes which should provide a mechanism to add a DOS32 target. Under most DOS32 environments that I've used, the first megabyte of linear address space will directly map over the lower 1 MB of RAM, making access to real mode DOS structures fairly straight forward. I'll assume that the ACK deals with a linear address space and not segmented, so you should not have to mess with segments. If I were going to create a development plan, I'd write a loader for the ACK binary format using either DJGPP or WatcomC/DOS4GW. See if you can load and execute some code produced by the ACK in a know, working environment. Don't make life harder than it needs to be.
 
It doesn't know about segments, so produces flat executables. (It will generate Linux ELF executables for various architectures, for example.) It has its own internal object file format and calling convention and can't interoperate with third-party libraries, which simplifies things no end.

So, generating the actual binaries seems straightforward. From the ACK's point of view they're flat absolute binary images with the traditional unix-style segment layout of .text, .data and .bss, with .text loaded at 4096 (to allow the bottom page of the segment to be unmapped for NULL protection, if I ever figure out how to do that). There's then a header which contains the bootstrap code and .exe header. This sets up DPMI, switches to protected mode, allocates memory for the main program, loads it and jumps to it. I'd expect 90% of the complexity to be in the system call interface (the other 90%, of course, being the DPMI setup). I shouldn't have to write any additional ACK tooling. Maybe.
 
It's done: https://github.com/davidgiven/ack/tree/default/plat/msdos386 It was both less and more work than I expected, as DPMI is pretty special. Thank goodness for DEBUG.COM. I'm using CWSDPMI which doesn't provide a DOS extender so there's a lot of nasty stack and segment juggling whenever calling any DOS interrupts which get passed pointers. It would have been easier if I could have somehow mapped real-mode memory into the 32-bit linear address space, but, er, I couldn't figure out how.
 
Back
Top