Branching and looping is fundamental to computing, and the Z80 has a comprehensive set of instructions to support this.

You can:

  • 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.

Subroutines

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.

Conditions

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:

Condition Description
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

Note:

  • 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.

Looping

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.

In Practice

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