The stack is a special area of memory used for temporary storage of data, especially return addresses and register values. It follows a Last In, First Out (LIFO) order, meaning the most recently pushed value is the first to be popped off.
On the Z80, the stack grows downward — each new value is stored at a lower memory address than the last. The stack pointer (SP) always points to the top of the stack.
Games involve lots of fast, nested behaviour — player actions, enemy AI, collisions, rendering, and more. These often rely on:
Understanding how the stack works allows you to:
When your program calls a subroutine with the instruction:
CALL address
the Z80 needs to remember where to come back to after the subroutine finishes. It does this by automatically saving the return address (the next instruction’s location) onto the stack. Here’s what happens step by step:
CALL. This address is what the CPU will return to later.SP by 1.SP again.When the subroutine reaches a RET instruction, the CPU retrieves the return address from the stack so it can resume where it left off:
SP.SP again.CALL.In short: CALL pushes the return address onto the stack so the CPU can come back after the subroutine, and RET pops it off again to resume execution.
Because return addresses are saved on the stack, the Z80 can handle nested subroutines — that is, a subroutine that calls another subroutine. Each return address is pushed onto the stack in turn. As each RET is encountered, the correct return address is popped and execution continues at the right place.
The stack must be large enough to handle these return addresses. On the Tatung Einstein, more than 256 bytes are typically available, which is usually sufficient.
To store or retrieve register values on the stack manually, use PUSH and POP.
PUSH qq
qq is any register pair: AF, BC, DE, or HLIX and IYThis decrements SP twice and stores the register pair at the new stack location.
POP qq
LD HL, 9 ; Load 9 into HL
PUSH HL ; Save HL to stack
LD HL, 0 ; Overwrite HL
POP HL ; Restore original HL from stack
LD A, L ; Copy low byte (9) into A
ADD A, 48 ; Convert 9 into ASCII '9'
RST 8 ; Print '9'
DEFB 158 ; End marker for monitor
RET
CALL DrawPlayer ; Call a routine that uses HL and DE
DrawPlayer:
PUSH HL ; Save HL (we'll use it temporarily)
PUSH DE ; Save DE as well
; ... drawing code here ...
POP DE ; Restore original DE
POP HL ; Restore original HL
RET
This technique is essential when writing reusable subroutines that won't accidentally overwrite important registers — especially in game loops or input handling routines.
CALL pushes the return address onto the stackRET pops the return address and resumes executionPUSH and POP let you manually save and restore registersWrite a small program that:
HLHLWrite a program with a main routine and a subroutine. Use print statements to observe the stack pointer before and after the call and return.
Write a DrawTile subroutine that temporarily uses DE and HL. Use PUSH and POP to protect these registers and test that the values are restored correctly after the call.