Learn Z80 assembly · Lesson 2 of 7
A and the flags, B to L, the pairs BC, DE and HL, SP and PC
Lesson 1 used HL as a pointer and BC as a counter without asking why those two. This lesson writes the alphabet along row 4 and a white block under each letter, and on the way it builds an address a byte at a time, so you can see what the pairs are made of.
START: LD H,3DH ; H, the High byte of the address ...
LD L,00H ; ... and L, the Low byte: HL is 3D00H
LD D,H ; DE is a copy of HL, made a byte at a time
LD E,L
LD A,E ; 8-bit sums go through A
ADD A,40H ; E plus 40H, one row of 64 cells ...
LD E,A ; ... back into E: DE is 3D40H, row 5Carried over from lesson 1: ORG 7000H, HL as the pointer into the screen at 3C00H, LD (HL),... and INC HL, the white block 0BFH, a loop closed by JR NZ, and the WAIT loop at the end. New: setting H and L apart, a second pointer in DE with LD (DE),A, an 8-bit counter in B with DEC B, and ADD A.
The registers
The Z80 has seven 8-bit registers you name in code, and one you mostly don't:
- A, the accumulator. 8-bit arithmetic and logic (
ADD,SUB,AND,OR) take one operand from A and leave the result in A;CPcompares with A and sets only the flags. That is why lesson 1 copied B into A before it couldOR C. - F, the flags. Each instruction that computes sets some of its bits: Z when the result was zero, C on a carry or borrow, S for the sign, and a few more.
JR NZand its kin read F. There is noLD F: the flags change as a side effect of the work. - B, C, D, E, H, L, six general registers, which pair up as BC, DE and HL for 16-bit values.
Then the two 16-bit registers that run the program: PC, the program counter, the address of the next instruction, and SP, the stack pointer, which lesson 5 uses for CALL and RET.
In JavaScript terms a pair is two bytes of one number:
const hl = (h << 8) | l; // H is the high byte, L the low byte
const h = hl >> 8, l = hl & 0xff;So LD H,3DH and LD L,00H together are LD HL,3D00H, one byte at a time. Nothing joins them but the instructions that treat them as a pair.
Why BC, DE and HL, and not BE
Inside an instruction each register is a 3-bit number: B is 0, C 1, D 2, E 3, H 4, L 5, the memory at HL is 6, and A is 7. LD r,r' is one byte, 01 then the destination's three bits then the source's: LD D,H is 01 010 100, 54H, and LD (HL),C is 01 110 001, 71H. (That order is Fugløy's own decoding table, in packages/z80/src/tables.ts.)
A pair is a register and its neighbour: 0 and 1, 2 and 3, 4 and 5. The pairs are fixed by those numbers, so there is a BC and a DE, but no BE.
H for High, L for Low
The names come from the Z80's ancestors. Intel's 8008 had A, B, C, D, E, H and L, and it reached memory only through H and L, the High and Low bytes of an address. The 8080 kept that and paired B with C and D with E for 16-bit work. The Z80 ran the 8080's programs, so it kept all of it, and added IX and IY (lesson 7), a second, shadow set of A, F, B to L, and more.
That history is still in the instruction set. HL is the pointer every register can use: LD (HL),C stores C, and LD B,(HL) loads B. Through BC and DE, only A can go:
NEXT: LD (HL),C ; any register can be stored where HL points
LD A,0BFH ; but only A can be stored where DE points,
LD (DE),A ; so the white block goes through A
INC HL ; both pointers move on one cell
INC DE
INC C ; the next letter
DEC B ; one letter fewer; DEC B sets the zero flag
JR NZ,NEXT ; not zero yet: the next letterOne more difference from lesson 1: DEC B, on an 8-bit register, sets the zero flag, so JR NZ can test it directly. DEC BC, on a pair, sets no flags, which is why lesson 1 needed LD A,B and OR C.
Run it: the alphabet appears along row 4, with a white bar under it on row 5.
Loading the program…
Loading the TRS-80…