The F register
The six flags, where they sit in the byte, which instructions write which of them, and the eight conditions that read them. It ends on P/V doing three different jobs and on the signed comparison the Z80 has no condition for.
The overview of this topic is in Assembly basics. The same topic in M68K, MIPS, RISC-V.
The Z80 keeps six bits about how the last instruction came out, and they live in one byte called f.
The flags panel above the registers is that byte drawn one bit at a time, in the order the bits sit
in it, high bit first.
| bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| is | S | Z | undoc | H | undoc | P/V | N | C |
S, sign. A copy of bit 7 of the result, so it is 1 when the result read as a signed number is negative.Z, zero. 1 when the result was zero.H, half carry. The carry out of bit 3 into bit 4, which nothing butdaareads.P/V, parity or overflow. Three different jobs, below.N, add or subtract. 1 when the last instruction was a subtraction, which nothing butdaareads.C, carry. The carry out of bit 7 (or bit 15), the borrow of a subtraction, and the bit that falls out of a shift or a rotate.
Bits 3 and 5 are copies of bits 3 and 5 of the result. Zilog never documented them, real programs did use them, and this editor does not show them.
The instruction reference prints the flags of each instruction as a
six character string in the order C N P/V H Z S, which is this table read from the bottom up, so
add a,b is +0V+++: C from the result, N reset, P/V holding the overflow, and H, Z and
S from the result.
Which instructions write which
A flag is written from the result, forced to 0 or 1, or left exactly as it was, and knowing which is the difference between a working comparison and a comparison that was destroyed two lines later.
| instruction | S | Z | H | P/V | N | C |
|---|---|---|---|---|---|---|
add, adc, sub, sbc, cp, neg | ✓ | ✓ | ✓ | overflow | ✓ | ✓ |
inc r, dec r | ✓ | ✓ | ✓ | overflow | ✓ | - |
and, or, xor | ✓ | ✓ | ✓ | parity | 0 | 0 |
rlc, rl, rrc, rr, sla, sra, srl | ✓ | ✓ | 0 | parity | 0 | ✓ |
rlca, rla, rrca, rra | - | - | 0 | - | 0 | ✓ |
bit n, r | ? | ✓ | 1 | ? | 0 | - |
add hl,rr, add ix,rr | - | - | ✓ | - | 0 | ✓ |
adc hl,rr, sbc hl,rr | ✓ | ✓ | ✓ | overflow | ✓ | ✓ |
inc rr, dec rr | - | - | - | - | - | - |
ld, ex, exx, push, pop, jp, call | - | - | - | - | - | - |
ldi, ldd, ldir, lddr | - | - | 0 | bc ≠ 0 | 0 | - |
scf | - | - | 0 | - | 0 | 1 |
ccf | - | - | ✓ | - | 0 | ✓ |
✓ means set from the result, 0 and 1 mean forced, - means untouched, ? means undefined.
Three rows of that decide how a program is written. A load never touches a flag, so unlike the
M68K, where a plain move sets them, you can compute an address in the middle of a comparison and
the branch still sees what the cp left. inc and dec on an 8 bit register leave C alone, so
a loop can count with dec b while keeping a carry from an addition. And inc and dec on a pair
touch nothing at all, so after dec bc there is no zero flag and you cannot branch on it.
| after this line | S | Z | H | P/V | N | C |
|---|---|---|---|---|---|---|
add a, 100 | 0 | 0 | 0 | 0 | 0 | 1 |
ld b, a | 0 | 0 | 0 | 0 | 0 | 1 |
inc a | 0 | 0 | 0 | 0 | 0 | 1 |
add hl, de | 0 | 0 | 0 | 0 | 0 | 0 |
Step through it and watch C. It goes to 1 on the second line, survives the load and the inc, and
only the add hl, de on the last line puts it back to 0.
The eight conditions
jp, call and ret all take a condition, and the same eight are written the same way:
| written | jumps when | reads |
|---|---|---|
nz | not zero | Z is 0 |
z | zero | Z is 1 |
nc | no carry | C is 0 |
c | carry | C is 1 |
po | parity odd, or no overflow | P/V is 0 |
pe | parity even, or overflow | P/V is 1 |
p | plus | S is 0 |
m | minus | S is 1 |
jr takes only the first four, because there was no room in a two byte instruction for more, and
that is the practical difference between jr and jp beyond the range. po and pe can also be
written nv and v, the spelling to reach for when the flag is holding an overflow. This assembler
accepts both names for the same condition.
The c in jp c, label is the carry flag, not the register c. jp c, done and ld a, c
have nothing to do with each other, and the assembler tells them apart by where the c is.
cp, and comparing unsigned numbers
cp n computes a - n, throws the answer away and keeps the flags. For unsigned numbers the two
flags you want are Z and C, and the four cases fall out of them:
| you want | after cp n |
|---|---|
a == n | jp z |
a != n | jp nz |
a < n | jp c |
a >= n | jp nc |
C is set when the subtraction had to borrow, which is exactly when a was the smaller of the two.
a > n and a <= n have no condition of their own, so you either swap the operands or write the
test as "not less and not equal".
b comes out at 01, with C at 1 and S at 1. Try changing ld a, 3 to ld a, 7: the subtraction
does not borrow, the jump is taken and b stays 0.
P/V does three jobs
One flag, three meanings, and which one you get depends on the instruction that set it:
- After arithmetic (
add,sub,cp,inc,dec,neg,adc hl,sbc hl) it is the signed overflow: 1 when the true answer did not fit in the signed range. - After logic and shifts (
and,or,xor, theCBrotates,in r,(c)) it is the parity of the result: 1 when the number of 1 bits is even. - After a block instruction (
ldi,ldir,cpi,cpirand the rest) it saysbcis not zero, which is how a program that copies one byte at a time knows there is more to do.
Step through it with the flags panel open. P/V goes 1, then 0, then 1 again, and only the third one
is about a number being too big.
or a with a as the operand is the idiom in the middle of that program. It computes a | a, which
leaves a exactly as it was, and sets S, Z and P/V from it while forcing C to 0. So it is how
you ask "is a zero" without a cp 0, and how you clear the carry before an sbc hl, de. and a
does the same job.
The comparison the Z80 has no condition for
For signed numbers C is the wrong flag, and S on its own is not enough either. When a
subtraction overflows, the sign of the answer is the opposite of the truth.
S comes out at 0 and P/V at 1. -128 minus 1 is -129, which does not fit in a byte, so the answer
wrapped round to 0x7F, whose top bit is 0. jp m would not jump, and -128 really is less than 1.
The M68K has a blt that reads N and V together and gets this right. The Z80 has no such
condition, so the two flags are read one after the other: a < n signed is true when S and P/V
differ.
b comes out at 01, which is the right answer. Try changing ld a, 0x80 to ld a, 7 and cp 1 to
cp 5: 7 is not less than 5, no overflow happens, and b stays 0.
Five instructions for one signed comparison is why 8 bit programs keep their numbers unsigned wherever they can, and why sizes, counts and addresses on this machine are unsigned by habit.
Your turn
The test starts a at 0. Leave 1 in b if a is zero and 0 if it is not, without changing a. The
idiom above is the whole answer.
Show solution
The second one starts a at 100 and c at 200. Leave a at the larger of the two read as
unsigned numbers, which is 200. One cp and one conditional jump.