Branching and looping is fundamental to computing, and the Z80 has a comprehensive set of instructions to support this.
- Perform relative and absolute jumps.
- Perform conditional jumps.
- Jump to a direct address, or to the contents of a register.
- Call and return from subroutines.
Relative and Absolute Jumps
There are two types of jumps in Z80; JR (Jump Relative) and JP (Jump) instructions.
A relative jump is encoded in two bytes, the second byte being a two’s complement number (between 127 and -128), and jumps relative to the PC at the point of the jump, so can only jump to a point within the instruction’s 128-byte vicinity.
An absolute jump is encoded in three bytes, but can jump to any 16 bit address, and is slightly quicker to execute.
The instruction set CALL is similar to JP, but the address of the next instruction (PC) is pushed onto the stack before the jump is performed.
The instruction set RET pops the word off the top of the stack into the program counter (PC), and uses it as the address of the next instruction to be executed.
These are similar to the functions GO SUB and RETURN in BASIC.
The instructions JR, JP, CALL and RET support conditional jumps. If the condition is not fulfilled then program execution carries onto the next instruction.
The conditions can be one of the following:
|C||carry (C) is set|
|NC||carry is not set|
|Z||zero (Z) is set|
|NZ||zero is not set|
|M||sign (S) is set|
|P||sign is not set|
|PE||parity/overflow (P/V) is set|
|PE||parity/overflow is not set|
- The instruction JR only supports the conditions Z, NZ, C and NC.
- The instructions JP (HL), JP (IX) and JP (IY) do not support conditionals, though it is possible to get around this with either self-modifying code, using the stack, or using another branch instruction before the jump (see code examples at bottom).
- In all instances, the flags are preserved.
There is a special loop instruction DJNZ. This decreases the register B, and performs a JR if the result is not zero. The flags are preserved, and since this is a relative jump can only jump within the instruction’s 128-byte vicinity.
Most of the maths operations detailed in the previous chapter will set or clear some or all of the condition flags, so it’s easy to start combining those with branching operations to create simple programs.
Here is a sample code snippet that counts how many bits are set to 1 in the accumulator (register A). It returns the result in register C, with register A containing it’s original value.
LD A,%00110001 LD B,8 ; Number of times to loop (8 bits) LD C,0 ; Store the answer in C LOOP: RRCA ; Rotate A right, with bit 0 moved to bit 7 and the carry flag JR NC, SKIP ; If the carry flag is not set, that bit was 0, so we skip the next bit INC C ; Increment the C register SKIP: DJNZ LOOP ; Decrease B, and loop 8 times
This code snippet does the equivalent of JP NZ (HL):
JR Z, SKIP JP (HL) ; This instruction will only be called if the Z flag is NZ SKIP: ... ; The rest of your code here
An alternative to the above, using self-modifying code:
LD (LABEL+1),HL ; Store the contents in HL directly in the jump instruction LABEL: JP NZ, &0000 ; The jump instruction is encoded as C2 HH LL, where HH and LL are the H and L address bytes
And finally, a neat solution using the stack:
PUSH HL ; Push the address onto the stack RET NZ ; Pop it back off, and start executing at that address