|
DevelopmentSega Master System / Mark III / Game Gear |
Home - Forums - Games - Scans - Maps - Cheats - Credits |
A stack is a general data structure which stores data in a last-in, first-out structure. Many processors, including the Z80, use a stack to facilitate function calls (and by extension interrupts) and temporary data storage for user code.
Data may be added to the stack, which is known as a push, or removed from it (pop).
The stack is defined by the stack pointer register sp
. This contains the memory address of the current "top" of the stack. When a 16-bit value (two bytes) is push
ed onto the stack, sp
is decremented by two, and the data is written to the memory location it now points to. When a value is pop
ped, the 16-bit value is retrieved from the currently pointed location and the stack pointer is then incremented by two. Thus, the stack is an area of memory extending from one byte before the initial sp
value downwards through the address space.
For user code, only regular register pairs (af
, bc
, de
, hl
, ix
and iy
) can be pushed onto the stack. Other opcodes and operations implicitly using the stack (call
, rst
, hardware interrupts, ret
/reti
/retn
) push/pop the program counter (pc
) register.
The most simple use is to save the value in a register for later use. A contrived example is:
It is common to do this when you need to access a particular register, but it contains an important unknown value which cannot be preserved in another register.
An unoptimised function implementation might push all registers at its start, and pop them all at the end, to make itself reusable, but efficiency might require that this is not always done. For interrupt handlers, it is sometimes necessary.
If an operation can only be performed on certain registers, push/pop can be used to transfer values; however, it is generally slower than a normal register copy.
slower than
Push and pop opcodes are relatively slow and can be used to provide delays. For example,
will use 29 cycles in 4 bytes and has no effect on the system.
Any call
or rst
will push the pc
onto the stack. Due to the way the Z80 operates, this will have been pre-incremented so it points to the next instruction after the call. A hardware interrupt will have a similar effect, except that it may occur mid-instruction (for instructions like ldir
) but it is all handled by the Z80 so it works correctly.
On ret
urning from a function/interrupt, pc
is popped from the stack and execution carries on as expected.
Thus, to use functions and interrupts, it is necessary to define the stack correctly.
It is common practice on many systems to pass function parameters on the stack in the form:
(or variants of this, according to the calling convention in use.)
The function is responsible for extracting the parameters without losing the return address, so more advanced stack manipulation (such as dealing directly with the value of sp
) is needed.
In general, this is not recommended in Z80 code. It takes more space and is far slower than passing parameters in registers. If you need to pass in more parameters than can fit into the available registers, consider using a memory structure instead.
If your code contains deeply recursive function calls, or contains some mis-matched push
es, the stack will grow and grow until it exceeds the space available to it. This will result in it overwriting other memory, and eventually it may run out of RAM altogether. Thus, don't have deeply recursive function calls (if you can help it; all recursions can be made into iterations) or mis-matched push
es (at all).
The effect on the stack of a recursive function can be alleviated by optimising it to require less stack space per recursion.
The stack pointer sp
has to be set before any interrupts can happen, because they will attempt to use it. Thus, in any program's startup code, it has to perform a ld sp,nnnn
operation as early as possible - usually, immediately after the initial di
and im 1
instructions.
Because the stack grows downwards in RAM, it is common practice to start it at the highest available address, because then it grows into the "unused space" above the "regular" memory area, which is conventionally allocated starting at the lowest address. However, on the SMS, the very highest area of RAM is affected by various common hardware register writes - writes to $fffc-$ffff for paging, $fff8-$fffb for the 3D glasses, and certain other registers from $fff0 for official Sega development hardware - which are mirrored in RAM at $dff0-$dfff. Thus, it is common practice to initialise the stack pointer to $dff0: