uz80as (Micro Z80 assembler) 1.03

Table of Contents

Micro Z80 assembler

This manual is for uz80as (Micro Z80 assembler) version 1.03 (updated 4 April 2017).


1 About

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 jorge.giner@hotmail.com .


2 Source syntax

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.


2.1 Statements

Each assembler statement must follow this format:

label operation operands comment

For example:

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.


2.1.1 Label field

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:


2.1.2 Operation field

The operation field is a Z80 instruction opcode (like ADD, SUB, CALL, etc) or an assembler directive (like .ORG, .EQU, etc). 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 ‘.’.


2.1.3 Operands field

The operands field contains the operands for the instruction or the arguments of assembler directives. They can involve expressions, addressing modes, etc.


2.1.4 Comment field

A comment starts with a semicolon ‘;’ character and extends to the end of the line. It can be the only field on a line.


2.2 Expressions

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.


2.2.1 Numeric constants

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

2.2.2 Character constants

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.


2.2.3 String constants

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 .TITLE, .TEXT, .BYTE, .DB, .WORD and .DW. String constants can contain escape sequences to represent non-printable characters. An escape sequence starts with the backslash ‘\’ character:

\n

Line feed.

\r

Carriage return.

\b

Backspace.

\t

Tab.

\f

Form feed.

\\

Backslash.

\"

Double quote.

You can express any other character by using the backslash ‘\’ followed by an octal constant using exactly 3 digits. For example, \377 represents the character value 255, and is the maximum octal value representable using a backslash.

Examples:

"This ends with a newline.\n"
"\tThe name is \"Bye Bug\"."

2.2.4 Program counter

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.

For example:

START  LD HL,START

is equivalent to

START  LD HL,$

2.2.5 Operators

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:

+

Addition.

-

Subtraction or negation.

*

Multiplication.

/

Integer division.

%

Modulo.

<<

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.

&

Binary ‘AND’.

|

Binary ‘OR’.

^

Binary ‘XOR’.

For the shift operators >> and <<, the second operand specifies the number of bits to shift the first operand.


2.3 Assembler directives

The assembler directives are distinguished from the Z80 instructions because they begin with a dot character ‘.’. They are commands to control the assembly process.


2.3.1 BLOCK

[label] .BLOCK expr

expr is evaluated and its value added to the current program counter. Thus .BLOCK n is equivalent to .ORG $+n.


2.3.2 BYTE

[label] .BYTE expr [, expr ...]

The .BYTE directive is supplied with one or more expressions separated by commas. Each 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 .BYTE.


2.3.3 CHK

[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

2.3.4 CODES

Enables the generation of line numbers, opcodes, etc. in the listing file. This is enabled by default, but can be disabled using the .NOCODES directive.


2.3.5 DB

.DB is an alternate name for .BYTE.


2.3.6 DW

.DW is an alternate name for .WORD.


2.3.7 ECHO

[label] .ECHO expr
[label] .ECHO string

Outputs to the console (stderr) an expression value or a string.

For example,

.ECHO "The code size is "
.ECHO PRG_END - PRG_START
.ECHO " bytes long.\n"

may result in:

The program size is 237 bytes long.

2.3.8 EJECT

The .EJECT directive is accepted but not implemented. In TASM, forces a new page in the listing file.


2.3.9 END

[label]	.END [addr]

The .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 .END directive.


2.3.10 EQU

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 .EQU:

label = 25

2.3.11 EXPORT

[label] .EXPORT label [,label ...]

The .EXPORT directive is accepted but not implemented.


2.3.12 FILL

[label] .FILL number_of_bytes [, fill_value]

Outputs number_of_bytes bytes to the output file. The value output to each byte is the least significant byte of fill_value. If no fill_value is supplied, the value 255 is used. It is an error to supply a negative number_of_bytes.


2.3.13 LIST

Turns on the output to the listing file. This is the default. Use .NOLIST to disable it.


2.3.14 LSFIRST

Turns on little endian mode. When a .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. Use .MSFIRST to change this behavior.

This example will output the byte ‘$34’ and then ‘$12’ to the output file.

        .LSFIRST
        .WORD $1234

2.3.15 MSFIRST

Turns on big endian mode. When a .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. Use .LSFIRST to change this behavior.

This example output the byte ‘$12’ and then ‘$34’ to the output file.

        .MSFIRST
        .WORD $1234

2.3.16 NOCODES

Disables the generation of line numbers, opcodes, etc. in the listing file. Use .CODES to enable it again.


2.3.17 NOLIST

Turns off the output to the listing file. Use .LIST to enable it again.


2.3.18 NOPAGE

Accepted but currently ignored. See the PAGE directive as well.


2.3.19 ORG

[label] .ORG expr
[label] *=expr
[label] $=expr

Sets the program counter to the value of expr, which must be in the range [0, 65536]. 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 .ORG.

You can use .ORG or the alternative forms *= and $=.


2.3.20 PAGE

Accepted but currently ignored. See the NOPAGE directive as well.


2.3.21 TEXT

[label] .TEXT string

Outputs the 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\""

2.3.22 TITLE

Accepted but currently ignored.

        .TITLE "Program version 1.2"
        .TITLE "Subtitle"

2.3.23 WORD

[label] .WORD expr [, expr ...]

The .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 .MSFIRST and .LSFIRST directives.

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. For example:

START   .EQU  0
        .WORD $1234, $

will output:

$34 $12 $00

and not:

$32 $12 $02

2.4 Preprocessor directives

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.


2.4.1 DEFINE

#DEFINE macro_name[(arg_label [, arg_label ...])] [macro_definition]

The #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:

        .DB (STARTLO+1)

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 ADDMAC macro:

((x)+(y))

and then searches for x in this text and substitutes it by 5. Next, finds y and substitutes it by 6.

((5)+(6))

Finally, the resulting text is substituted in the original location:

        .DB ((5)+(6))

Note that if you do not supply a parameter, nothing will be substituted:

        .DB ADDMAC(5)

will expand into:

        .DB ((5)+(y))

2.4.2 DEFCONT

#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

2.4.3 INCLUDE

#INCLUDE "filename"

The #INCLUDE directive is used to include the text of another file to be assembled.

For example, if the file ‘common.h’ is:

        .DB 5

and the file ‘prg.asm’ is:

#include "common.h"
        .DB 6

the assembler will compile

        .DB 5
        .DB 6

2.4.4 IF

#IF expr

The #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 #ENDIF or #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

Note that #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:

        LD A,1

2.4.5 IFDEF

#IFDEF macro_name

#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

will assemble:

        CALL spectrum_fun

2.4.6 IFNDEF

#IFNDEF is like #IFDEF, but tests if a macro name has not been defined.


2.4.7 ELSE

Used to end a section that began with an #IF, #IFDEF or IFNDEF directive.


2.4.8 ENDIF

Used to end a section that began with an #IF, #IFDEF, IFNDEF or #ELSE directive.


3 Reference


3.1 Invoking uz80as

uz80as [OPTION]... ASM_FILE [OBJ_FILE [LST_FILE]]

To assemble program.asm you can use:

uz80as program.asm

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:

-h, --help

Display usage information and exit.

-v, --version

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 '-f FF'.

-dmacro, --define macro

Define a macro. If the macro is simply a label, you can use -dLABEL. If it is a macro for text substitution, you have to enclose the macro definition in double quotes. For example: -d"MUL(a,b) (a*b)".

-q, --quiet

Disables the generation of the listing file.

-x, --extended

Enables an extended instruction set. Unofficial instructions will be accepted. See Z80 instruction set.


3.2 Z80 instruction set

Where:

Note that in the official Zilog syntax, some of the arithmetic group of instructions take the A register as first argument: ADD, ADC and SBC. Others do not: SUB, AND, OR, XOR and CP. So you write:

	ADD A,B

and do not write

	ADD B

And you write:

	SUB B

but do not write

	SUB A,B

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.


3.3 Implementation-defined features

Limits:

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: #IF, .BLOCK, .END, .EQU, .FILL (number of positions), .ORG.


3.4 Differences with TASM


4 Source guide


4.1 Modules


5 Copyright

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.