This manual is for
uz80as (Micro Z80 assembler) version
1.03 (updated 4 April 2017).
uz80as is a cross assembler for the Zilog Z80 microprocessor.
It accepts source files with the same syntax accepted by the Telemark Cross Assembler (TASM), with only minor differences.
uz80as is free software.
The latest version of the program, source code and documentation can be found at http://jorgicor.sdfeu.org/uz80as.
For bug reports and suggestions, you can write to Jorge Giner at firstname.lastname@example.org .
Each line of the source file must be a an assembler statement or a preprocessor directive. Mathematical expressions can be used where a number is expected.
Each assembler statement must follow this format:
label operation operands comment
START LD A,5 ; Load 5 into A
Everything that starts with an alphabetic character at the first column is considered a label.
A label must be separated by the rest of fields on the line by a space, tab or a colon ‘:’.
The operation can be a Z80 instruction or an assembler directive (assembler directives start with a dot ‘.’).
Assembler directives can start at column 1, as they start with a dot ‘.’ and cannot be taken for a label. But a Z80 instruction cannot start at column 1, or it would be taken as a label.
The operation must be separated by the operands (if any) with a space.
The rest of the line is the operands field until we reach the end of line or a semicolon ‘;’.
If we find a semicolon ‘;’ character at any position, the rest of the line is ignored.
All of these fields are optional except the operands field which, if present, must be preceded always by an operation field.
These are examples of statements:
; Program version 3 .ORG 4000 LABEL LD A,5 ADD 6 ; Add 6 MEM .FILL 2 .END ; End of program
A backslash character ‘\’ can be used to simulate a new line. Then it is possible to put one or more statements on the same line:
LD A,B\ LD B,C\ LD C,A
Remember that, as the backslash character simulates a new line, if the first character after it is alphabetic, it will be considered as a label. That is why we leave a space after the backslash in the example above.
The label field must start with an character from ‘A’ to ‘Z’. The next characters, if any, must be letters, numbers, underscores ‘_’ or periods ‘.’. Any other character terminates the label, and it must be a space, a tab or a colon ‘:’.
A label with more than 31 characters will issue an error.
Labels are case sensitive, so ‘START’ is different from ‘start’.
Each label has a value, which is:
.EQUdirective follows the label, the label takes the value of the expression introduced by the
.ORGdirective follows the label, the label takes the address that the
.ORGdirective is setting.
The operation field is a Z80 instruction opcode (like
CALL, etc) or an assembler directive (like
If it is a Z80 instruction, it cannot start at the first column of the line, because in that case it would be considered a label.
Assembler directives can start at any column as they start with a dot ‘.’.
The operands field contains the operands for the instruction or the arguments of assembler directives. They can involve expressions, addressing modes, etc.
A comment starts with a semicolon ‘;’ character and extends to the end of the line. It can be the only field on a line.
Wherever a number is accepted in the operands of an instruction or directive, you can use an expression instead. An expression is formed using labels, constants, the program counter symbol, operators and parenthesis.
A decimal numeric constant is expressed normally by the decimal number, optionally followed by the ‘D’ suffix.
An hexadecimal constant is expressed by using the ‘$’ prefix or by using the ‘H’ suffix. It can be formed by the digits from ‘0’ to ‘9’ and the letters from ‘A’ to ‘F’. Note that an hexadecimal constant that uses the suffix form and starts with a letter must be prefixed with a ‘0’ digit or it will be taken as a label.
An octal constant is expressed by using the ‘@’ prefix or by using the ‘O’ suffix. It can be formed by the digits from ‘0’ to ‘7’.
Binary constants are expressed using the ‘%’ prefix or by using the ‘B’ suffix.
All the letters for hexadecimal constants or suffixes can be in lower case as well.
For example, all these values represent the decimal number 255, using different notations:
Decimal 255 or 255D Hexadecimal $FF or 0FFH Octal @377 or 377O Binary %11111111 or 11111111B
Character constants are single characters enclosed in single quotes, for example: ’c’.
The ASCII code of the character is used as the value.
Non-printable characters cannot be expressed this way, but you can use the
.TEXT directive instead.
String constants are one or more characters enclosed in double quotes, for example: "This is a string." .
String constants are not allowed in expressions but can be used with certain directives, like
String constants can contain escape sequences to represent non-printable characters.
An escape sequence starts with the backslash ‘\’ character:
You can express any other character by using the backslash ‘\’ followed by an octal constant using exactly 3 digits.
\377 represents the character value 255, and is the maximum octal value representable using a backslash.
"This ends with a newline.\n" "\tThe name is \"Bye Bug\"."
In an expression you can use the value of the current program counter which is the Z80 address that will be assigned to the line we are assembling. You can use the dollar character ‘$’ or the asterisk ‘*’ to represent the current program counter.
START LD HL,START
is equivalent to
START LD HL,$
All operations are done using at least 32 bit signed precision. An expression is evaluated left to right and there is no operator precedence. Use parenthesis if you want to change the precedence. For example:
1 + 2*3 + 4
will be evaluated as:
((1 + 2) * 3) + 4
Use parenthesis to indicate the desired order of evaluation:
1 + (2*3) + 4
Summary of operators:
Subtraction or negation.
Logical left shift.
Arithmetic right shift (the sign bit fills new positions).
One’s complement (invert all bits).
= or ==
Equal. The result is 1 if equal, 0 if not.
Not equal. The result is 1 if not equal, 0 if equal.
Less than. The result is 1 if a < b, 0 otherwise.
Less than or equal. The result is 1 if a <= b, 0 otherwise.
Greater than. The result is 1 if a > b, 0 otherwise.
Greater than or equal. The result is 1 if a >= b, 0 otherwise.
For the shift operators
<<, the second operand specifies the number of bits to shift the first operand.
The assembler directives are distinguished from the Z80 instructions because they begin with a dot character ‘.’. They are commands to control the assembly process.
[label] .BLOCK expr
expr is evaluated and its value added to the current program counter.
.BLOCK n is equivalent to
[label] .BYTE expr [, expr ...]
.BYTE directive is supplied with one or more expressions separated
expr can be a numeric expression or a string constant.
If the expression is numeric, the lower eight bits of the result are output to the object file.
If the expression is a string, for each character its ‘ASCII’ value is output to the object file.
START .BYTE 'a', "hello", 5 - START
Note that the program counter symbol used in any of the expressions refers to the value the program counter had at the beginning of the line, and not at the start of each expression.
You can use
.DB as an alternative name for
[label] .CHK expr
The checksum directive, takes all the bytes from the address
expr up to the current address, and adds them.
The least significant byte of the result is output to the object file.
The address defined by
expr must be in the range
[0, current program counter[.
For example, this will output in the object file the bytes 1, 2, 3, 4 and 10:
START .DB 1, 2, 3, 4 .CHK START
Enables the generation of line numbers, opcodes, etc. in the listing file.
This is enabled by default, but can be disabled using the
.DB is an alternate name for
.DW is an alternate name for
[label] .ECHO expr [label] .ECHO string
Outputs to the console (stderr) an expression value or a string.
.ECHO "The code size is " .ECHO PRG_END - PRG_START .ECHO " bytes long.\n"
may result in:
The program size is 237 bytes long.
.EJECT directive is accepted but not implemented.
TASM, forces a new page in the listing file.
[label] .END [addr]
.END directive should be the last one in the program.
It is accepted only for compatibility with
TASM but, if not used, we will only issue a warning.
If it is present, it is an error to use any directive or instruction that generates code after it.
It is an error to have more than one
label .EQU expr
Normally, a label takes the value of the current program counter, but you can assign the result of an expression to a label. The label is mandatory in this case.
An alternative syntax uses the equal sign ‘=’ instead of
label = 25
[label] .EXPORT label [,label ...]
.EXPORT directive is accepted but not implemented.
[label] .FILL number_of_bytes [, fill_value]
number_of_bytes bytes to the output file.
The value output to each byte is the least significant byte of
fill_value is supplied, the value 255 is used.
It is an error to supply a negative
Turns on the output to the listing file.
This is the default.
.NOLIST to disable it.
Turns on little endian mode.
.WORD directive is found, it will take the 16 lower significant bits of the value, and from these the least significant byte will be output first to the object file, then the most significant byte.
This is the default.
.MSFIRST to change this behavior.
This example will output the byte ‘$34’ and then ‘$12’ to the output file.
.LSFIRST .WORD $1234
Turns on big endian mode.
.WORD directive is found, it will take the 16 lower significant bits of the value, and from these the most significant byte will be output first to the object file, then the least significant byte.
.LSFIRST to change this behavior.
This example output the byte ‘$12’ and then ‘$34’ to the output file.
.MSFIRST .WORD $1234
Disables the generation of line numbers, opcodes, etc. in the listing file.
.CODES to enable it again.
Turns off the output to the listing file.
.LIST to enable it again.
Accepted but currently ignored. See the PAGE directive as well.
[label] .ORG expr [label] *=expr [label] $=expr
Sets the program counter to the value of
expr, which must be in the range
expr can have references to the current program counter.
For example, to advance the program counter to the next 256 boundary, we can use:
.ORG ($ + 0FFH) & 0FF00H
Note that a label that is used with an
.ORG directive will take the value of the program counter set by the
You can use
.ORG or the alternative forms
Accepted but currently ignored. See the NOPAGE directive as well.
[label] .TEXT string
ASCII value of each character of the supplied string to the object file as a byte.
Special characters can be embedded in the string using escape sequences.
See String constants.
msg1 .TEXT "Enter the file name\n" msg2 .TEXT "Say \"YES\" or \"NO\""
Accepted but currently ignored.
.TITLE "Program version 1.2" .TITLE "Subtitle"
[label] .WORD expr [, expr ...]
.WORD directive accepts an expression or a list of expressions, and outputs the 16 bit value of each expression as two bytes.
The default is to output the least significant byte first.
You can change this behavior with the
Note that if you use the program counter symbol (‘$’) in any expression in a
.WORD directive, it takes the value of the program counter at the beginning of the line, and not its value at the start of each expression.
START .EQU 0 .WORD $1234, $
$34 $12 $00
$32 $12 $02
The preprocessor directives can be used to assemble or not some parts of the source, to include text from other files to assemble, and to define macros that can cause text substitution.
#DEFINE macro_name[(arg_label [, arg_label ...])] [macro_definition]
#DEFINE directive is used to define a macro name.
Macro names can be used for text substitution.
For example, you can define a label to be expanded to arbitrary text prior compilation:
#DEFINE STARTLO (START & 255) .DB STARTLO+1
When the assembler finds
STARTLO, it will substitute it by the text
(START & 255), so it will finally assemble this text:
.DB (START & 255)+1
The substitution is recursive. For example:
#DEFINE STARTLO (START & 255) #DEFINE STARTLO_PLUS_1 (STARTLO+1) .DB STARTLO_PLUS_1
This will expand first to:
And then to:
.DB ((START & 255)+1)
Note that you can define a macro label that expands to no text:
#DEFINE VOID .DB 5 VOID .DB 6
And you can make synonyms for directives to, for example, allow to compile the syntax from other assemblers.
For example, imagine an assembler which does not use directives that begin with a dot as we do.
To assemble a source that was written for that assembler with
uz80as, you can use a set of defines at the beginning of your source file, and use them later:
#DEFINE DB .DB #DEFINE DW .DW ... DB 5, 6, 7
You can define macros with arguments, for example:
#DEFINE ADDMAC(x,y) ((x)+(y)) .DB ADDMAC(5,6)
This works first by taking the text of the
and then searches for
x in this text and substitutes it by
y and substitutes it by
Finally, the resulting text is substituted in the original location:
Note that if you do not supply a parameter, nothing will be substituted:
will expand into:
#DEFCONT can be used to add more text to the previous defined macro.
The macro text will always form an unique line, so remember to use the backslash character if you are forming multiline statements:
#DEFINE ADDMAC(x,y) LD A,x #DEFCONT \ LD B,y #DEFCONT \ ADD B
#INCLUDE directive is used to include the text of another file
to be assembled.
For example, if the file ‘common.h’ is:
and the file ‘prg.asm’ is:
#include "common.h" .DB 6
the assembler will compile
.DB 5 .DB 6
#IF directive evaluates the supplied expression.
If the value of the expression is zero, the next lines are ignored by the assembler, until an
#ENDIF or an
#ELSE directive is found.
If the value of the expression is not zero, the next lines are assembled normally, until an
#ELSE directive is found.
In this case, if we find an
#ELSE directive, the next lines after the
#ELSE will be ignored.
In this example, as the expression evaluates to something different than zero, the line
LD A,1 will be assembled and
LD A,0 ignored:
ASSEMLE .EQU 1 #IF ASSEMBLE LD A,1 #ELSE LD A,0 #ENDIF
On the other hand, here the opposite will happen:
ASSEMLE .EQU 1 #IF !ASSEMBLE LD A,1 #ELSE LD A,0 #ENDIF
#IF directives can be nested:
TRUE .EQU 1 FALSE .EQU 0 #IF TRUE #IF FALSE LD A,0 #ELSE LD A,1 #ENDIF #ELSE #IF TRUE LD A,2 #ELSE LD A,3 #ENDIF #ENDIF
In this example, this code will be assembled:
#IFDEF is like
#IF, but tests if a macro name has been defined.
#DEFINE SPECTRUM #IFDEF SPECTRUM CALL spectrum_fun #ELSE CALL amstrad_fun #ENDIF
#IFNDEF is like
#IFDEF, but tests if a macro name has not been defined.
Used to end a section that began with an
Used to end a section that began with an
uz80as [OPTION]... ASM_FILE [OBJ_FILE [LST_FILE]]
To assemble program.asm you can use:
If there are no errors, this will generate the file program.obj with the binary machine code of the program. Also, the file program.lst will be generated with the listing of the source program, plus more info like line numbers, the value of the program counter at each line, the generated machine code in hexadecimal, etc.
You can give another names to the object and listing files by specifying their names after the name of the source file. For example, this will generate the object file with the name program.bin and the listing file with the name list.txt:
uz80as program.asm program.bin list.txt
Additional command line options can be used before the these arguments. They are:
Display usage information and exit.
Display version information and exit.
-f nn, --fill n
By default, the entire memory addressable by the Z80 (64K) is filled by zero.
You can specify a different value to fill the memory.
'nn' must be formed by two hexadecimal digits.
For example, to fill the memory with the value 255 decimal, use
-dmacro, --define macro
Define a macro.
If the macro is simply a label, you can use
If it is a macro for text substitution, you have to enclose the macro definition in double quotes. For example:
Disables the generation of the listing file.
Enables an extended instruction set. Unofficial instructions will be accepted. See Z80 instruction set.
Note that in the official Zilog syntax, some of the arithmetic group of instructions take the
A register as first argument:
Others do not:
So you write:
and do not write
And you write:
but do not write
By default, it is an error to use the unofficial forms of these instructions, but you can enable them by specifying the
-x option at the command line.
With this option enabled, you can write this:
ADD A,B ADD B SUB A,B SUB B
For compatibility with other assemblers, it is better to keep the official syntax.
In the places where we can specify an expression, sometimes we require that the labels referenced in the expression have already a well defined value in the first pass of the assembler.
The following directives do not allow to specify a label not already defined at the point where the directive appears in the first pass:
.FILL (number of positions),
uz80asonly generates binary object files.
uz80asallows for forward references of labels in the first pass of the assembler can differ from
TASM. See Implementation-defined features.
TASMalways needs a space or a colon ‘:’ character after a label. Anything else is considered an error. So this does not compile in
TASMbut it is accepted by
TASM, for some reason, ignores the ‘2’ in the following expression and treats it as ‘*-3’, that is, the current program counter minus 3. We correctly parse it as ‘2*(-3)’.
Something similar happens with the symbol
* used for the current program counter with the multiplication operator.
.DB ***, **2
are incorrectly parsed by
We correctly parse them as
$*2, that is, the current program counter multiplied by itself or by 2.
TASMsays that it uses logical right shifts (inserting zeros) for the right shift operator, but it is actually using arithmetic right shift (the sign bit is extended) at least on x86 machines. We use arithmetic shift as well. So this results in
uz80as, instead of
uz80as only the least
N bits of the second operand are used in the shifting operation, where
N is the number of bits used by an integer on this machine, minus one.
For a system where an integer is 32 bits,
N is 31.
This seems compatible with
TASM, on the other hand, shows a strange behavior. For example, this generates an object code of 256 bytes (?) without error:
.ORG 65536 .DB 1 .END
TASM accepts this as well without error:
.ORG -1 .DB 1 .END
.FILL. Starting at
.ORG 0, using
TASMgenerates a 65535 length object file;
.FILL 65536generates a zero length object file.
.FILL -1 issues an error;
.FILL 65536 correctly generates a 65536 length object file.
.LOCALLABELCHARare not accepted at this moment by
uz80as, but they are planned.
.ENDdirectives and assembles code after it, at least in binary mode. We don’t accept that, but we accept programs without an
.CHKdirective works in
TASM. Given the description in the manual, this should generate the byte 37H by the
.CHKdirective, but generates 0BH.
START .DB 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 .CHK START
.CHK directive can generate a value completely different than
TASM, but should correctly implement what this manual says.
Aregister as first argument or without it (if the
-xoption is specified).
TASMsticks to the official Zilog syntax. See Z80 instruction set.
TASMaccepts including files without enclosing them in double quotes:
We always require the double quotes:
TASMallows a trailing comma in
.BYTE 1, 2, 3,
We do not allow for trailing commas, you should write:
.BYTE 1, 2, 3
This manual is for uz80as (Micro Z80 assembler) version 1.03 (updated 4 April 2017).
Copyright © 2016 Jorge Giner Cordero
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.