zmac - Z-80 Macro Cross Assembler
zmac has been around for a long time. My version runs under Windows (10, 8, XP or Vista and probably older versions). It also includes source code and builds easily for Mac and Linux. It has quite a number of improvements:
- Cycle-counting with sett, setocf pseudo-ops and t, tilo, tihi and ocf operators.
- Flexible label format and new pseudo-ops making it compatible with MRAS and MACRO-80.
- Outputs .hex, .cmd, .cas, .wav, .rel, .cim and .ams files.
- $ and 0x prefixes for hex constants
- 8080 mnemonics supported
- Undocumented Z-80 instructions
- JR promotion
- Full C expressions and parenthesis allowed.
- Include data files directly (incbin)
- 32 bit constants (defd)
- -I to specify include directories
- rept, irp and irpc in-line macros.
- Ever improving compatibility with MAC, MACRO-80 and MRAS.
You may also want the ld80 linker if you're creating ".rel" files or need to link with existing ".rel" files.
The best description of the T-state counting macros can be found at the end of my cycle counting tutorial. Might also explain why you would want such a feature. setocf and ocf() are newer features not covered there. They're needed when counting clock cycles on the TRS-80 Model 4 when it runs in high speed (4 MHz) mode. While the Z-80 mostly runs twice as fast in that the opcode fetch portion of each instruction is slowed down by 2 wait states. Code on the Model 1 or Model 3 (or Model 4 running in Model 3 mode) can be timed like this:
code: ld a,(de) ld b,(hl) add a,b time equ t($)-t(code)The first two instructions take 7 T-states, the 3rd 4 T-states so zmac will set time equal to 18. But if you need cycle-accurate counts on a Model 4 you'll have to do this:
code: ld a,(de) ld b,(hl) add a,b time equ t($)-t(code)+2*(ocf($)-ocf(code))As before we get a base time of 18 T-states but in this case each instruction also takes an opcode fetch thus the equation will add an additional 6 T-states and zmac will set time to 24.
While most instructions require only one opcode fetch many others take more. All instructions require at least one opcode fetch so ocf() will be at least a count of instructions.
Just drop me a line at the e-mail address at the bottom of this page if there's any features you'd like to see in zmac, have bugs to report or even just to say "hi".
zmac has all its documentation built-in (zmac -h and zmac --doc). Here's the output of --doc for convenient reference.
Overview of zmac
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.To reduce clutter and command line option usage, by default all zmac output is put into an (auto-created) zout subdirectory. For file.z the listing will be in zout/file.lst, the TRS-80 executable format in zout/file.cmd and so on. For more friendly usage in make files and integrated development environments the -o, --oo, --xo and --xd options may be used to select sepcific output file formats and where they are written.
Undocumented Z-80 instructions are supported as well as 8080 and Z-180 (aka HD64180).
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.
Usage
zmac [ --help ] [ --version ] [ --dep ] [ --mras ] [ --od dir ] [ --oo sfx1,sfx2 ] [ --xo sfx1,sfx2 ] [ --dri ] [ --rel ] [ --rel7 ] [ --nmnv ] [ --z180 ] [ --fcal ] [ --doc ] [ --zmac ] [ -8bcefghijJlLmnopstz ] [ filename[.z] ]Space-separated arguments in the ZMAC_ARGS environment variable are added to the end of the command line.
Options
--help | Display a list of options and a terse description of what the options do. |
--version | Print zmac version name. |
--mras | MRAS compatibility mode. Any ? in a label will be expanded to the current module identifier as set by *mod. Operator precedence and results are changed. |
--od dir | Place output files in dir instead of the default "zout" subdirectory. Creates dir if necessary. |
--oo hex,cmd | Output only the the file types by suffix. Multiple --oo arguments may be used. "--oo lst,cas" is equivalent to "--oo lst --oo cas". See "Output Formats" for a list of output types by suffix. |
--xo tap,wav | Do not output the file type types listed by suffix. |
--dri | Enable compatibility with Digital Research (CP/M) assemblers: Ignores dollar signs in constants and symbols. Silences a warning when using Z80.LIB. Allows the use of '*' in first column for comment lines. Accepts $-MACRO directives. |
--nmnv | Do not interpret Z-80 or 8080 mnemonics as values in expressions. |
--rel | Output ".rel" (relocatable object file) format only. Exported symbols are truncated to length 6. |
--rel7 | Output ".rel" (relocatable object file) format only. Exported symbols are truncated to length 7. |
--zmac | 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. |
--z180 | Use Z-180 timings and extended instructions. Undocumented Z-80 instructions will generate errors as the Z-180 (or H64810) does not support them. Equivalent to .z180 pseudo-op. |
--dep | Print all files read by include, incbin and import. |
--doc | Print this documentation in HTML format to standard output. |
-Pk=number | Set @@k to the given numeric value before assembly. Up to 10 parameters can be set from 0 though 9. -Pk is shorthand for -Pk=-1. For example, P4=$123 effectively puts @@4 equ $123 at the top of the first file. |
-Dsymbol | Define symbol to be 1 before assembly. |
--fcal | Always treat an indentifier in the first column as a label. zmac uses various heuristics in the case of ambiguity when a label does not have a colon. This option turns heuristics off. |
-8 | Accept 8080 mnemonics preferentially and use 8080 instruction timings. Equivalent to .8080 pseudo-op. |
-b | Don't generate any machine code output at all. |
-c | Don't display cycle counts in the listing. |
-e | Omit the "error report" section in the listing. |
-f | List instructions not assembled due to "if" expressions being false. (Normally these are not shown in the listing.) |
-g | List only the first line of equivalent hex for a source line. |
-h | Display a list of options and a terse description of what the options do. (same as --help) |
-i | Don't list files included with include, read or import. |
-I dir | Add dir to the end of the include file search path. |
-j | Promote relative jumps and DJNZ to absolute equivalents as needed. |
-J | Error if an absolute jump could be replaced with a relative jump. |
-l | List to standard output. |
-L | Generate listing no matter what. Overrides any conflicting options. |
-m | List macro expansions. |
-n | Omit line numbers from listing. |
-o filename.cmd | Output only the named file. Multiple "-o" options can be used to name a set of different files. |
-p | Use a few linefeeds for page break in listing rather than ^L. |
-P | Output listing for a printer with headers, multiple symbols per column, etc. |
-s | Omit the symbol table from the listing. |
-t | Only output number of errors instead list of each one. |
-z | Accept Z-80 mnemonics preferentially and use Z-80 instruction timings. 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'.
For compatibility and to ease writing code that generates code, any mnemonic can be used as a data value. For example, mvi a,xra will load A register with $A8. And dw ldir will output the same data a ldir by itself (ldir evaluates to $B0ED). A full table of mnemonics and their values is in Mnemonic Values. The --nmnv command line option turns off this feature.
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.
Single quotes are ignored at the end of identifiers allowing non-binding notation indicating alternate register use during heavy applications of exx and ex.
Here is how other things work. Numbers are used as examples, but a full expression can be used in their place.
Data
defb 42
A byte. ascii, byte, db, defm, dm 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.
def3 $123456
A 3 byte word (24 bits). d3 is a synonym.
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. bytes is a synonym.
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.
Symbols
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.
varname 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, set and = are synonyms (despite set also being a Z-80 mnemonic).
varname OP = expression
Shorthand for varname defl varname OP expression. Allows for C-like handling of variable such as var += 5. OP can be +, -, *, /, %, &, |, ^, <<, >>, && or ||.
varname++
Shorthand for varname defl varname + 1
varname--
Shorthand for varname defl varname - 1
min
max
Same as defl except that the symbol is defined as the smaller or bigger of two comma-separated expressions.
name equ register
Define a symbol to be an alias of a register. count equ bc lets count stand for register bc so push count and dec count will both operate on register bc.
*mod
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.
label ++
Equivalent to label defl label + 1.
label --
Equivalent to label defl label - 1.
label += 10
label -= 10
Equivalent to label defl label + 10 and label defl label - 10 respectively. Also works for *=, /=, %=, |=, &=, ^=, <<= and >>=.
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).
dephase
End phase mode assembly.
aseg cseg dseg
Switch to the absolute, code and data segments respectively. No effect unless zmac is producing ".rel" output.
common /name/
Set the address to the start of the selected common block. The blank common block will be selected if name is empty or all blanks or omitted entirely. No effect unless zmac is producing ".rel" output.
Input Control
end
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. It is required for ".500.cas", ".1000.cas" and ".1500.cas" output.
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.
ifdef symbol
Like if, but tests if symbol has been defined. Declaring a symbol as external counts as it being defined.
ifndef symbol
Like if, but tests if symbol has not yet been defined.
ifeq expr1,expr2
ifne expr1,expr2
iflt expr1,expr2
ifgt expr1,expr2
Shorthand for if expr1 == expr2, !=, <, >. For MRAS and MAC80 compatibility.
import file
Like include but will only bring in the file once. File tracking is done using only the file name so, for example, an import file will stop both import ./file and import dir/file even if they actually refer to different files.
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. *include file and *get file work if started in the first column. In --mras mode ".asm" will be added if file has no suffix and file/ext will be changed to file.ext Original MRAS source uses TRS-80 file system names where / is the extension introducer.
maclib file
Like include but adds .lib to the file name so includes file.lib.
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
8080
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".
z80
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".
z180
Allow assembly of Z-180 instructions. Make cycle counting operators return Z-180 cycle counts and interpret any ambiguous assembly statements as Zilog Z-180 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. Also acceptable are xh, xl, yh, yl and hx, lx, hy, ly. They are referred to generically as ixylh here.
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.
pfix
pfiy
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.
Miscellaneous
pragma str ...
Like C's #pragma, a generic hook for special purpose operations. Only two are currently defined.
pragma bds rest-of-line to output rest-of-line to the .bds output file.
pragma mds rest-of-line to output rest-of-line to the .mds output file.
The .bds output format supports setting initial values for Z-80 registers and I/O ports so pragma gives you access to that.
The .mds output format is a MAME debug script thus additional initial debugging commands may be output. Of particular use on the TRS-80 Model II is pragma mds ib@$ff=1 which maps page 1 of RAM into $8000 .. $FFFF and thus allows programs to load into that area.
name str
Set the name of the output module 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:
eject
Start a new listing page.
nolist
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.
list arg
Turns output to listing file (.list) off if arg < 0 or on if arg > 0. If no arg supplied then listing is enabled. Use this to avoid listing certain parts of the source. In --mras mode arg must be either on or off and *list can be used if started in the first column.
title
Set title (used in listing and symbol file).
space arg
Output arg blank lines in the listing, or one line if no arg is given.
Expressions
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)
Expressions change significantly in --mras mode:
Evaluation is strictly left to right. Except for and, or, xor and =. This doesn't break compatibility as original MRAS source code only allows .and., .or. and .xor. but the precedence difference may surprise if code is added.
! is bitwise OR instead of logical not.
< is left shift (or right shift when shift amount is negative)
MRAS operators (.and. .eq. .ge. .gt. .high. .le. .low. .lt. .mod. .ne. .not. .or. .shl. .shr. .xor.) are recognized even if apparently in identifers. (e.g., a.or.b is seen as a .or. b).
Logical operators return -1 for true and 0 for false. Normally zmac, like C, uses 1 for true.
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 |
Macros
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 | |
b1 | |
b2 | |
... | |
endm |
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 | |
ld`dir`r | |
endm |
Invoking move i will construct a ldir block move instruction.
For compatibility, & can also be used as in MAC to concatenate macro parameters. This conflicts with zmac's bitwise and operator but you can use the and synonym in macros to avoid the conflict.
In --mras mode arguments will be expanded even if they are inside other identifiers. The move could be written:
move macro dir | |
lddirr | |
endm |
Macro definitions can contain macro definitions which will be defined when the outer macro is first exapnded. Macros can be redefined as well.
Macro expansion continues to the endm directive but can be stopped prematurely by the exitm directive. Typically the exitm is inside some conditional part of the macro.
Parameters passed to a macro can be empty and are tested with the nul operator:
if nul &par | |
... | |
endif |
Macro parameters can contain commas if grouped inside < and >. Or a comma can be escaped with ^ which can also escape spaces and other special characters. It is also be put in front of a macro parameter name inside the expansion to suppress the replacement by its value.
Expansion of parameters in a macro body is purely textual. This can lead to surprises in complex situations. The % character can be used to force a macro parameter to be replaced with the evaluation of it as an expression.
Inline Macros
zmac supports the commonly available rept, irp and irpc inline macros
rept repeats its block the given number of times. This will output 10 nop instructions:
rept 10 | |
nop | |
endm |
irpc runs through a string of letters assigning them to a variable and expanding the macro block each time. For example, this will load 7 into registers b, d and h:
irpc reg,bdh | |
ld ®,7 | |
endm |
irp runs through a list of parameters assiging each entry to a variable and expanding the macro block. Here we load bc, de and hl with 0:
irp rpair,<bc,de,hl> | |
ld &rpair,0 | |
endm |
Lists can be nested. Here's an example of and irp passing lists on down to another irp:
irp listlist,<<one,two,three>,<four,five,six>> | |
irp list,<listlist> | |
ascii '&list' | |
endm | |
endm |
Compatibility
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. However, in --mras mode expressions are evaluated strictly left-to-right for compatibility.
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.
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.
B | Balance error |
A string is missing an closing quote or an if is missing an endif | |
E | Expression error |
An expression did not parse or attempts a divide or modulus by 0. | |
F | Syntax error |
General problem with the syntax on a line. Sometimes extra explanation will be printed on standard output. | |
I | Digit error |
A numeric constant has too many digits to be represented as a 32 bit number. | |
M | Mult. def. error |
A symbol has been defined more than once and those values differ. | |
P | 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. | |
U | Undeclared error |
An undeclared symbol was used in an expression or public statement. | |
V | 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. | |
O | Phase/Dephase error |
phase was used within another phase or dephase without phase. Or if org is used within phase. | |
A | Assertion failure error |
An assert statement evaluated to zero. | |
J | 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. | |
R | 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. | |
G | Register usage error |
A invalid register was given to an instruction. For example, LD B,(DE) or ADD HL,IX. | |
Z | Invalid instruction. |
The instruction is not valid for the current architecture. For example, a Z-80 instruction in 8080 mode (.8080 or -8 mode is in effect). Or a Z-180 instruction in 8080 or Z-80 mode. Or an undocumented Z-80 instruction in Z-180 mode. However, use use of Z-80 mnemonics that output valid 8080 instructions is always OK. | |
H | $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. | |
N | 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. | |
W | Generic warning |
Higher-level warning; see text of warning for explanation. |
Output Formats
Except for ".rel", zmac writes every known output when assembling by default. This is no burden on modern computers and saves having to meticulously select the desired 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. Conversely, a simple "org $8000" will be an error for ".rel" output as it defaults to the code segment where absolute origin statements are forbidden.
If ".rel" is selected for output either by --relopt or with --oo rel or -o file.rel then all other output formats are suppressed (except the ".lst" source file listing).
.ams | AMSDOS executable format for Amstrad computers. |
.bds | For source-level debugging and automatic memory protecttion in trs80gp |
.1500.cas | 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. Requires an entry address. |
.250.cas | TRS-80 250 baud cassette Level I CLOAD file. If your program has an entry address and $41FE does not contain that entry address then the file will be loaded at $41FE with relocation code added to move it to the desired location. |
.500.cas | 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. Requires an entry address. |
.1000.cas | Identical to 500 baud but intended for double-speed LNW-80 which can can load cassette files at double speed for an effective 1000 baud rate. Requires an entry address. |
.cim | 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. |
.cmd | TRS-80 DOS executable file format as used by all major DOSes on the TRS-80 (TRS-DOS, LDOS, MULTIDOS, NEWDOS, etc.) |
.hex | Intel hex record format. |
.rel | Relocatable object module format as produced by MACRO-80 and other assemblers. |
.tap | ZX Spectrum cassette tape format. |
.1500.wav | Same as .1500.cas but in ready-to-play audio format. |
.250.wav | Same as .250.cas but in ready-to-play audio format. |
.500.wav | Same as .500.cas but in ready-to-play audio format. |
.1000.wav | Same as .1000.cas but in ready-to-play audio format. |
.mds | MAME debug script (e.g., mame trs80 -d -debugscript zout/prog.mds) |
Miscellaneous
In the symbol table listing, the = separator is given for those symbols defined by equ or defl. The / separator is shown for common blocks. Aliases are distinguished by their double-quoted strings.The .rel file format can store symbol names of up to 7 characters in length. However, MACRO-80 truncates symbols to 6 characters so that it has one character in reserve for extending linking operations such as subtracting two externals from each other. To be compatible (and sensible), --rel truncates externals to 6 characters. For MRAS compatibility, --mras truncates symbols to 7 characters. This is not a problem for MRAS as it doesn't support extended linking. But necessary if you want zmac to produce .rel files that will link with MRAS generated .rel files. The --rel7 option sets symbol truncation to 7 characters so you can assemble files that will link with MRAS output. However, it will break extended linking on labels longer than 6 characters.
The ignoring of single quotes can be handy for tracking alternate register usage. Consider the following code fragment:
ld a,(hl) | |
rra | |
exx | |
ld a,(hl') | |
ex af,af' | |
ld a',(hl') | |
rra' | |
ex af,af' | |
djnz' loop | |
ld d',e' | |
exx |
Although zmac does nothing but ignore the single quotes they are useful for indicating which register is currently active. A more advanced mode where zmac pays attention to the trailing quotes and emits exchange instructions as needed has been considered.
Official Zilog Syntax
The official Zilog syntax for Z-80 has some rather arbitrary restrictions that zmac ignores. For instance, add a,b is the only correct form but sub a,b is invalid as sub b must be used. Here is a list of the official and alternate forms of the various affected instructions. rmn refers to a, b, c, d, e, h, l, (hl), (ix+d), (iy+d) or 8 bit immediate value.
Official Accepted Variant | |
add a,rmn add rmn | |
adc a,rmn adc rmn | |
sub rmn sub a,rmn | |
sbc a,rmn sbc rmn | |
cp rmn cp a,rmn | |
and rmn and a,rmn | |
xor rmn xor a,rmn | |
or rmn or a,rmn | |
jp (hl) jp hl | |
jp (ix) jp ix | |
jp (iy) jp iy | |
ex de,hl ex hl,de | |
ex hl,(sp) ex (sp),hl | |
ex ix,(sp) ex (sp),ix | |
ex iy,(sp) ex (sp),iy | |
in a,(n) in a,n | |
out (n),a out n,a | |
in r,(c) in r,(bc) | |
out (c),r out (bc),r | |
rst 8 rst 1 | |
rst 16 rst 2 | |
rst 24 rst 3 | |
rst 32 rst 4 | |
rst 40 rst 5 | |
rst 48 rst 6 | |
rst 56 rst 7 |
Mnemonic Values
Values for 8080 mnemonics. Note that cpi and jp are interpreted as "compare immediate" and "jump if positive" in 8080 mode.
aci $CE dad $09 ldax $0A rnz $C0 | |
adc $88 dcr $05 lhld $2A rp $F0 | |
add $80 dcx $0B lxi $01 rpe $E8 | |
adi $C6 dec $01 mov $40 rpo $E0 | |
ana $A0 di $F3 mvi $06 rrc $0F | |
ani $E6 ei $FB nop $00 rst $C7 | |
call $CD hlt $76 ora $B0 rz $C8 | |
cc $DC in $DB ori $F6 sbb $98 | |
cm $FC inr $04 out $D3 sbi $DE | |
cma $2F inx $03 pchl $E9 shld $22 | |
cmc $3F jc $DA pop $C1 sphl $F9 | |
cmp $B8 jm $FA push $C5 sta $32 | |
cnc $D4 jmp $C3 ral $17 stax $02 | |
cnz $C4 jnc $D2 rar $1F stc $37 | |
cp $B8 jnz $C2 rc $D8 sub $90 | |
cpe $EC jp $F2 ret $C9 sui $D6 | |
cpi $FE jpe $EA rlc $07 xchg $EB | |
cpo $E4 jpo $E2 rlcr $07 xra $A8 | |
cz $CC jz $CA rm $F8 xri $EE | |
daa $27 lda $3A rnc $D0 xthl $E3 |
Values for Z-80 mnemonics. Ambiguous mnemonics such as ld and inc evaluate to the 8 bit register operation base value. ex is arbitrarily mapped to ex de,hl. Bit and shift operations on (IX+d) and (IY+d) evaluate to 32 bit values and the offset goes into the third byte. and, or and xor can be used in data statements but parsing ambiguity prevents their use in operations.
adc $88 otir $B3ED | |
adcx $8EDD out $D3 | |
adcy $8EFD outd $ABED | |
add $80 outdr $BBED | |
addx $86DD outi $A3ED | |
addy $86FD outir $B3ED | |
and $A0 outp $41ED | |
andx $A6DD pcix $E9DD | |
andy $A6FD pciy $E9FD | |
bit $40CB pfix $DD | |
bitx $4600CBDD pfiy $FD | |
bity $4600CBFD pop $C1 | |
call $CD popix $E1DD | |
ccd $A9ED popiy $E1FD | |
ccdr $B9ED push $C5 | |
ccf $3F pushix $E5DD | |
cci $A1ED pushiy $E5FD | |
ccir $B1ED ralr $10CB | |
cmpx $BEDD ralx $1600CBDD | |
cmpy $BEFD raly $1600CBFD | |
cp $B8 rarr $18CB | |
cpd $A9ED rarx $1E00CBDD | |
cpdr $B9ED rary $1E00CBFD | |
cpi $A1ED res $80CB | |
cpir $B1ED resx $8600CBDD | |
cpl $2F resy $8600CBFD | |
daa $27 ret $C9 | |
dadc $4AED reti $4DED | |
dadx $9DD retn $45ED | |
dady $9FD rl $10CB | |
dcrx $35DD rla $17 | |
dcry $35FD rlc $CB | |
dcxix $2BDD rlca $7 | |
dcxiy $2BFD rlcr $CB | |
dec $5 rlcx $600CBDD | |
di $F3 rlcy $600CBFD | |
djnz $10 rld $6FED | |
dsbc $42ED rr $18CB | |
ei $FB rra $1F | |
ex $EB rrc $8CB | |
exaf $8 rrca $F | |
exx $D9 rrcr $8CB | |
halt $76 rrcx $E00CBDD | |
im $46ED rrcy $E00CBFD | |
im0 $46ED rrd $67ED | |
im1 $56ED rst $C7 | |
im2 $5EED sbc $98 | |
in $DB sbcd $43ED | |
inc $4 sbcx $9EDD | |
ind $AAED sbcy $9EFD | |
indr $BAED scf $37 | |
ini $A2ED sded $53ED | |
inir $B2ED set $C0CB | |
inp $40ED setb $C0CB | |
inrx $34DD setx $C600CBDD | |
inry $34FD sety $C600CBFD | |
inxix $23DD sixd $22DD | |
inxiy $23FD siyd $22FD | |
jp $F2 sl1 $30CB | |
jr $18 sla $20CB | |
jrc $38 slar $20CB | |
jrnc $30 slax $2600CBDD | |
jrnz $20 slay $2600CBFD | |
jrz $28 sll $30CB | |
lbcd $4BED spix $F9DD | |
ld $40 spiy $F9FD | |
ldai $57ED sra $28CB | |
ldar $5FED srar $28CB | |
ldd $A8ED srax $2E00CBDD | |
lddr $B8ED sray $2E00CBFD | |
lded $5BED srl $38CB | |
ldi $A0ED srlr $38CB | |
ldir $B0ED srlx $3E00CBDD | |
ldx $46DD srly $3E00CBFD | |
ldy $46FD sspd $73ED | |
lixd $2ADD stai $47ED | |
liyd $2AFD star $4FED | |
lspd $7BED stx $70DD | |
lxix $21DD sty $70FD | |
lxiy $21FD sub $90 | |
mvix $36DD subx $96DD | |
mviy $36FD suby $96FD | |
neg $44ED xor $A8 | |
nop $0 xorx $AEDD | |
or $B0 xory $AEFD | |
orx $B6DD xtix $E3DD | |
ory $B6FD xtiy $E3FD | |
otdr $BBED |
Values for Z-180 mnemonics.
in0 $00ED | |
mlt $4CED | |
otdm $8BED | |
otdmr $9BED | |
otim $83ED | |
otimr $93ED | |
out0 $01ED | |
slp $76ED | |
tst $04ED | |
tstio $74ED |
Exit Status
0 | No errors. |
1 | One or more errors were found during assembly, or zmac exited with a fatal error. |
Credits
Bruce Norskog originally 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).
To the extent possible under law,
George Phillips
has waived all copyright and related or neighboring rights to
zmac macro cross assembler for the Zilog Z-80 microprocessor.
This work is published from:
Canada.
George Phillips, October 18, 2022. george -at- 48k.ca