Note: Zig 0.12.0 removed "bring your own OS" support from the standard library. This project will remain paused on Zig 0.11.0 until a newer Zig release reintroduces BYOS support.
Write and cross-compile DOS programs with the Zig programming language. Programs run in 32-bit protected mode and require a resident DPMI host. CWSDPMI is bundled with the executable for environments that do not have DPMI available.
To comply with the CWSDPMI license, published programs must provide notice to users that they have the right to receive the source code and/or binary updates for CWSDPMI. Distributors should indicate a site for the source in their documentation.
This package is in a primordial state. It is a minimal demonstration of how to create a simple DOS program with Zig. Only basic file/terminal input/output are working, and does not include proper error handling. It will require hacking if you wish to adapt it for your own needs.
Install:
Setup submodules:
- Run the command
git submodule init && git submodule update
Add DOSBox to the path:
- Mac: Add
export PATH="$PATH:/applications/dosbox.app/contents/macos/"
to your~/.zshrc
. - Windows: https://stackoverflow.com/questions/9546324/adding-a-directory-to-the-path-environment-variable-in-windows
Run:
zig build run
There are five main components of this package:
- DOS API wrappers call the 16-bit real mode interrupt 21 routines (via DPMI) and implement operating system interfaces for the Zig standard library.
- DPMI API wrappers manage extended memory blocks and segments.
- A custom linker script produces DJGPP COFF executables.
- The CWSDPMI stub loader enters protected mode and runs the COFF executable attached to it.
- A small demo program exercises all of the above.
- Proper error handling.
- Parse environment data (command, variables) and hook into standard library abstractions.
- Implement
mprotect
for stack guard and zero pages. - Implement a
page_allocator
for the standard library. - Add graphical demo program.
It is technically possible, but not a goal of this package. Zig (via LLVM) can
generate "16-bit" code using the code16
ABI target. In reality, this code is
often 32-bit instructions with added prefixes that override the address or
operand size. Using code16
actually produces larger binaries. Additionally,
the oldest CPU that can be targeted is an Intel 80386, which supports 32-bit
protected mode. It's guaranteed to be there and has a lot of advantages, so we
might as well use it.
This was attempted and had mixed results. DJGPP's object format (COFF) is subtly different from the one produced by modern toolchains such as Zig. Crude conversion scripts had to be used to get them to work together and it was not robust. While it would be nice to leverage DJGPP's C library, it ultimately felt like more trouble than it was worth. Not relying on a separate toolchain will make things easier in the long run.