Control directives tell the disassembler to treat specified areas of the code to be disassembled as something other than executable code. The user specifies control directives by editing (with any text editor) a control file with the same name as the file to be disassembled, but with a file extention of .ctl (eg: program.ctl to control the disassembly of the file program.hex or program.bin). A generic control file called 'generic.ctl' is provided to get the user started. Copy 'generic.ctl' to 'file.ctl' where 'file' is the name of the binary or Intel hex file to be disassembled, and modify as required based on the output of the disassembler. Then run the disassembler again to get a more readable disassembly of the code.

All directives consist of a character that specifies the type of data, followed by a hexadecimal number specifying the value or range of values of the data. In the case of a label or symbol directive, the hexadecimal number is followed by ascii text defining the label or symbol. See individual directives for examples. All directives must be in the first column of the line. Directives may be complete words, but only the first character is significant, ie:

Label       0       reset                   and
looney       0       reset                    

will both generate the label "reset" for address 0. Note that they are not case sensitive.

Numerical values must be in hexadecimal and may optionally be preceeded by "0x". For example, a hexadecimal number may be entered as "1234" or "0x1234". Either will be interpreted as the number 1234H.

Everything following a semi-colon (;) is a comment and will be ignored by the disassembler.

Label and symbol directives differ in that a label refers to an address and a symbol refers to any 8 or 16 bit immediate data.
Symbols apply only to immediate data. To specify a symbol for an Z80 data memory location, use the R directive , or to force name substitution for an operand at a given address, use the X directive.

Single values are specified by entering just the value; ranges of values are specified by a start and stop address separated by a dash (-), or by a start address and count separated by a plus (+), ie:

t 1000             the data at address 1000H is ascii text.
t 1000-1010             the data from address 1000H to (and including) address 1010H is ascii text.
t 1000+6             ascii text starting at 1000H for 6 bytes.

Directives are processed in the order in which they are read from the control file. Specifying an area as ascii data and then later specifying it as data to be ignored (uninitialized data) will cause that area to not be disassembled at all. Care must be taken in specifying areas in the control file. 

A Directive (address data)

Specifies that the address contains a word value corresponding to an address for which a label should be generated. For example, a vector table may be located at address 0x1000 containing four entries. The user can modify the control file by adding the following entry:

a 1000-1007

This will cause the disassembler to generate the following output lines:

defw vec1
defw vec2
defw vec3
defw vec4

assuming that the control file also contains entries for the values found at those addresses to generate the labels vec1 through vec4. The addresses referenced will be flagged so that labels will be generated in the output file at those addresses. 

B Directive (byte binary data)

Specifies that the data is to be interpreted as 8 bit binary data. For example:

b 1000-1007

tells the disassembler that the data from address 1000H to 1007H should generate the line:

defb 0,1,2,3,4,5,6,7

assuming that the data at address 1000H and up is 00H, 01H, etc. 

C Directive (code data)

Forces the disassembler to interpret the data as executable code. This may be necessary because the disassembler skips over strings of 00H or ffH bytes that occur since they are unlikely to be real code. However, sometimes programmers insert several NOPs for timing purposes. 

D Directive (define data type (label or symbol))

d address [0 or label]         search label table only
d address [1 or symbol]         search symbol table only
d address [2 or none]         don't search either table

Forces the disassembler to search only the label or symbol table. This allows you to specify a label and a symbol for the same value.
Note that the last parameter can be either a number (0, 1, or 2) or a word (label, symbol, none). If the last parameter is a word, only the first character is checked for L, S, or N, and is not case sensitive.

I Directive (ignore data)

Tells the disassembler to ignore a range of addresses that may be initialized by the input file. This is useful when the input file is a binary file generated by an eprom programmer that dumps the entire eprom space. The valid data from a 4K eprom might only be, say, 3K in length. By adding

i c00-fff

to the control file, you tell the disassembler to not disassemble the data from address 0c00H to 0fffH.  

L Directive (label definition)

Defines a label to the disassembler. Labels are generated in the output disassembly file whenever a reference to the address is found and the label exists in the label table. Suppose that address 0000H contains the code 01 43 (ajmp 0043H). Then the entry

l 43 start

in the control file will cause mdz80 to disassemble this code as:

JP start

rather than:

JP X0043

The code beginning at address 43H will then have the label 'start' in the label field rather than the label 'X0043'. See the entry for the S directive for an explanation of the difference between labels and symbols and also -e command line option that can be used to automate this task.

N Directive (suppress address label generation)

The disassembler will generate labels for addresses that are referenced by code such as

LD A,6

If the operand represents a constant rather than an address, you can suppress the automatic generation of a label (X0006) for address 6h by entering

n 6

in the control file. You'd want to make sure that address 6h isn't legitimately addressed by some other code before using this directive. 

O Directive (add hexadecimal offset to addresses)

This directive causes the disassembler to add a hexadecimal offset to the address of every location in the code file. See the -x option for details. It will be overridden by any -x option specified on the command line. If the -t option (trace and analyze code) is given on the command line, the O directive has no effect since any existing control file will be rewritten. In this case you must use the command line -x option to specify an offset. 

P Directive (patch inline code)

This directive is of dubious value, but has been added for anyone who might find it useful. The P directive is similar to the comment directive except that the user supplied string is patched into the output stream as code, not as a comment. You could, for instance, patch in a macro definition or an include statement. Patching in executable code is not a real good idea. 

S Directive (symbol definition)

Defines a symbol to the disassembler. Symbols are output to the disassembly file whenever the value is encountered in the input file and the symbol exists in the symbol table. The user can specify a symbol for a value by a line such as:

s 20 space

Code which uses this value, such as:

LD A,20h

will then be disassembled as:


Labels and symbols differ in that a label applies to an address, whereas a symbol applies to any 8 or 16 bit immediate data. For code which can only refer to values, such as

LD A,20h

only the symbol table will be searched for a matching value. For code which could refer to either an address or a value, such as:

LD HL,(1234h)

the label table will be searched first, and only if no matching value is found will the symbol table then be searched. 

T Directive (text [ascii] data)

Forces the data in the range specified to be disassembled as text (ascii data). Thus the code: 48 69 20 74 68 65 72 65 at address 1000H will be disassembled as:

DEFB 'Hi there' or
ASCII 'Hi there' (command line option -a used)

by putting the line:

t 1000-1007

in the control file. Data specified by this directive is not checked to verify that it really is ascii data--be careful. 

W Directive (word binary data)

Specifies that the data is to be interpreted as 16 bit binary data. For example:

w 1000-1007

tells the disassembler that the data from address 1000H to 1007H should generate the lines:


assuming that the data at address 1000H and up is 00H, 00H, 00H, 01H, 00H, 02H, etc. This differs from the A (address) directive in that the A directive also causes a reference to be made to the indirect address. In other words, if the data at address 1000H is 1234H, the A directive will cause a label (X1234) to be put in the output file at address 1234H, whereas the W directive will not. 

X Directive (specify name for operand data)

Specify a name for an operand at a particular address. This is similar to the S directive except that it applies only to the given address. For example, if the symbol 'CR' is defined as 0dh, every instance of 0dh as an operand will have the symbol 'CR' substituted. It may be, however, that the value 0dh has a different meaning when used as an operand for a particular instruction. The X directive allows you to modify the interpretation of an operand at specific operand locations. Why not then use the X directive exclusively? Because that would force you to place an X directive in the control file for every instance in which that value appeared as an operand. The S directive, defined only once for an operand value, will substitute the symbol text for everyoccurrance of that operand value unless overridden by the X directive. Note that the address defined in the X directive is the address of the operand, not the address of the instruction. For example:

X 102 loopinit

forces substitution of the text 'loopinit' for the operand located at address 102h. Therefore an instruction that should be interpreted as


will not be disassembled as


if the control file contains the above x directive, and also contains an S directive defining 0dh as 'CR', and the value of the operand for the mov instruction happens to also be 0dh.

Y Directive (specify name for operand data but suppress EQU generation)

This is similar to the X directive except that the disassembler will not generate an EQU statement for the operand value in the output file. This should be used when you want to specify a name for an operand that is an assembler pseudo-op such as LOW() or HIGH(). 

# Directive (header comment string)

Defines a comment string to be output prior to disassembly at the specified address. For example:

        # 95
        # 95 Start of initialization
        # 95

in the ctl file would cause the output

        ; Start of initialization
                LD  A,80H           ;0095     3E 80     >.

assuming that the code 'c2 af' was located at address 0x95. Since ASCII strings consume prodigious amounts of memory, it is wise to use this directive sparingly. It is, however, useful for marking blocks of code that have been identified in previous runs of mdz80. 

! Directive (inline comment string)

Defines a comment string to be output after disassembly at the specified address. For example:

        ! 95 Start of initialization

in the ctl file would cause the output

                LD  A,80H           ;0095     3E 80     >.    ; Start of initialization

for the above example. Note that inline comments are only available for code and word data, and will do nothing if the data at the specified address is defined as data other than code or word data (ascii text, for example).


To MZT main page.



My Z80 Tools


MZT or My Z80 Tools is a complete set of cross-assembly tools targeted at the Z80 cpu in a Linux enviroment. It includes a macro assembler (mzmac), a linker/librarian (mld80) and a disassembler (mdz80).
The components of MZT are designed to provide a set of well-integrated tools capable to operate in the most recent versions of 64-bit Linux.

All the software si derived by a fork of existing projects. See in the sections relative to each tool for original authors credits.

MZT needs cmake to compile. Cmake is available on all major Linux distributions and you can always visit project site for additional informations.


Why MZT?
I could answer with a simple "why not?", but the point is that the tools available do not usually make a unique environment, or do not have all the features that I want, or simply they do not fit my tastes and way of working, and since i still spent much time on Z80 projects this is more then enough for me to write a new one. Wink


If you are of those in a hurry, you can jump directly to the downloads, or you can read more in each tool's section: mzmac, mld80, mdz80.



Install cmake, if you do not already have it on your system.

Extract MZT tar file in a working subdirectory and then do:

$ make; make install

By default MZT will be installed in /usr/local, if this is not what you want, in MZT top directory you can edit the Makefile and change the value of the variable INSTALL_PREFIX to your desired installation path.

Depending on where you decide to install MZT executables, you may need root permissions to do "make install".


Mzmac is a macro cross-assembler for the Z80 and is based onthe well-known zmac of which it inherits all the features. In particular mzmac is based onthe highly advanced version of zmac developed by George Phillips that includes, among other features, cycle counting and various output formats including the most important ".rel".

Mzmac differs from its predecessor in management of output formats, the format of listing and in management of options from command line.

It also has a better support for 64-bit environments, reformatted source code and some minor improvements/bug fixing.
At present mzmac is able to successfully compile almost every kind of source including CP/M 3.
An integral partof mzmac is its support for macro directives in style of m80, rmac or SLR.
In the current version however, are not yet supported the following directives: IRP, IRPC, REPT and EXITM.
The implementation of these directives has the highest priority among the objectives of the next release.

Mzmac exactly as zmac has a long list of credits: Bruce Norskog wrote the original version in 1978 and then it was updated by John Providenza, Colin Kelley, Russell Marks, Mark Rison, Chris Smith, Matthew Phillips and Tim Mann.

Finally George Phillips has written the versions on which mzmac is based.


 It has the following options:

        --version show version number
        -8      use 8080 interpretation of mnemonics
        -b      no binary (.hex,.cmd,.cas, etc.) output
        -c      add cycle counts in listing
        -d      make the listing discontinuous.
        -e      error list only
        -f      print if skipped lines
        -g      do not list extra code
        -h      show this information about options and quit
        -i      do not list include files
        -I dir  add 'dir' to include file search path
        -j      promote relative jumps to absolute as needed
        -J      warn when a jump could be relative
        -l      no list
        -L      force listing of everything
        -m      print macro expansions
        -n      put line numbers off
        -p      put out four \n's for eject
        -s      don't produce a symbol list
        -t      output error count instead of list of errors
        -x      generate listing to `listfile' (`-' for stdout)
        -z      use Z-80 interpretation of mnemonics

        --bin   output .bin file (default)

        --ams   output .ams file
        --bds   output .bds file
        --cas   output .cas file
        --cmd   output .cmd file
        --hex   output .hex file
        --lcas  output .lcas file
        --rel   output .rel file

        --dep   list files included

        --mras  limited MRAS/EDAS compatibility
        --help  show detailed help
        --html  output detailed help in HTML format

Using --help or --html, produce a much more informative help page, respectively in (coloured) text or in HTML format that you can also read here.

Apart its ability to produce .rel format output, mzmac can perform accurate timing evaluation (cycle counting) on assembled code. George Phillips has written an interesting tutorial on this at his site.


Known bugs:

Equates of this kind:

TRUE        EQU    0FFFFH
FALSE       EQU    0

are not always evaluated correctly.

Replace with this to be sure:

TRUE        EQU    -1
FALSE       EQU    0


Mld80 is the linker/librarian tool of MZT toolset.

While it is a replacement for the Micrososft L80 linker for CP/M and incorporates almost all of its capabilities, mld80 has also extra features.

Mld80 accepts object (.rel) files produced by mzmac or by original m80, rmac or compatible assemblers as well as library (.rel) files made by LIB80.

In this initial release it does not support any of expected librarian function (create or add/change/remove members). It is also not capable to produces .SPR, .PRL or .OVL files.

All this features will be in the next release.

Mld80 is a derivative of ld80 by Gábor Kiss.



mld80 -o outfile [options] objectfile ...


Addresses must be in the range 0-FFFF and expressed in hexadecimal notation. Addresses prefixed by a percent (%), as %NNNN, are not absolute and instructs mld80 to place following segments on a NNNN-boundary.

E.g. -D%10 causes the following data segments to be located on a 16 byte boundary.


specify the address where code segment of the following object files will be placed. If an object file specification is not preceded by -P option, code segment will be appended to the previous one. If no -P option specified before the first object file, its code segment will be placed to 0000.


specify where data segment of the following object files will be placed. If an object file specification is not preceded by -D option, data segment will be appended to the previous one. If no -D option specified before the first object file, its data segment will be appended after the last code segment.


address is the address where common block name will be placed. If block name name is empty string or it begins with space it will be treated as blank common block. However no extra whitespace character around of comma (,) are allowed. Name is not case sensitive. Common blocks of unspecified location will be appended to the last data segment.


Common block named name will be "uncommon". Normally common blocks of the same name are located on the same address. However blocks marked as uncommon will be concatenated like code or data segments. In this way you can spread your code/data over several region of physical memory. Name is case insensitive.


The following object file is a library. Mld80 will scan the library loading only modules that satisfies unresolved external references. If a new reference is encoutered to an already extracted module than the newer module will not loaded again. It is possible to specify the same library more than once in order to resolve backward references.


Suppress data segments. The output file will contain just code and absolute segments only.


Output format specification. Possible values of oformat are:

    • ihex: Intel hex. This is the default format.
    • bin: Raw binary, gaps filled with X'00'.
    • binff: Raw binary, gaps filled with X'ff'.


Request for warning messages. Possible value of warns is (only):

extchain: Warn if ambigous external reference found.


Name of symbol file. `-' stands for the standard output.


Generate map. A list of segment mapping will be placed into symbol file if it has been specified. Otherwise the segment list will printed on standard output.


Symbol table size. Use this option if the linker have to process more than 1024 symbols.


Print version number and exit.


Mdz80 is a "smart" disassembler for the Z80 cpu. It has the abilty to trace input code to "guess" what kind of binary data it is, and so, to have a disassembly that correctly produce different output for data or code.

Disassembly is also controlled by a "control" file (autogenerated the first time) that lets you assign a "type" to variuous regions of code to produce highly readable code even from the first attempts of disassembly job.

Of the MZT toolset mdz80 is the most mature and heavily rewritten respect to original code.

Mdz80 is a fork of the D52 microcontroller disassembler from Jeffery L. Post.


The mdz80 disassembler allows the user to specify memory areas as code, binary data (8 or 16 bit), ascii text, address tables, or data that should be ignored (not disassembled). You may specify such areas in an ascii text file with the same name as the file to be disassembled, but with an extension of ctl (for example: program.ctl controls the disassembly of program.hex or program.bin).

With the use of a control file, you can, by an iterative process, get a progressively more meaningful disassembly by modifying the control file based on examination of the output of previous runs of the disassembler. You can also specify names for labels and symbols in the control file, resulting in a much more readable source file.

Labels and symbols may have as many characters as will fit in one line of the control file, but some cross assemblers (and especially cross linkers) will truncate any more than some fixed number of characters. Consult your cross development tool documentation. Labels and symbols are not case sensitive.

The control file may exist in either the current directory or the directory of the file to be disassembled. If it is in both directories, the control file in the current directory will take precedence. The output file will always be placed in the current directory.

The getting started section of this document gives some minimal information for users who are eager to try out the disassembler. For best use of the disassembler, however, you should read the sections on command line options and control file directives before proceeding.




Usage: mdz80 [options] <filename> [-o <ofile>] [-c <cfile>] [-e <efile>]

File control options:
        -o use 'ofile' as output.
        -c use 'cfile' as control.
        -C disassemble CP/M .com file (implies -x100).
        -b force .bin format/extension on input file.
        -h force .hex format/extension on input file.
           If neither 'b', 'c', nor 'h' is specified, mdz80 will first search
           for a .hex file, then a .bin file and then the filename alone.
        -t trace and analyze code before disassembly.
        -e use asm source 'efile' to add/evaluate symbols
           (Altoghether with -t option will create/modify control file).

Disassembling options:
        -a use ascii macro instead of db/defb for text.
        -d include address and data in comment field.
        -n use C style for hexadecimal operands
        -f format hex values as '$xx' instead of xxH.
        -p put dot '.' at beginning of pseudo ops
        -s change 'defb' and 'defw' to 'db' and 'dw'.
        -l output labels, symbols, and mnemonics in lower case.
        -x [nnnn] add hexadecimal offset to program addresses.
        -8 generate 8080 mnemonics.
        -T during trace (-t) presume unidentified binary is code

General options:
        -v be verbose.
        -V print version and exit.

Options are case sensitive and may be entered in a freeform
fashion as long as a dash (-) preceeds any option (group).
        mdz80 -bd filename
        mdz80 -d filename -b -o out_file

Getting Started

mdz80 is a command line disassembler. To disassemble a file called 'program.bin' or 'program.hex', type in (at the command line prompt):

mdz80 -d program

A control file with the same name as the file to be disassembled, but with an extension of '.ctl' can be created in which you can specify areas of memory to be ascii text, 8-bit binary data, 16-bit binary data, etc. A control file is not necessary for disassembly, but as will be seen, it will provide for a much more complete disassembly. The -t option will cause mdz80 to trace and analyze the code before disassembly, and will create a preliminary control file.

If a file extension is not provided and neither the -b option nor the -h option is specified on the command line, the disassembler will first search for a hex file and, if it is not found, will then search for a bin file. The disassembler can be forced to look for only one or the other by specifying either the binary or the hex option on the command line:

mdz80 -db program for the file program.bin, or
mdz80 -dh program for the file program.hex

If a file extension of '.hex' or '.bin' is provided in the filename (ie: mdz80 test.hex), that is equivalent to using the -h option or -b option.

mdz80 will produce a file named 'program.z80' containing the disassembly of the original hex or binary file. It is very useful on the first few disassemblies to use the -d option , which adds a comment field to every line. The comment field contains the hexadecimal address of each instruction, the hex data for each byte in the instruction, and the ascii code for each byte of the instruction.

(opcode/operands) (address) (data) (ascii)
JR X0131 ; 0100 18 2F ./

This makes it easy to spot areas of ascii text or other nonexecutable code, and you can then modify the control file to tell the disassembler that these areas should be treated as some other kind of data on subsequent disassemblies.

By examining the output file and then modifying the control file based on the information obtained, you can get a progressively better disassembly of your program. Generally, about half a dozen iterations of the disassembly are enough to produce a very readable source file that can then be modified and reassembled.

Command Line Options

Options are case sensitive and may be entered in a freeform fashion as long as a dash (-) preceeds any option or group of options:

mdz80 -bd filename


mdz80 -d filename -b -o out_file

Command line options are: 

-a (ascii macro)

The A option tells the disassembler to change the db/defb pseudo op to the ascii macro (pseudo op 'ascii' instead of 'db' or 'defb') for areas defined as ascii text. This will cause the disassembler to include a macro definition in the disassembly output file that should expand to 'db' for the cross assembler. To use this option, your cross assembler must be able to handle macros, and in particular, mzmac's macro format. If used in conjunction with the -s option , the -s option must come first on the command line.

There are some caveats regarding the ascii macro. The intent of this macro is to make ascii text easier to find in the output file. Once you're satisfied with the readability of the output file, you may want to run the disassembler once more without the -a option to produce a file compatible with your cross assembler.

-8 (disassemble 8080 code)

Disassembles code using 8080 opcodes instead of Z80 opcodes (that is the default).

-b (read binary file)

The -b option forces the disassembler to read a binary file (extention '.bin') instead of an Intel hex file (extention .hex). If neither the -b option nor the -h option are specified, the disassembler will first search for a file with an extention of .hex, and if not found, will then search for a binary file with an extention of .bin. 

-c (control filename)

This option lets you specify a filename for the .ctl file to be used, instead of the default that is input filename postfixed with ".ctl".

-C (disassemble CP/M .com file)

Disassembles a CP/M .com executable file beginning at address 100H. Use of this option will cause mdz80 to look for a file with a .com extension, as opposed to a .bin or .hex extension. This option also sets the offset to 100H without the need to do so with the -x option.

-d (put data in comment field)

The -d option tells the disassembler to include a comment field in each disassembled line. The comment field will contain the hexadecimal address of each instruction, the hex data for each byte in the instruction, and the ascii code for each byte of the instruction.

(opcode/operands) (address) (data) (ascii)
JR X0131 ; 0100 18 2F ./

This makes it easy to spot areas of ascii text or other nonexecutable code, and then modify the control file to tell the disassembler that these areas should be treated as some other kind of data on subsequent disassemblies. 

-e (evaluate symbols from asm source)

Use this option to feed mdz80 with an assembler source file the contains valid labels, equates and entry points in the context of what you are disassembling. In this way mdz80 will extract a list of symbols to be used in the current disassembly substituting symbols in list with the autogenerated ones.
Normally this is to be used in the very first runs of mdz80 in conjunction with -t option to trace code and generate an initial .ctl file that already contains valid labels. 

-f (use '$' prefixed operands)

Use this option to have the disassembler output hexadecimal operands using '$' prefix notation, ie: $cd instead of the default 0cdh.

-h (read hexadecimal file)

The -h option forces the disassembler to read an Intel hex file (extention '.hex') instead of a binary file (extention '.bin'). If neither the -b option nor the -h option are specified, the disassembler will first search for a file with an extention of '.hex', and if not found, will then search for a binary file with an extention of '.bin'. 

-l (lowercase case output)

Causes labels, symbols, mnemonics, etc to be output in lower case, for cross assemblers that may require lower case code. Comments, literal data values, strings, and other output are not affected by this option.

-n (use C type operands)

Use this option to have the disassembler output hexadecimal operands using C notation, ie: 0xcd instead of the default 0cdh. 

-o (output filename)

This option lets you specify the output filename, instead of the default that is input filename postfixed with ".z80".

-p (add dot in front of pseudo ops)

Some assemblers require pseudo operations to begin with a dot. Use this option to have the disassembler output ".equ" instead of "equ" and similar for org and other pseudo operations. 

-s (set string pseudo op)

This option tells the disassembler to generate the pseudo ops 'db' and 'dw' instead of 'defb' and 'defw' for those cross assemblers that may require this.Use this option for cross assemblers that do not recognize 'defb' and 'defw'. If used in conjunction with the -a option , the -s option must preceed the -a option on the command line.

-t (trace and analyze code)

This option tells mdz80 to trace and analyze the Z80 code before disassembling it. mdz80 will attempt to determine which parts of the code are actually code, and which parts are ascii text, binary data, pointers, etc. While it does a reasonably good job of analyzing the code on most files, no program can do as good a job as a human programmer. A control file with the same name as the hex or bin file, but with an extension of '.ctl', will be created before the actual disassembly is performed. You can then edit the control file to correct any erroneous directives, or to add new directives.

Warning: Using this option will overwrite any control file that may already exist for the code file being disassembled. It is intended to be used on the first disassembly only or until you combine disassembly options in the right way to have a good starting point.

-T (presume code during trace)

This option is meaningful only in conjunction with -t option during initial traces of the code. If you are disassembling very fragmented code or code that cannot clearly traced from the beginning (i.e. a library) this can easily confuse mdz80 produncing wrong output. -T option will instruct mdz80 to presume the all binary input that can't be exactly identified as code or data must be considered as code and in this way disassembled.

-v (be verbose)

Will inform you of the phases of disassembling.

-V (print version)

Will print mdz80 version and then exit.

-x (add hexadecimal offset to addresses)

This option causes the disassembler to add a hexadecimal offset to the address of every location in the code file. This may be useful for disassembling code that was read from the second (or subsequent) rom of a set of roms. For example, suppose that binary data was obtained from a set of 4K byte eproms (call them rom1 and rom2). Then the first file should be disassembled with the command line:

mdz80 -d rom1

and the second should be disassembled with the -x option:

mdz80 -d rom2 -x1000

The two files then can be combined into one and all references within the disassembled code will be to the correct addresses.

If given on the command line, the -x option overrides any -o directive in the control file.


The control file

The control file gives you the way to assign the proper type to regions of code in disassembly that are not (eventually) correctly identified by mdz80 during trace.

See here how to use the control file.

  •  Implements missing macro directives in mzmac (IRP, IRPC, REPT, EXITM)
  • Add librarian functions to mld80
  • Add the ability to link .PRL, .SPR files to mld80
  • Add an option to mdz80 to recognize embedded print routines and disassemble embedded ascii text in the right way.


Suggestions or, even better, code enhancements are more then welcome... Smile


Latest version (source format):



Older versions:

- none -





zmac is a Z-80 macro cross-assembler. It has all the features you'd expect. It assembles the specified input file (with a '.z' extension if there is no pre-existing extension and the file as given doesn't exist) and produces program output in many different formats. It also produces a nicely-formatted listing of the machine code and cycle counts alongside the source in a ".lst" file.

Undocumented Z-80 instructions are supported as well as 8080 code.

zmac strives to be a powerful assembler with expressions familiar to C programmers while providing good backward compatibility with original assemblers such as Edtasm, MRAS and Macro-80.


zmac [ --help ] [ --version ] [ --dep ] [ --mras ] [ --rel ] [ --doc ] [ --html ] [ -8bcefghijJlLmnopstz ] [ filename[.z] ]



Print this documentation in text format to standard output.
Print zmac version name.
MRAS compatibility mode. Any ? in a label will be expanded to the current module identifier as set by *mod.
Output ".rel" (relocatable object file) format only.
zmac compatibility mode. defl labels are undefined after each pass. Quotes and double quotes are stripped from macro arguments before expansion. $ is ignored in identifiers allowing foo$bar to construct identifiers in macro expansions. Use ` (backquote) instead in normal mode. Labels starting with "." are temporary and are reset whenever a non-temporary label is defined (thus they may be reused). Labels starting with "_" are local to their file thus avoid multiple definition when brought in with include.
Print all files read by include and incbin.
Print this documentation in HTML format to standard output.
Accept 8080 mnemonics preferentially. Equivalent to .8080 pseudo-op.
Don't generate any machine code output at all.
Make the listing continuous, i.e., don't generate any page breaks or page headers. Can make things less confusing if you're going to consult the listing online rather than printing it. This is the default.
Omit the "error report" section in the listing.
List instructions not assembled due to "if" expressions being false. (Normally these are not shown in the listing.)
List only the first line of equivalent hex for a source line.
Display a list of options and a terse description of what the options do. (same as --help)
Don't list files included with include or read.
-I dir 
Add dir to the end of the include file search path.
Promote relative jumps and DJNZ to absolute equivalents as needed.
Error if an absolute jump could be replaced with a relative jump.
Don't generate a listing at all.
Generate listing no matter what. Overrides any conflicting options.
List macro expansions.
Omit line numbers from listing.
List to standard output.
Use a few linefeeds for page break in listing rather than ^L.
Output listing for a printer with headers, multiple symbols per column, etc.
Omit the symbol table from the listing.
Only output number of errors instead list of each one.
Accept Z-80 mnemonics preferentially. Equivalent to .z80 pseudo-op.

Input Format

zmac uses the standard Zilog mnemonics, and the pseudo-ops are also largely as you'd expect.

A "." may optionally preceeed any psuedo-op. For example, ".org" and "org" are treated as equivalent.

Input can be upper or lowercase.

Comments start with ;and carry on to the end of the line.

Number constants can take a trailing h or a leading $ or 0x for hex, a trailing b for binary, a trailing o or q for octal, or a trailing d for decimal.

'LH' (any length 2 string) can be treated as a number whose value is 'H' * 256 + 'L'.

Labels are declared with label: or just label - indentation is unimportant. Labels can be up to 40 chars long. They can start with and contain letters, digits, $, ., ?, @ and _. Ambiguous identifiers like $FCB will be treated as hex constants unless defined as a label. Labels declared with two colons (label::) make the label public.

Here is how other things work. Numbers are used as examples, but a full expression can be used in their place.



defb 42

A byte. ascii, byte, db, defm and text are synonyms.

defb 'foobar'

An ASCII character string (not NUL-terminated). Double quotes can also be used.

defb 'Who needs anything more than CP/M?',13,10,'$'

Strings and bytes can mix together.

defw 2112

defw $123,0x456

A word (16 bits). word and dw are synonyms.

defd $12345678

A double word (32 bits). dword is a synonym.

defs 500

Skip output ahead 500 bytes. This will insert 500 zeros in the ".ams" and ".cim" output files or if inside a ".phase" section. block, ds and rmem are synonyms.

dc 'string'

Like ascii but accepts only a single string and the high bit of the last character will be set.

dc count,value

Repeat the byte value a total of count times. Similar to defs except that memory is always filled with value.

incbin file

Inserts the raw contents of the file into the assembly. Simpler for large amounts of data.



label equ 100

Define a symbol to have a fixed value. The symbol can be used before it is defined. A symbol defined with equ or as a label can be defined only once, except that a symbol defined with equ may be redefined to the same value.

label defl 200

Define a symbol to have a changeable value. The symbol cannot be used before it is defined, and it can be redefined to a different value later with another defl. aset is a synonym.



Same as defl except that the symbol is defined as the smaller or bigger of two comma-separated expressions.


Increment the internal module name string. The first time this results in "a". Then "b", "c", ... "z". Then "aa", "ab", "ac", etc. all the way up to "zzzz". The module name string is used in --mras mode where "?" in label names is replaced with the current module name.

extern lab1,lab2,...

The listed labels are defined in an external module for later linking. No effect unless zmac is producing ".rel" output. ext and extrn are synonyms.

public lab1,lab2,...

The given labels will be visible to external modules when linking. No effect unless zmac is producing ".rel" output. global and entry are synonyms.


Location Control

org 9000h

Set the address to assemble to 0x9000.

phase address

Continue to produce code and data for loading at the current address but assemble instructions and define labels as if they originated at the given address. Useful when producing code that will be copied to a different location before being executed (e.g., an overlay).


End phase mode assembly.

aseg cseg dseg

Switch to the absolute, code and data segments respectively. No effect unless zmac is producing ".rel" output.


Input Control


Ends the input. Any lines after an end are silently ignored. If an arg is given, it declares the entry address for the program. This has no effect in ".cim" output. In ".hex" output it generates an S-record directing 0 bytes of data to be loaded at the given address.

if ... [ else ... ] endif

For conditional assembly. If you do if foo and foo evaluates to zero, all the lines up until the next corresponding else or endif are completely ignored. Conversely, if foo evaluates to non-zero, any lines from a corresponding else to the endif are ignored. Ifs can be nested. cond/endc are synonyms for if/endif.

include file

Include a file. Like C's (well, cpp's) #include and follows the same include path search rules, but the filename arg lacks the angle brackets or quotes (though single or double quotes may be used). read is a synonym.

comment X

Suspend assembly until the next occurence of character X on a line. The rest of the line will be ignored. A multi-line comment.

assert expr

Stop assembly if expr is non-zero.


Cycle Counting

sett expr

Set the current T-state count to expr. tstate is a synonym.

setocf expr

Set the current opcode fetch count to expr.


Code Generation


Make cycle counting operators return 8080 cycle counts and interpret any ambiguous assembly statements as Intel 8080 mnemonics. CP will be interpreted as "call on positive" and JP as "jump on positive".


Make cycle counting operators return Z-80 cycle counts and interpret any ambiguous assembly statements as Zilog Z-80 mnemonics. CP will be interpreted as "compare accumulator" and JP as "jump unconditionally".

jperror enable

If enable is non-zero, turn on errors when JR instructions could be used instead of JP, off otherwise. Used to check existing code for situations where shorter code could be generated. Same as -J option. No effect if in 8080 mode.

jrpromote enable

If enable is non-zero, JR and DJNZ instructions will be promoted to equivalent JP and DEC B, JP NZ instructions if the relative branch offset is out of range. If enable is zero, promotion is disabled. Same as the -j option. No effect if in 8080 mode.


Undocumented Instructions

Most Z-80 chips support a number of undocumented instructions that were part of the original design but not made an offical part of the Zilog specification. These instructions may not be supported by all Z-80 chips, especially licensed variants, but are fairly widely available nonetheless.

sl1 r

Same as sla r but shifts a 1 into the lower bit of r rather than a 0.

in (c)

Inputs a byte from port c but does not store the value. Flags are still set as with the normal in r,(c) instruction.

out (c),0

Outputs a zero to port c.

bit/set/res n,(ix+d),r

rlc/rrc/rl/rr/sla/sl1/sra/srl (iy+d),r

Same as the corresponding operation on just (ix+d) or (iy+d) but with the result being stored both into (ix+d) and register r. Except for bit which has no effect on r. zmac supports the syntax to allow those instruction patterns to be generated.

The upper and lower bytes of the ix and iy can be used in a number of instructions much in the same way as d and e correspond to the upper and lower bytes of de. zmac names these ixh, ixl, iyh and iyl and are referred to generically as ixylhhere.

inc/dec/add/adc/sub/sbc/and/xor/or/cp ixylh

Arithmetic or logical operation on ix or iy high or low byte.

ld a/b/c/d/e,ixylh

Load register with ix or iy high or low byte.

ld ixylh,a/b/c/d/e

Load ix or iy high or low byte with register.



Output $DD and $FD prefix bytes. The Z-80 allows multiple prefix bytes for IX and IY instructions. This allows you to specify them abstractly. There is little purpose except for delaying an interrupt or confusing disassemblers.



name str

Set the name of the output model to str. For compatibility reasons str may be parenthesized (e.g., "name ('foo')"). Not all output formats support an internal name and many have severe length limits.

rsym and wsym

Read/write a symbol file. These simply load/save the currently defined symbols from/to the file specified (in a non-portable format). rsym takes place at the point it is encountered in the file (on the first pass); wsym is delayed until assembly has finished.


Listing Pseudo-ops

There are several pseudo-ops for controlling the listing. None of these ops appear in the listing themselves:


Start a new listing page.


Do nothing. This can be used to have a comment in the source but not the listing, I suppose.

elist, flist, glist, mlist

These have the same effect as the similarly-named command-line options, though possibly with the sense reversed depending on the default. Use an arg >0 (or no arg) to enable, and an arg <0 to disable.


Sets whether to list or not. You can use this to avoid listing certain parts of the source. Takes same optional arg as 'elist', etc..


Set title (used in listing and symbol file).


Output arg blank lines in the listing, or one line if no arg is given.




Expressions feature a full set of C operators with the same precedence rules and some common assembler extensions and names. Here is the complete list of operators, highest-precedence first. Operators separated only by a space are synonyms; for example, ~ is the same as not.

! (logical), ~ not (bitwise), + (unary), - (unary), low, high, t, tilo, tihi, ocf

*, /, % mod

+, -

<< shl, >> shr

< lt, > gt, <= le, >= ge

== = eq, != <> ne

& and(bitwise)

^ xor(bitwise)

| or(bitwise)



? :(ternary choice operator)

You can use normal parentheses or square brackets to override the precedence rules. Square brackets can be used where parentheses would conflict with Z-80 mnemonic syntax, but this is not necessary in any practical case.

The ? may need spaces around it to distinguish it from a label that has ?in it.

The unary operators not familiar to C programmers:

low expr 
Returns low 8 bits of expr
high expr 
Returns high 8 bits of expr
t expr 
Current count of T-states up to memory location expr
tilo expr 
Low count of T-states used by instruction at memory location expr
tihi expr 
High count of T-states used by instruction at memory location expr
ocf expr 
Current count of opcode fetches up to memory location expr


The following defines a macro named m with zero or more formal parameters p1, p2, ..., pn, zero or more local symbols ?s1, ?s2, ..., ?sm, and body b1, b2, ...:

m macro p1, p2, ..., pn, ?s1, ?s2, ..., ?sm 

The macro is called by writing:

m v1, v2, ..., vn

A macro call expands to the text of the macro's body, with each occurrence of a formal parameter pk replaced by the corresponding value vk, and with each local symbol ?sk replaced by a new, unique symbol invented for this call. Invented symbols begin with ?, so you should avoid using such symbols elsewhere in your program.

zmac currently does not check that you have provided the right number of parameters when calling a macro. If you provide too few, unmatched formals are replaced with the empty string. If you provide too many, the additional values begin to replace local symbols as if they were ordinary parameters. (This could be considered a feature.) After the local symbols are all replaced, additional parameters are silently ignored.

For compatibility with Macro-80, the first line of a macro definition can list other labels that will be treated locally:


   local lab1,lab2,...

Each time the macro is expanded the local labels are replaced with unique names thus avoiding multiple definition problems.

For compatability with MRAS, macro arguments may be preceeded by  in their definition and use.

Any `(backquote) in a macro is ignored thus allowing a macro to construct identifiers. For example:

move macro dir 

Invoking move i will construct a ldir block move instruction.


zmac is broadly compatible with many original Z-80 and 8080 assemblers because it accepts many different names for common operations and has liberal identifier and numeric formats. It also accepts most simple usage of macros.

When assembling old code keep these portability problems in mind.

Expression order of evaluation may be different. zmac uses C semantics more order of evaluation but assemblers often used simple left to right ordering. zmac will evaluate 2+2*3 as 8 where other assemblers will yield 12.

zmac has no support operating on strings in macros. Assemblers like Macro-80 could perform conditional tests on strings.

Advanced macros are unlikely to work. zmac hasn't advanced to the state where all the possible ways of substituting parameters are supported. Nor does it have simple repetition, a way to exit a macro expansion early and so on.

Consult the original assembler manual. zmac error messages won't help you figure out what an unknown assembler command is supposed to do.

Compare against original output. The very safest thing to do when porting assembly code is to compare the binary output of zmac against that produced by the original assembler. This way you can ensure everything has been interpreted correctly. Only once that has been achieved should you modify the code.

Errors and Warnings

Any errors or warnings encountered during assembly are reported to standard error and in the listing file. The errors output immediately give the source file and line number containing the error. In listings the error letter and message appear just after the line containing the error.

Balance error
A string is missing an closing quote or an if is missing an endif
Expression error
An expression did not parse or attempts a divide or modulus by 0.
Syntax error
General problem with the syntax on a line. Sometimes extra explanation will be printed on standard output.
Digit error
A numeric constant has too many digits to be represented as a 32 bit number.
Mult. def. error
A symbol has been defined more than once and those values differ.
Phase error
On the second or subsequent assembly passes the assembly has changed significantly. Most commonly it means an if has changed conditions but can also happen when labels or equated values do not converge to a fixed value.
Undeclared error
An undeclared symbol was used in an expression or public statement.
Value error
An invalid value was given to a statement. Often this means using less than -128 or greater then 255 in a defb or less than -32768 or greater than 65535 in a defw. Or similar invalid values used Z-80/8080 opcodes requiring an 8 or 16 bit value (and other restrictions like 0 to 7 for BIT). Also if a relative jump is out of range or if a negative value is given in defs or dc.
Phase/Dephase error
phase was used within another phase or dephase without phase. Or if org is used within phase.
Assertion failure error
An assert statement evaluated to zero.
Use JR error
An absolute jump instruction was used where relative jump was in range of the destination address. Only generated if -j or jrpromote is in effect.
Not relocatable error
An expression was used that must be generated at link time but cannot be handled by the ".rel" format. For instance, an org to a symbol in the data segment when in the code segment. Or a relative jump to a different segment. The ".rel" format can evaluate expressions at link time using the high, low, not, -, +, *, / and % operators. zmac is clever enough to use high or low in place of & $ff00 and & 255. But it won't replace a shl with a multiply.
Register usage error
A invalid register was given to an instruction. For example, LD B,(DE) or ADD HL,IX.
Z-80 instruction in 8080 mode error
An instruction was assembled that is only valid on the Z-80 but .8080 (or -8) mode is in effect. However, use use of Z-80 mnemonics that output valid 8080 instructions is always OK.
$hex constant interpreted as symbol warning
A symbol such as $FCB has been defined even though it could appear to be a hexadecimal constant. zmac will treat $FCB as symbol for the entire assembly which could be rather surprising if that were not the intent.
Not implemented warning
For statements that have been added as parse rules but have no effect. The only current example is subttl which sets the sub title of a listing in certain assemblers.


Output Formats

Except for ".rel", zmac writes every known output when assembling. This is no burden on modern computers and saves having to come up with options to select a particular output format.

".rel" is a special case since that format is intended for linking and can have undefined external symbols which would be errors in the other formats which do not support them.

AMSDOS executable format for Amstrad computers.
Experimental format with the potential for source-level debugging.
TRS-80 high-speed (1500 baud) cassette SYSTEM file. The internal name of the file is the source file name shortened to 6 characters with suffixes removed.
Core In-Memory image. A raw binary format with the first byte corresponding to the lowest generated code or data and proceeding contiguously until the highest address generated. Any gaps are filled with zeros. Typically used for CP/M where all executables start at address 256 or for ROM images.
TRS-80 DOS executable file format as used by all major DOSes on the TRS-80 (TRS-DOS, LDOS, MULTIDOS, NEWDOS, etc.)
Intel hex record format.
TRS-80 low-speed (500 baud) cassette SYSTEM file. The internal name of the file is the source file name shortened to 6 characters with suffixes removed.
Relocatable object module format as produced by MACRO-80 and other assemblers.



In the symbol table listing, the = prefix is given for those symbols defined by equ or defl.

Exit Status

No errors.
One or more errors were found during assembly, or zmac exited with a fatal error.



Bruce Norskog original wrote zmac in 1978.

Updates and bugfixes over the years by John Providenza, Colin Kelley, and more recently by Russell Marks, Mark RISON, Chris Smith, Matthew Phillips and Tim Mann.

Extensive modifications for cycle counting, multiple output formats, ".rel" output, 8080 mode and older assembler compatibilty were written by George Phillips.

This document was based on Russell Marks zmac man page which had tweaks by Mark RISON and Tim Mann. George Phillips converted it to HTML and documented the new features and some older ones (e.g., phase/dephase).