Z80 Introduction

        .cr     z80         To load this cross overlay

There is no doubt about it, the Z80 is one of the most popular 8-bit micro processors ever. It was designed by a few former Intel employees, who enhanced the design of the Intel 8080. Being backward compatible with the 8080 ensured the Z80 of a large software library right from the start.

To my opinion, but who's asking me, the Z80 is not the easiest processor to start with because of some inconsistencies in the command syntax. This doesn't effect the usefulness of the processor though, only programming it may be a bit confusing.
E.g. the ADD instruction requires you to specify the destination register (ADD A,data), whereas the SUB instruction is happy without it (SUB data).
The rarely used hardware instruction NEG (taking the 2's complement of A) can easily be replaced by the instructions CPL and INC A which has exactly the same result in exactly the same execution time with the same number of bytes used.
Especially the refresh register was a very good idea, simplifying the interfacing with dynamic memory. Unfortunately they only made it 7 bits wide, making it unsuitable for bigger DRAM chips.

TRS-80 The TRS-80 ran on a Z80

I also must admit that there are features in the Z80 that are hardly ever found on any other processor. Especially the interrupt capabilities are state of the art, even by today's standards! Also the Z80 family peripheral chips make the competitors byte the dust!

Later quite some interesting complete micro controller systems were built around the Z80.

Programming Model

The programming model in the picture below shows the most important registers of the Z80 processor. I only include a little summary about the features of the Z80's programming model here. It is not my intention to make the original documentation obsolete, so please refer to the original documentation for further details.

Z80 programming model

All working registers are shadowed by an identical second set. These shadow registers can be used for very fast context switching during interrupts, or can simply expand the standard register set. Special instructions exist to exchange the two register sets.

The Accumulator

The Accumulator is the most important register for 8 bit arithmetic operations. Its standard name is A, which is a reserved word.
The Accumulator and the Flag register F form one single 16-bit register. PUSH and POP treat the Accumulator and F as one pair of registers called AF, where A is the MSB and F is the LSB.

The Program Status Word

The Flag register contains 6 system flags:

SSign Flag
ZZero Flag
0Always "0"
HHalf Carry Flag
0Always 0
P/VParity/Overflow Flag
NADD/SUB flag (used together with H)
CYCarry Flag

General Purpose Registers

Six additional general purpose 8-bit registers assist the Accumulator. Three pairs of these registers can be concatenated to form three 16-bit register pairs. Especially the HL pair is used as a data pointer to transfer data to and from memory and it can be used for a limited number of 16-bit arithmetic functions.

Index register IX and IY

New to the Z80, compared to the 8080, are two identical index registers. These two register assist in indexed addressing of data and supplement the use of the HL pair.

Memory refresh register R

The Z80 has a refresh register which enables easy interfacing with DRAM memory. Seven bits of the R register are incremented after each instruction fetch. The eighth bit remains as set by a previous LD R,A instruction.

The I register

The I register provides the upper 8 address bits during interrupt vectoring, while the interrupting peripheral device supplies the lower 8 address bits of the address. This way interrupt service routines may be located anywhere in memory. The least significant bit of the vector address is always 0, which means that all interrupt service routines should start at an even address.

The Stack Pointer

The stack on an Z80 can be located anywhere in RAM memory, pointed to by the stack pointer SP. Every time something is pushed on to the stack, the SP pointer is decremented, so the stack is growing down in memory.

Stack operations are always performed with register pairs.
A push on the stack, whether it comes from the PUSH instruction, a subroutine call, or interrupt has the following sequence:

  • Decrement SP by 1
  • Save most significant byte of register pair
  • Decrement SP by 1
  • Save least significant byte of register pair

Naturally a pop from the stack has just the opposite effect:

  • Load least significant byte of register pair
  • Increment SP by 1
  • Load most significant byte of register pair
  • Increment SP by 1

The Program Counter

The program counter PC is normally incremented after fetching each instruction or operand byte during program execution. The only way you can change this behaviour is with the jump, call and return instructions. Also interrupts can change the program counter's value.

Timing

Version 3 of the SB-Assembler can display instruction timing information when the TON list option is switched on. The indicated values are the number of T cycles each instruction takes.

Reserved Words

The SB-Assembler Z80 cross overlay has a few reserved words. Reserved words are all register names. You better avoid these reserved words when you assign your own labels. E.g. don't call your labels IX, or A or H.
If you do use the reserved words as label names you may expect unpredictable behaviour of the assembler sooner or later. Please note that the assembler will not warn you if you try to assign a label with a reserved name!
Reserved names can not be used in expressions, like label names can. An Undefined label error will be reported if you do try to use a reserved word in an expression because it is treated as a normal label in this case.

Here's the list of all reserved words:

A, F, B, C, D, E, H, L, I, R, BC, DE, HL, IX, IY, SP, NC, Z, NZ, PE, PO, P, M

Special Features

Addressing modes

In the introduction I've already mentioned that the Z80 assembler doesn't always follow the same syntax rules for equivalent instructions. I used the ADD and SUB example there to show you what I meant.

The SB-Assembler Z80 cross is very forgiving when it comes to these inconsistencies. In the example below you see both operand notations for the ADD and SUB instructions, which are both understood by the SB-Assembler..

        ADD   A,DATA         Normal notation according to Zilog
        ADD   DATA           Has the same effect as the line above
        ADD   A,#DATA        This is also possible
        ADD   #DATA          And this too

        SUB   DATA           Normal notation according to Zilog
        SUB   A,DATA         This is accepted just the same

As you can see in the example above you may also use the immediate operand identifiers, although it is not absolutely necessary. The SB-Assembler will automatically use 8 or 16-bit values, depending on the instruction's needs. If you want another part of the 32-bit number to be loaded immediately into any of the registers or register pairs you could use the other immediate mode identifiers /, =, or \.

The official indexed addressing mode using one of the new index registers is (IX+offset), where offset can be an 8-bit positive or negative number. If that number is 0 it may be omitted in the SB-Assembler. So the official notation (IX+0) can also be written as (IX).

Compound instruction

Another peculiar property of the Z80, inherited from the 8080, is the lack of a Clear Carry instruction. There is an instruction to set the carry and there is one to complement the carry, two instructions that I personally rarely use. But an instruction to clear the carry is strangely missing.
Therefore I've added a compound instruction to clear the carry, called RCF. This instruction is translated by the assembler to the instructions SCF and CCF, effectively clearing the carry.

Restart Vectors

Originally the 8 possible, 8080 compatible, restart vectors are numbered from 0 to 7 in other assemblers. This is very inconvenient, because you can't let the restart vector point to the appropriate vector address by using their label names.
Therefore I've added another addressing format to the RST instructions. Not only can you use the original addressing format, using a number from 0 to 7, but you can also use the appropriate vector addresses as well. The list below shows the relationship between the vector address and interrupt number.

Vector Address Interrupt Number
$000
$081
$102
$183
$204
$285
$306
$387

Please note that the value of the operand after an RST instruction may not be any other value than 0 to 7, $08, $10, $18, $20, $28, $30 or $38. Any other value will result in a Out of range error to be reported.

Overlay Initialization

Two things are set while initializing the Z80 overlay every time it is loaded by the .CR directive.

  • Little endian model is selected for 16-bit addresses and for the .DA and .DL directives. This means that words or long words are stored with their low byte first.
  • The maximum program counter value is set to $FFFF.

Differences Between Other Assemblers

There are some differences between the SB-Assembler and other assemblers for the Z80 processor. These differences require you to adapt existing source files before they can be assembled by the SB-Assembler. This is not too difficult though, and is the (small) price you have to pay for having a very universal cross assembler.

  • Not all assemblers will understand the compound instruction RCF.
  • Not all assemblers will as forgiving when it comes to the operands of the ADD and SUB instructions.
  • The obvious differences in notation of directives and radixes common to all SB-Assembler crosses.
  • Don't forget that the SB-Assembler does not allow spaces in or between operands. Only Version 3 will allow one space after each comma separating operands in the operand field.