Z80 Programming

From Sinclair Wiki
Jump to navigation Jump to search

This article describes how to write machine code programs for the Z80.

Requirements

For writing Z80 machine code programs you will typically need an assembler (to convert Z80 assembly listings into binaries) and probably also an emulator for testing and single-stepping your code. Some emulators include assemblers although the level of usability is somewhat variable. Some popular Z80 assemblers are:

  • z80asm (part of z88dk)
  • binutils-z80 (GNU binary utilities, for the z80-unknown-coff target)

Z80 machine code reference

Registers

The Z80 has a collection of 16-bit registers, some of which can also be accessed as pairs of 8-bit registers. (Or is it the other way round?)

The following table gives details; one-character names imply 8 bits, while two-character names imply 16 bits. (I've done this from memory, so someone should check it, particularly the Access column. Also a proper chart of all this would be good, with things like "load from [nn]" and "16-bit add to"; the current table is incomplete and changing that would clutter it enormously)

Name Description Access
PC Program Counter Write with JP/JR/CALL/RET
A Accumulator All LD, 8-bit arithmetic & logic ops, IN, OUT, CPI[R]/CPD[R], DAA. As AF with PUSH/POP and EX AF,AF'
F Flags register Written indirectly by most ops, directly by CCF/SCF; read with conditional ops (JP/JR/CALL/RET cc). As AF with PUSH/POP/EX
BC User register Most LD, PUSH/POP, 16-bit arithmetic, block instructions, IN, OUT, EXX. As B,C with LD, 8-bit arithmetic & logic, DJNZ.
DE User register Most LD, PUSH/POP, 16-bit arithmetic, block instructions, EXX, EX DE,HL. As D,E with LD, 8-bit arithmetic & logic.
HL User register All LD, PUSH/POP, 16-bit arithmetic, block instructions, EXX, EX DE/SP,HL. As H,L with LD, 8-bit arithmetic & logic.
SP Stack pointer Some LD, 16-bit arithmetic, EX SP,HL, write indirectly with CALL/RET/PUSH/POP.
I Interrupt vector LD A,I; LD I,A
R DRAM Refresh register LD A,R; LD R,A
IX, IY Index registers All LD, PUSH/POP, 16-bit arithmetic, block instructions, EXX. As IXH,IXL/IYH,IHL with LD, 8-bit arithmetic & logic.
AF' Shadow register EX AF,AF'
BC', DE', HL' Shadow registers EXX

Instruction set

Anything enclosed in (brackets) refers to the contents of the memory location pointed to. So LD HL,(0x4000) will store the first two bytes of screen RAM in L and H (the order is backwards because the Z80 is little-endian). Whenever IX or IY is used as a pointer, the instruction contains a literal 'displacement' byte, which is signed. This is typically used for tables and structs. When the action of an opcode is given in C-style notation, a prefixed asterisk has the same meaning. So the action of LD HL,(0x4000) is HL=*0x4000; (although in C you'd probably need to cast that, but never mind).

LD (Load) - many variants, all essentially of the form LD dest,src. Reads a byte or word from src and writes it to dest. Mostly take 4 T-states plus 3 for each byte of literal plus 4 if using IX/IY plus 3 if reading/writing memory plus 8 if using IX+d/IY+d.

LD r,s      Register Direct Load 8-bit    - r and s are 8-bit registers; if either is I or R then the other must be A
LD r,n      Register Load Literal 8-bit   - r is an 8-bit register, n is an 8-bit literal
LD r,(hh)   Register Load Indirect 8-bit  - r is an 8-bit register, hh is HL, IX+d or IY+d
LD (hh),r   As above
LD (hh),n   Load Indirect Literal 8-bit   - hh is HL, IX+d or IY+d, n is an 8-bit literal
LD SP,hh    Register Direct Load 16-bit   - hh is HL, IX or IY
LD rr,nn    Register Load Literal 16-bit  - rr is a 16-bit register (BC, DE, HL, SP, IX or IY), nn is a 16-bit literal
LD rr,(nn)  Register Load Indirect 16-bit - rr is a 16-bit register (BC, DE, HL, SP, IX+d or IY+d), nn is a 16-bit literal.  Value is read LSB first.
LD (nn),rr  As above
LD A,(qq)   Accumulator Load Indirect     - qq is BC or DE, or a 16-bit literal
LD (qq,A)   As above

PUSH - store a value on the stack. Action is *--SP=rrh;*--SP=rrl; (in C notation). 11 T-states, plus 4 if using IX/IY.

PUSH rr     Push 16-bit value to stack    - rr is a 16-bit register (BC, DE, HL, SP, IX or IY)

POP - fetch a value from the stack. Action is *SP++=rrl;*SP++=rrh; (in C notation). 10 T-states, plus 4 if using IX/IY.

POP rr      Pop 16-bit value from stack   - rr is a 16-bit register (BC, DE, HL, SP, IX or IY)

EX (Exchange) family - generally of the form EX dest,src. Swaps the values in dest and src. 4 T-states, plus 15 if writing through (SP), plus 4 if using IX/IY.

EX DE,HL    Exchange DE with HL           - note that FD or DD prefixes don't make this use IX/IY, it still uses HL
EX AF,AF'   Exchange AF with shadow
EXX         Exchange BC,DE,HL with shadows
EX (SP),hh  Exchange word at SP with hh   - hh is HL, IX or IY

Block loads - take 16 T-states, or 21 if they Repeat and BC!=0

LDI         Load and Increment            - *DE++=*HL++; BC--;
LDIR        Load, Increment and Repeat    - *DE++=*HL++; BC--; if(BC) PC-=2;
LDD         Load and Decrement            - *DE--=*HL--; BC--;
LDDR        Load, Decrement and Repeat    - *DE--=*HL--; BC--; if(BC) PC-=2;

Block compares - take 16 T-states, or 21 if they Repeat, BC!=0 and A!=*HL

CPI         Compare and Increment         - compare(A-*HL); BC--;
CPIR        Compare, Increment and Repeat - compare(A-*HL); BC--; if(BC && !ZF) PC-=2; // ZF is Z flag, F&0x40, set by the compare()
CPD         Compare and Decrement         - compare(A-*HL); BC--;
CPDR        Compare, Decrement and Repeat - compare(A-*HL); BC--; if(BC && !ZF) PC-=2; // ZF is Z flag, F&0x40, set by the compare()

(Not finished, obviously)