CP/M-86 & MS-DOS Cross Development Environment

May 24, 2026 · View on GitHub

This project regroups a simple method to create a cross development environment for CP/M-86. It happens that all DOS based tools can also be used for building DOS programs. This project was developed for myself in a nerdy spirit and for a lot of fun. Just sharing without any guarantees.

Languages supported are:

  • C (K&R and almost ANSI)
  • Assembler
  • Basic

Where to find CP/M-86?

The source for CP/M-86 doc, sources and binaries is http://www.cpm.z80.de.

A cleaned-up distribution and kernel is available at https://github.com/tsupplis/cpm86-kernel. This distribution is working well in virtual environments, patched with all known patches, 'y2k' friendly (it contains the version of tod which sources are in this project) and AT friendly.

Key tools for CP/M-86 development

  • aztec c compiler version 3.4/3.40a (K&R, the CP/M-86 library is provided as c86.lib), patched

  • aztec c compiler version 4.2/4.10d (Almost ANSI, the code for the CP/M-86 library (c86.lib) is patched and recompiled from 3.4 sources, as it is not part of the default compiler package. a dos 1.1 library (d11.lib) is also provided in the same manner), the documentation can be found at (https://www.aztecmuseum.ca/docs/Aztec_C_MSDOS_4.10C_Commercial_Apr88.pdf)

  • rasm86/link86,lib86 DOS version from Digital Research

  • asm86.com and gendef.com from Digital Research

  • cb86.exe and libraries from Digital Reasearch

  • nasm netwide assembler

  • masm, link, asm, exe2bin, hex2bin from Microsoft (the version of masm in this repository has been patched to work with emu2 and other emulators, see https://github.com/tsupplis/pcdos11-hacking for details). asm.com and hex2bin.com have been rebuilt for modified sources at https://github.com/tsupplis/pcdos11-hacking .

  • The Super Cool emu2 DOS emulator to run the DR tools on macOS and Linux (https://github.com/dmsc/emu2). This is an incredible way to bring dos command line development tools to a modern and up to date shell/make/whatever based dev environment. Another stunning emulator. Emu2 and PCE are an incredible pair.

  • We also need to run some cp/m-80 programs, for that, the tinylpo emulator is used (https://gitlab.com/gbrein/tnylpo). It works very well with asm86.com and gencmd.com programs

License Considerations

Script Mapping

All the tools are wrapped in the bin directory for direct usage:

script nameprogramquick description
cpm_asm86asm86.cmdDR assembler (CP/M-80 tool)
cpm_gencmdgencmd.cmdDR H86 converter (CP/M-80 tool)
pcdev_rasm86rasm86.exeDR assembler (OBJ)
pcdev_linkcmdlinkcmd.exeDR linker for CP/M-86
pcdev_link86linkcmd.exe" " " " " "
pcdev_linkexelinkexe.exeDR linker for DOS
pcdev_lib86lib86.exeDR librarian
pcdev_masmmasm.exeMicrosoft assembler
pcdev_linklink.exeMicrosoft linker
pcdev_exe2binexe2bin.exeMicrosoft EXE converter
pcdev_hex2binhex2bin.comMicrosoft HEX converter
pcdev_asmasm.comMicrosoft/86-DOS assembler
pcdev_cmdinfocmdinfo.comCMD info tool
pcdev_bin2cmdbin2cmd.comCMD converter
aztec34_asas.exeAztec Assembler.
aztec34_cccc.exeAztec K&R C compiler
aztec34_sqzsqz.exeAztec C object optimizer
aztec34_linkln.exeAztec C linker
aztec34_liblb.exeAztec C lib
aztec34_ordord.exeAztec C library ordering helper
aztec34_obdobd.exeAztec C object dump
aztec34_hex86hex86.exeAztec C H86 generator
aztec42_asas.exeAztec Assembler.
aztec42_cccc.exeAztec ANSI C compiler
aztec42_sqzsqz.exeAztec C object optimizer
aztec42_linkln.exeAztec C linker
aztec42_liblb.exeAztec C lib
aztec42_ordord.exeAztec C library ordering helper
aztec42_obdobd.exeAztec C object dump
aztec42_hex86hex86.exeAztec C H86 generator
drcbcpm_bccb86.exeDR cbasic compiler for CP/M-86
drcbcpm_linklink86.exeDR cbasic linker for CP/M-86
drcbdos_bccb86.exeDR cbasic compiler for DOS
drcbdos_linklinkexe.exeDR cbasic linker for DOS
cpm86-CP/M-86 emulator
hexcom-HEX to binary translation
doscat-Truncate files beyond ^Z
nasm-Netwide assembler

Fetching the tools

The development environment can be assembled by using the following steps:

./fetch_tools
export PATH=`pwd`/bin

it pulls the following:

Clearing the directory is achieved by:

./clear_tools

Download archive / offline rebuilds

Every file downloaded by fetch_tools (the Aztec, DR, CB86 and NASM archives, but not the git checkouts) is cached under archive/ instead of being deleted.

This keeps the build environment reproducible even if an upstream disappears:

  • If a download fails, fetch_tools falls back to the cached copy in archive/.
  • Set ARCHIVE_FIRST=1 to rebuild entirely from archive/ without touching the network (e.g. offline, or to pin exact versions):
    ARCHIVE_FIRST=1 ./fetch_tools
    
  • clear_tools will not remove the archive/, so a cleared tree can always be rebuilt from the local cache.

Testing

The cpm86 tool is a cpm86 emulator for dos pulled with Aztec C. We have reverse engineered it and patch it to fix a few bugs and add some new features. It should not be relied on fully as some bugs surely remain, but it can be experimented with and works for many programs. For build tools DOS/CP/M-80 emulations and DOS/CP/M-80 cross compilations are far better options and proper emulation of CP/M-86 is the best option for testing; PCE is a very good choice (http://www.hampa.ch/pce/).

Note that the enhanced cpm86 emulator supports padding records with EOF instead of NULL which may make working with text files easier, but because this behavior does not match real CP/M-86, it's disabled by default and only enabled if you set CPM86_EOF=1, for example:

env CPM86_EOF=1 cpm86 program.cmd

This may be a next step:

  • automating pce, cpmtools
  • bootable/up-to-date CP/M-86 floppy ...

Docker image

A Dockerfile is provided for this environment. To build the image from the docker directory, jusr run make, alternatively:

docker build --rm=true -t cpm86:latest -f Dockerfile .

to user the created image, just ensure that the local path where the compilation happense ismounted properly:

docker run -it --rm -h cpm86 -v `pwd`:/work -w /work cpm86 pcdev_rasm86 helloa.a86
docker run -it --rm -h cpm86 -v `pwd`:/work -w /work cpm86 aztec34_cc helloc.c

The only difference on the command line happens with ;

pcdev_masm hellod \; 

... becomes ...

docker run -it --rm -h cpm86 -v `pwd`:/work -w /work cpm86 pcdev_masm hellod '\\;' 

Using the tools

Finally, a simple Makefile with a sample c, assembler for rasm86, assembler for asm86 is provided:

./build_demo

Basic Programs

drcbcpm_bc hellor.bas
drcbcpm_link hellor.cmd=hellor.o
pcdev_cmdinfo hellor.cmd

C Programs

aztec34_cc helloc.c
aztec34_sqz helloc.o
aztec34_link -o helloc.cmd helloc.o -lc86
pcdev_cmdinfo helloc.cmd

if the code is using ANSI syntax ...

aztec42_cc helloc.c
aztec42_sqz helloc.o
aztec42_link -o helloc.cmd helloc.o -lc86
pcdev_cmdinfo helloc.cmd

C runtime startup

aztec34_link/aztec42_link automatically prepend the matching C runtime startup object, so the $begin -> Croot_ -> main -> exit entry/exit code is always present. The startup is selected from the C library you link against:

librarytarget / modelstartup
-lc86CP/M-86 smallbegin86.o
-lc / -lclcMS-DOS smallsbegin.o
-lcl / -lcldMS-DOS largelbegin.o

These startup objects are produced from the C libraries by fetch_tools (src/fetch/buildstartups). Without this, Aztec's single-pass ln only pulls the startup from the library on demand, so a program that references no libc symbol — e.g. int main(void){ return 0; } — would link with no startup and crash on exit, and a program that references the startup indirectly (e.g. exit()) could fail to link (Undefined symbol: _exit_) unless the library was ordered with ord or passed twice (-lc86 -lc86). Prepending the startup as a command-line object fixes all of these cases, so neither ord nor a doubled library is needed. To restore the old behaviour set AZTEC_NOSTARTUP=1, or force a specific startup with AZTEC_STARTUP=<obj-in-lib>.

Assembler Programs with rasm86

pcdev_rasm86 helloa.a86 '$' pz sz
pcdev_linkcmd helloa '[$sz]'
pcdev_cmdinfo helloa.cmd

Assembler Programs with asm86

cpm_asm86 hellob.a86
cpm_gencmd hellob.h86
pcdev_cmdinfo hellob.cmd

Assembler Programs with masm

pcdev_masm hellod \;
pcdev_link hellod \;
pcdev_exe2bin hellod.exe
pcdev_bin2cmd hellod.bin hellod.cmd

Assembler Programs with nasm

nasm hellon.asm -fbin -o hellon.bin 
pcdev_bin2cmd hellon.bin hellon.cmd

You can build a native unix/dos version of bin2cmd/cmdinfo from (https://github.com/tsupplis/cpm86-cmdtools)