Creating Software Delay Loops

Since the Z80 runs at a fixed clock speed, you can create a precise time delay by executing a loop a fixed number of times, knowing the exact number of clock cycles each instruction takes.

The Delay Routine: A delay routine typically uses nested loops and relies on the DJNZ instruction, as it’s efficient for looping.

Example: Basic 16-bit Delay Loop

This example uses the BC register pair for a longer delay (B for the outer loop, C for the inner loop).

DELAY:
    LD   B, 10           ; Outer loop count (B)
OUTER_LOOP:
    LD   C, 255          ; Inner loop count (C)
INNER_LOOP:
    DEC  C               ; 1. Decrement C (4 cycles)
    JP   NZ, INNER_LOOP  ; 2. Jump if C ≠ 0 (10/7 cycles)
    
    DJNZ OUTER_LOOP      ; 3. Decrement B; loop if B ≠ 0 (13/8 cycles)
    
    RET                  ; Delay finished

Timing Notes: By calculating the exact cycle count of the instructions inside and outside the loop, you can determine the total delay time (e.g., in milliseconds).

The Refresh Register (R)

The Refresh Register (R) is an 8-bit register whose primary job is to provide address pulses for dynamic memory refreshing. It constantly increments automatically every machine cycle.

Using R for Randomness: Because its value is constantly changing based on the CPU’s internal timing and external factors (like interrupts), reading the R register when you need a simple random number is a common trick in retro game programming.

Reading the R Register: The R register cannot be accessed directly with LD A, R but must be accessed via the LD A, I or LD A, R extended instructions (using the EDH prefix).

    LD   A, 0            ; Clear A
    LD   R, A            ; # Not possible, R is read-only for direct loads

    ; The correct way to read R (using an ED prefix instruction)
    IN   A, (40H)        ; # Read from a hardware timer port instead (safer)

    ; If direct R reading is available:
    LD   A, R            ; # R → A (using the extended instruction format)
    AND  1FH             ; Mask to get the lower 5 bits (a smaller random range)

Best Practice: While R is available, reading a hardware timer port is generally a more reliable source of simple, unpredictable “randomness” in Z80 systems.