4

I'd like to create tiny 32-bit (i386) executables for DOS.

As a reference, here is the NASM assembly source code of my tiny 16-bit (8086) executable for DOS:

; $ nasm -o hi16.com hi16.nasm  # 26 bytes.
bits 16
org 0x100
mov dx, msg  ; 16-bit pointer to string.
mov ah, 9  ; Print message.
int 0x21
ret  ; exit(0).
msg: db 'Hello, World!', 13, 10, '$'

Since I want to use more than 1 MiB of memory in my 32-bit DOS executables, most probably I need a DOS extender. After looking at multiple DOS extenders, I've decided to try WDOSX, which seems to have the smallest stub (.exe prefix for setting up protected mode): WDOSX.DX (see here how to get it) is just 9720 bytes. The source code of my (wannabe) tiny 32-bit DOS executable is:

; $ nasm -o hi32.exe hi32.nasm  # 37+9720 bytes.
bits 32
wdosx_dx_start:
incbin "WDOSX.DX"  ; ~9720 bytes.   
org wdosx_dx_start-$
mov ax, 0x901
int 0x31  ; Enable virtual interrupts.
mov edx, msg  ; 32-bit pointer to string.
mov ah, 9  ; Print message.
int 0x21
mov ax, 0x4c00  ; exit(0).
int 0x21
msg: db 'Hello, World!', 13, 10, '$'

Both of these executables (hi16.com and hi32.exe) work out-of-the-box in DOSBox. By using DPMI function 0x0501 my 32-bit DOS program will able to allocate memory blocks larger than 1 MiB, thus my goal is fulfilled.

My question: Is there a stub smaller than WDOSX.DX (9720 bytes) I could use? WDOSX provides many features of a DPMI 0.9 host, and I don't need most of them, e.g. I don't need support for many binary formats (e.g. LE, PE), VCPI, INT15, 32-bit DOS API (all functions), mouse API, most of the DPMI API.

The features I need:

  • It has to work if there is a DPMI host (e.g. DOS window in Windows or HDPMI32.EXE running), and it has to work if XMS is available (but no v8086). I don't care about other environments, e.g. EMM386, VCPI, INT15.
  • Allocate several MiB of memory.
  • Upon program exit, release the allocated memory.
  • Open a DOS file by name, read it, write it, close it. It's OK if I have to copy data manually between a low memory address (<1 MiB, used by DOS) and my allocated large buffer.

I'm looking for a link to code samples or finished implementation of these features with XMS (unreal mode?) and using the DPMI API.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
pts
  • 80,836
  • 20
  • 110
  • 183
  • 3
    You can enter unreal mode using much less code than that. Heck, you can even write up a protected mode FAT file handler and ATA PIO in less. Depending on where you want to read from. – Jester Jun 29 '20 at 20:16
  • 1
    @Jester : true Jester although it is unclear what kind of DOS environment they are in. Do they have a memory manager (or some other driver/software) that already put the processor in v8086 mode? If so unreal mode isn't an option. This question despite the details given is quite broad. – Michael Petch Jun 29 '20 at 20:23
  • @MichaelPetch: It has to work if there is a DPMI host (e.g. DOS window in Windows or HDPMI32.EXE running), and it has to work if XMS is available (but no v8086). I don't care about other environments. I've updated the question. – pts Jun 29 '20 at 20:25
  • I am puzzled, if you already have DPMI/XMS then what exactly is the problem? – Jester Jun 29 '20 at 20:28
  • @Jester: I think there will be ~20 pieces to put together for both XMS and DPMI, and since I'm a beginner in this, I'd appreciate some code samples, preferably which have multiple pieces already. Somebody must have done huge chunks of this work already, I just don't know how to find it. Mentioning *unreal mode* as a search query already helped. – pts Jun 29 '20 at 20:32
  • It looks like the Smaller C compiler (https://wiki.osdev.org/Unreal_Mode#Smaller_C) can already target unreal mode. However, I also need DPMI and 32-bit code (unreal mode uses 16-bit code). – pts Jun 29 '20 at 20:38
  • 1
    You can use 32 bit instructions just fine in 16 bit/unreal mode with prefixes if your cpu is 32/64 bit. – Jester Jun 29 '20 at 21:10
  • 2
    If you don't need to access the memory directly (but you can accept calling some "`memcpy`-like" function to access memory above 1M), you can use the XMS driver directly. – Martin Rosenau Jun 30 '20 at 04:40
  • @Jester: I do need 32-bit instructions without prefixes, because my compiler generates code without prefixes. – pts Jun 30 '20 at 13:36
  • @MartinRosenau: I need to access memory with 32-bit instructions directly, because my compiler generates such code. Also the same application code has to work with both XMS and DPMI, so an XMS-only solution doesn't solve my problem. – pts Jun 30 '20 at 13:36
  • 2
    Writing an executable that runs with DPMI should be possible with much less than 9000 bytes (maybe 500 bytes). However, it would not work without DPMI. Writing a 32-bit executable that only requires XMS should be possible in ~1000 or 2000 bytes. However, it would not run if the CPU is in virtual mode (e.g. if an EMS driver is loaded or in the DOS window of Windows). – Martin Rosenau Jul 02 '20 at 18:18
  • @MartinRosenau: My plan was to use DPMI if available, otherwise use XMS, all in 2500 bytes. How do DOS extenders provide DPMI if an EMS driver is loaded -- how do they get out of virtual mode? – pts Jul 03 '20 at 14:32
  • 2
    @pts I just looked at the source code of `loadlin`: If the CPU runs in real mode, the tool executes own code to enter protected mode. If VCPI or DPMI is available, VCPI or DPMI is used. If virtual mode is active but there is neither VCPI nor DPMI, there is no chance to get into protected mode. In MS-DOS there is the possibility to deactivate `EMM386` using `EMM386 off` **if** no device driver uses EMS memory. – Martin Rosenau Jul 04 '20 at 05:34
  • 1
    Did you ever get a solution to this? I'm in a very similar position --- I just want to write DPMI executables from scratch with as small a stub as possible. – David Given Aug 03 '22 at 12:30
  • @DavidGiven: Download *pmode307.zip* (53 KiB, find it with Google), it contains example.asm, pmode.asm and makeex.bat. You won't need pmode.asm if there is a DPMI host already running, but you have to modify example.asm to autodetect and use it. – pts Aug 03 '22 at 13:03

1 Answers1

2

TL;DR The smallest achievable overhead overhead for writing 32-bit DOS programs is 5800 .. 9800 bytes.

I've looked around for DOS extenders, and here is what I've found:

  • It's possible to build a solution upon the PMODE 3.07 source code, giving <7500 bytes of DOS extender overhead in the program, 5800 bytes if compressed), but it needs substantial amount of work becaue of manual setup and relocations. TASM source code is available.
  • D3X dx3lite.exe can do it out-of-the-box with 8200 bytes compressed. TASM source code is available. Currently only FASM assembler specially modified for the Adam + relocations binary format can be used for development, but it should be possible to generate an object file (ELF, OMF or COFF) with NASM and convert it manually.
  • WDOSX 0.97 with 9720 bytes compressed is a convenient option, because it doesn't need any headers, and it works without relocations.
  • Others are incomplete or add at least 10000 bytes of overhead.

More details about each DOS extender I've considered and the overhead they add:

  • Xi Development Systems 1997-08-15: 4303 bytes; doesn't support VCPI, doesn't do relocations, doesn't have file I/O
  • SYSTEM 64 v1.210: 6392 bytes, the actual program is 16408 bytes longer because of NUL blocks; most DOS file I/O functions are missing
  • PMODE 3.07: 7048 bytes, the actual program is 424 bytes longer because of NUL blocks in the DOS .exe header
  • RAW32 r3: 7270 bytes, the actual program is 4186 bytes longer because of NUL blocks, the actual program makes DOSBox crash
  • PMODE 2.05: 7937 bytes, the actual program is 5867 bytes longer because of of NUL blocks
  • D3X d3xlite.exe 2022-06-13 by CandyMan: 8200 bytes compressed
  • WDOSX 0.97: 9720 bytes compressed
  • PMODE/W 1.22: 10286 bytes
  • E.O.S 3.0.5 eoslite.obj: 10800 bytes when compressed with WWPACK, default is uncompressed
  • D3XX d3x.exe 2022-08-01: 10908 bytes
  • Pro32 version 17 and also the version distributed with Pass32 2.5 assembler: 11273 bytes, the created .exe program makes DOSBox crash
  • D3X 0.90 2004-10-09: 11880, the actual program is 4800 bytes longer because of NUL blocks
  • PMODE/W 1.33: 11783 bytes
  • ZRDX: 12400 bytes
  • E.O.S 3.0.5 weoslite.exe: 13078 bytes when compressed with WWPACK, default compressed
  • DOS/4GW, DOS/32A, PMODE/W, PMODE/DJ etc. add even more than 14000 bytes.
  • DOSX shipped with Symantec C++ 6.1 21549 bytes
pts
  • 80,836
  • 20
  • 110
  • 183