Skip to main content

um80 - Microsoft MACRO-80 Compatible Toolchain for Linux

A complete Unix/Linux implementation of Microsoft's classic CP/M development tools from the 1980s:

  • um80 - MACRO-80 compatible assembler for 8080/Z80
  • ul80 - LINK-80 compatible linker
  • ulib80 - LIB-80 compatible library manager
  • ucref80 - Cross-reference utility
  • ud80 - 8080/Z80 disassembler for CP/M .COM files
  • ux80 - 8080 to Z80 assembly source translator

These tools can assemble, link, and manage 8080/Z80 assembly code to produce CP/M-compatible .COM executables on modern Linux systems.

Installation

From PyPI (recommended)

pip install um80

From source

git clone https://github.com/avwohl/um80_and_friends.git
cd um80_and_friends
pip install -e .

Quick Start

Assemble a source file

um80 program.mac                    # Creates program.rel
um80 -o output.rel program.mac      # Specify output name
um80 -l listing.prn program.mac     # Generate listing file
um80 -g program.mac                 # Export all symbols as PUBLIC (for debug)
um80 -t program.mac                 # Truncate symbols to 8 chars (M80 compat)
um80 -e ".z80" program.mac          # Execute code before source (set Z80 mode)
um80 --pre macros.mac program.mac   # Include file before source

Link object files

ul80 program.rel                    # Creates program.com
ul80 -o output.com a.rel b.rel      # Link multiple files
ul80 -s program.rel                 # Generate symbol file (.sym)
ul80 -S symbols.sym program.rel     # Specify symbol file name
ul80 -p E000 program.rel            # Set origin address (hex)
ul80 --prl program.rel              # Output MP/M .PRL format

Disassemble a COM file

ud80 program.com                    # Creates program.mac
ud80 -z program.com                 # Z80 mode
ud80 -e 0200 program.com            # Add entry point at 0200h
ud80 -d 0500-05FF program.com       # Mark range as data

Create/manage libraries

ulib80 -c mylib.lib a.rel b.rel     # Create library
ulib80 -l mylib.lib                 # List contents
ulib80 -p mylib.lib                 # Show public symbols
ulib80 -x mylib.lib module          # Extract module
ulib80 -a mylib.lib new.rel         # Add module
ulib80 -d mylib.lib module          # Delete module

Generate cross-reference

ucref80 program.mac                 # Print to stdout
ucref80 -o xref.txt *.mac           # Output to file

Translate 8080 to Z80 assembly

ux80 program.mac                    # Creates program_z80.mac
ux80 -o output.mac program.mac      # Specify output name

Tools Reference

um80 - Assembler

Microsoft MACRO-80 compatible assembler supporting:

  • 8080 and Z80 instruction sets
  • Macros with parameters (MACRO/ENDM)
  • Repeat blocks (REPT, IRP, IRPC)
  • Conditional assembly (IF/ELSE/ENDIF, IFDEF, etc.)
  • Segments (CSEG, DSEG, ASEG, COMMON)
  • PUBLIC/EXTRN for module linking
  • Include files
  • All standard directives (ORG, EQU, SET, DB, DW, DS, etc.)

Command-Line Pre-Execution (-e and --pre)

Code can be injected before the main source file using -e (inline code) and --pre (include file). Both options can be repeated and are processed in the order specified:

# Set Z80 mode from command line
um80 -e ".z80" program.mac

# Multiple statements using ! separator (DRI notation)
um80 -e ".z80!DEBUG equ 1!BUFSIZE equ 256" program.mac

# Include a file of macros before the main source
um80 --pre stdmacros.mac program.mac

# Combine both, processed left to right
um80 -e ".z80" --pre macros.mac -e "MYVAL equ 42" program.mac

This is useful for:

  • Switching CPU mode (.z80 or .8080) without modifying source files
  • Defining conditional assembly symbols (DEBUG equ 1)
  • Including project-wide macro libraries

See man um80 for full documentation, or refer to the original Microsoft M80 Manual.

ul80 - Linker

LINK-80 compatible linker that:

  • Links multiple .REL relocatable object files
  • Resolves external references
  • Produces CP/M .COM executables
  • Supports COMMON blocks
  • Can output Intel HEX format
  • Can output MP/M .PRL (Page Relocatable) format
  • Provides __END__ symbol for dynamic memory allocation

Predefined Symbols

The linker provides a predefined __END__ symbol that points to the first free byte after all linked segments (code + data + common blocks). This is useful for implementing heap allocation:

        EXTRN   __END__         ; Import linker symbol

START:  LXI     H,__END__       ; Load end of program
        SHLD    HEAP            ; Initialize heap pointer
        ...

        DSEG
HEAP:   DW      0               ; Heap pointer

See man ul80 for full documentation, or refer to the original Microsoft L80 Manual.

ulib80 - Library Manager

LIB-80 compatible library manager for:

  • Creating .LIB library archives
  • Listing library contents and public symbols
  • Adding/removing/extracting modules

See man ulib80 for full documentation, or refer to the original Microsoft CREF/LIB Manual.

ucref80 - Cross-Reference

Generates cross-reference listings showing:

  • Symbol definitions
  • Symbol references by file and line
  • PUBLIC and EXTRN declarations

See man ucref80 for full documentation.

ud80 - Disassembler

8080/Z80 disassembler that:

  • Disassembles CP/M .COM files to .MAC source
  • Produces output compatible with um80
  • Supports both 8080 and Z80 instruction sets
  • Allows marking data ranges and entry points
  • Generates re-assemblable source code

See man ud80 for full documentation (no Microsoft equivalent exists).

ux80 - 8080 to Z80 Translator

Source-to-source translator that converts Intel 8080 assembly to Zilog Z80 assembly:

  • Translates all 8080 instructions to equivalent Z80 mnemonics
  • Preserves all comments, labels, and formatting
  • Produces byte-identical output when assembled
  • Automatically adds .Z80 directive to output
  • Handles all assembler directives (passes them through unchanged)

Translation examples:

8080 Z80
MOV A,B LD A,B
MVI A,42H LD A,42H
LXI H,1234H LD HL,1234H
LDA addr LD A,(addr)
LHLD addr LD HL,(addr)
LDAX B LD A,(BC)
INR A INC A
INX H INC HL
DAD D ADD HL,DE
ADD B ADD B
ADI 10 ADD 10
JMP addr JP addr
JNZ addr JP NZ,addr
CALL addr CALL addr
CNZ addr CALL NZ,addr
RET / RNZ RET / RET NZ
RLC RLCA
CMA CPL
HLT HALT
PCHL JP (HL)
XCHG EX DE,HL
IN port IN A,(port)
OUT port OUT (port),A
PSW AF
M (memory) (HL)

See man ux80 for full documentation.

File Formats

Extension Description
.MAC Assembly source (MACRO-80 format)
.REL Relocatable object file
.COM CP/M executable
.PRL MP/M Page Relocatable executable
.LIB Library archive
.PRN Assembly listing
.SYM Symbol file

Compatibility Notes

These tools aim for compatibility with the original Microsoft tools while running on modern Unix/Linux systems:

  • Source files use Unix line endings (LF), but CR/LF is also accepted
  • File names are case-insensitive for symbols (converted to uppercase)
  • Default origin is 0100h (standard CP/M load address)
  • Output files are binary-compatible with original CP/M tools

8080 is the default; Z80 mnemonics need .Z80

Like M80, um80 assembles 8080 mnemonics until a .Z80 directive appears (use um80 -e .z80 file.mac to set the mode from the command line without editing the source). Where a Z80 instruction is spelled with an 8080 mnemonic — RET NZ, RLC B, RRC C — um80 rejects it in 8080 mode instead of dropping the operand:

$ um80 z80source.mac
Error at line 5: RET takes no operand, but was given 'NZ' RET NZ is Z80 syntax;
the 8080 spelling is RNZ. Add a .Z80 directive to assemble Z80 mnemonics.

Genuine M80 3.44 only flags these Q and emits the operand-less opcode, so RET NZ assembles as an unconditional RET (C9) and the .REL is still written. um80 makes it an error because that is a silent miscompile; this is a deliberate divergence, recorded in CHANGELOG.md. Nothing is written and the exit status is 1, as for any other assembly error.

Extended Symbol Names

The original Microsoft REL format limits symbol names to 8 characters. um80/ul80 extend this to support symbols up to 255 characters, which is essential for:

  • External references with offsets (e.g., MEMSEGTBL+2 stays intact instead of truncating to MEMSEGTB)
  • Long descriptive symbol names in modern code
  • Compatibility with source code written for other assemblers

Use -t or --truncate to disable this extension for strict M80 compatibility.

See docs/EXTENSIONS.md for technical details on the extended REL format.

DRI Extensions

um80 supports several Digital Research (DRI) assembly syntax extensions commonly found in CP/M and MP/M source code. These extensions are compatible with DRI's ASM, MAC, and RMAC assemblers.

Multi-Statement Lines (! separator)

Multiple instructions can be placed on a single line, separated by !:

        PUSH H! PUSH D! PUSH B      ; Save registers
        POP B! POP D! POP H         ; Restore registers
        MOV A,B! ORA A! RZ          ; Test and return if zero

LOW and HIGH Operators

Extract the low or high byte of a 16-bit value using function-call syntax:

        MVI L,LOW(BUFFER)           ; Load low byte of address
        MVI H,HIGH(BUFFER)          ; Load high byte of address
        MVI A,LOW(1234H)            ; A = 34H
        MVI B,HIGH(1234H)           ; B = 12H

Both LOW(expr) and HIGH(expr) syntax (with parentheses) and LOW expr / HIGH expr syntax (with space) are supported.

Digit Separators in Numbers ($)

The $ character can be used as a visual separator within numeric literals for readability:

        MVI A,1111$0000B            ; Binary with separator
        LXI H,1$0000H               ; Hex: 10000H
        MVI B,1$000D                ; Decimal: 1000

The $ characters are ignored during parsing and do not affect the numeric value.

Register Aliases via EQU

Symbols can be defined with EQU to represent registers, then used in place of register names:

; Define register aliases using register names
UR      EQU     B                   ; UR is an alias for register B
LR      EQU     C                   ; LR is an alias for register C
MR      EQU     E                   ; MR is an alias for register E
KR      EQU     H                   ; KR is an alias for register H (or HL for pairs)

; Use aliases in instructions
        MVI MR,0                    ; Same as MVI E,0
        MOV A,UR                    ; Same as MOV A,B
        INR LR                      ; Same as INR C
        LXI KR,0                    ; Same as LXI H,0 (H maps to HL for pairs)

For register pair instructions, single register aliases are automatically promoted:

  • B or C → BC
  • D or E → DE
  • H or L → HL

PUSH A / POP A

DRI assemblers allowed PUSH A and POP A as synonyms for PUSH PSW and POP PSW:

        PUSH A                      ; Same as PUSH PSW (push A and flags)
        POP A                       ; Same as POP PSW (pop A and flags)

Register number assignments (when using numeric values):

Number 8-bit Register 16-bit Pair
0 B BC
1 C DE
2 D HL
3 E SP
4 H -
5 L -
6 M (memory) -
7 A -

External Symbol Aliases (EQU external+offset)

Symbols can be defined as aliases to external symbols with an optional offset, then exported as PUBLIC:

; In library module - define entry points
        EXTRN   ADD10           ; External symbol from another module
        PUBLIC  ADD10_SKIP      ; Export the alias

; Define alias: ADD10_SKIP is ADD10+2 (skip first instruction)
ADD10_SKIP  EQU ADD10+2

This is useful for:

  • Defining alternate entry points into routines (skipping initialization code)
  • Creating symbolic offsets into data structures defined in other modules
  • Porting code from assemblers that support this feature (like z88dk)

The alias is resolved at link time:

; In main module - use both symbols
        EXTRN   ADD10
        EXTRN   ADD10_SKIP

START:  CALL    ADD10           ; Call full routine
        CALL    ADD10_SKIP      ; Call at offset (skips first 2 bytes)

For more details on these extensions and compatibility notes, see docs/EXTENSIONS.md.

Documentation

  • Man pages: man um80, man ul80, man ulib80, man ucref80, man ud80, man ux80
  • Original Microsoft manuals in docs/external/:
    • m80.pdf - MACRO-80 assembler
    • l80.pdf - LINK-80 linker
    • cref_lib.pdf - CREF and LIB-80
    • 8080asm.pdf - 8080 assembly reference

Testing

The test suite (143 tests) runs under pytest:

pip install -e ".[dev]"
pytest                                    # run everything
pytest tests/test_expr_precedence.py -v   # one file, verbose

The MACRO-80 compatibility tests below were validated against the genuine Microsoft MACRO-80 / LINK-80 3.44 binaries — each expected value was confirmed by assembling the same source with the real assembler — so they pin um80/ul80 to documented M80 behavior:

File Covers
test_expr_precedence.py Operator precedence (unary -/NOT/HIGH/LOW, relational, shifts)
test_macro_bang_args.py DRI ! separator vs. M80 argument-quote !, escaped commas
test_macro_concat.py & concatenation: leading/trailing/shared, in-string &param, case folding
test_macro_expansion.py EXITM in conditionals, macro shadowing built-ins, string-safe substitution, NUL
test_repeat_blocks.py Nested REPT/IRP/IRPC, IRP sublists, EXITM
test_radix_conditional.py Decimal .RADIX operand, unterminated / duplicate-ELSE diagnostics
test_symbol_class.py SET vs. EQU/label redefinability classes
test_z80_and_charconst.py LD A,I/LD A,R encoding, two-character constant byte order
test_linker_segments.py Absolute ASEG placement, mixed CSEG+ASEG, COMMON-only modules
test_linker_dupglobal.py Multiply-defined PUBLIC global is a link error

Further tests cover the toolchain more broadly: test_ds_org.py (DS/ORG and segment placement), test_defs_fill.py (DEFS fill value), test_end_symbol.py (END entry symbol and __END__), test_ext_alias.py (external aliases), test_jr_promotion.py (JR/DJNZ out-of-range promotion), test_no_operand_strict.py (an operand on a no-operand instruction is an error, the one deliberate divergence from M80), and test_case_sensitivity.py.

Example Workflow

# Assemble source files
um80 -o main.rel main.mac
um80 -o util.rel util.mac

# Create a library
ulib80 -c mylib.lib helper.rel support.rel

# Link everything together
ul80 -o program.com main.rel util.rel mylib.lib

# Run in CP/M emulator
cpm program.com

Installing Man Pages

After pip installation, install the man pages manually:

# Find where the package is installed
PKGDIR=$(python3 -c "import um80; print(um80.__path__[0])")

# Copy man pages to system location (requires sudo)
sudo cp "$PKGDIR/../docs/man/"*.1 /usr/local/share/man/man1/
sudo mandb

Or view them directly:

man docs/man/um80.1

Related Projects

  • 80un - Unpacker for the CP/M archive and compression formats LBR, ARC, squeeze, crunch, and CrLZH.
  • cpmdroid - Z80/CP/M emulator for Android phones and tablets. It emulates the RomWBW HBIOS interface and a VT100 terminal.
  • cpmemu - Z80/CP/M emulator for Linux and Windows, with Z80 and 8080 CPU cores. It translates the BDOS and BIOS calls of CP/M 2.2 programs to the host file system.
  • ioscpm - Z80/CP/M emulator for iOS and macOS. It emulates the RomWBW HBIOS interface and runs CP/M 2.2 and CP/M 3.
  • learn-ada-z80 - Collection of more than 90 Ada example programs for uada80, the Ada compiler for the Z80 processor and CP/M.
  • mbasic - Python interpreter for MBASIC 5.21, the Microsoft BASIC-80 for CP/M. Two compiler backends compile the programs to CP/M .COM files or to JavaScript.
  • mbasic2025 - Reconstruction of the lost source code of MBASIC 5.21, the Microsoft BASIC-80 for CP/M. The MACRO-80 source code assembles to a binary that matches mbasic.com byte for byte.
  • mbasicc - C++17 interpreter for MBASIC 5.21, the Microsoft BASIC-80 for CP/M. It runs on Linux and macOS.
  • mbasicc_web - Web browser interpreter for MBASIC 5.21, the Microsoft BASIC-80 for CP/M. Emscripten compiles the mbasicc interpreter to WebAssembly.
  • mpm2 - Z80 emulator for MP/M II, the multi-user CP/M operating system. Users connect over SSH, and SFTP clients transfer files.
  • romwbw_emu - Hardware-level Z80/CP/M emulator for Linux and macOS. It emulates the RomWBW HBIOS interface and switches banks in 512 KB of ROM and 512 KB of RAM.
  • scelbal - Floating-point BASIC interpreter for the 8080 processor and CP/M. A translator converts the original 8008 source code to 8080 source code.
  • uada80 - Ada compiler for the Z80 processor and CP/M 2.2. It compiles a subset of Ada 2012 to CP/M .COM files.
  • uc80 - C compiler for the Z80 processor and CP/M. It optimizes for small code size.
  • ucow - Cowgol compiler for the Z80 processor and CP/M. It runs on Linux in Python.
  • upeepz80 - Peephole optimizer for Z80 compilers that write lowercase Z80 assembly language. It shortens jumps to jr, builds djnz loops, and removes dead stores.
  • uplm80 - PL/M-80 compiler for the Z80 processor and CP/M. It writes Intel 8080 and Zilog Z80 assembly language.
  • z80cpmw - Z80/CP/M emulator for Windows. It emulates the RomWBW HBIOS interface and boots CP/M from disk images.

Release files for um80 0.3.46

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for um80 0.3.46
File Size Uploaded
um80-0.3.46.tar.gz 143.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for um80 0.3.46
File Interpreter ABI Platform
um80-0.3.46-py3-none-any.whl Python 3 none any Details

Total release size: 240.7 kB

Release files / um80-0.3.46.tar.gz

Download URL um80-0.3.46.tar.gz
Size 143.2 kB
Tags Source
SHA-256 checksum
How to use checksums
56d924006457796c0d24e4b9aa9f22d8ca03ba2974125ccecd6ad7c33d459e0a
BLAKE2b-256 checksum
How to use checksums
48213251c262f2105add05aaca470a419f712a753b67e2e03c2c88522f050dca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release files / um80-0.3.46-py3-none-any.whl

Download URL um80-0.3.46-py3-none-any.whl
Size 97.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e7ac9adbcc500a4001029171a3464b7e0096be6b62cf67dd24939d3b5f286a7d
BLAKE2b-256 checksum
How to use checksums
8095e6315d2accaea06aac9ada8e6d384d33844037805f190299c337fe94e7c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.46 This release

2 release files

0.3.43

2 release files

0.3.42

2 release files

0.3.37

2 release files

0.3.36

2 release files

0.3.35

2 release files

0.3.34

2 release files

0.3.33

2 release files

0.3.24

2 release files

0.3.23

2 release files

0.3.22

2 release files

0.3.20

2 release files

0.3.19

2 release files

0.3.18

2 release files

0.3.17

2 release files

0.3.16

2 release files

0.3.15

2 release files

0.3.14

2 release files

0.3.13

2 release files

0.3.12

2 release files

0.3.11

2 release files

0.3.10

2 release files

0.3.9

2 release files

0.3.8

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.0

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page