The Z80 Stack: The SP Register and Setting the Stack in Assembler

ZX Land #05

Do You Speak Assembler?

(Part Three)

Hello to everyone who wants something more than just playing and is looking for other ways to express themselves. Knowing how to do a given task really is very satisfying—and if someone praises us for it too...

But now, let’s get down to business!

I’ll try to explain what the “stack” is, how it works, and how to use it.

In a Polish dictionary, under this entry we can read that it is “many things arranged one on top of another; a large number of something...” In a computer, the stack is implemented in a similar way. It is used to store register values.

The stack is, of course, in RAM, and its exact location is specified by a special 16-bit register, SP. The BASIC instruction CLEAR address sets the bottom of the stack. In assembler, it can look like this:

1  LD SP,address ; set the stack starting at a specific address
2  LD SP,HL      ; set the bottom of the stack
3  LD SP,IX      ; from the value in the
4  LD SP,IY      ; given registers
5  LD SP,(address) ; set the stack from the value in two
                  ; memory cells

But hold on! Quite a few new things have appeared. Perhaps I should start by explaining what the “bottom of the stack” is. It is its deepest point. This address means that registers will be stored on the stack from that address backwards—towards the beginning of memory.

The instruction LD SP,HL is a transfer—it copies the value of the HL registers to SP.

The instructions LD SP,IX and LD SP,IY mean the same thing. It is worth knowing that registers IX and IY exist. Various clever books say that they are index registers. But we’ll deal with these registers another time.

The fifth instruction in our list, LD SP,(address), means setting the bottom of the stack from the value of two memory cells. The first cell is the so-called low byte, the second the high byte. The high byte specifies the memory to an accuracy of 256 bytes and is called the page; the low byte tells us which of those 256 bytes we mean.

For example, address 16384 written as low byte–high byte looks like this:

address   = 0
address+1 = 64, so:
256*(address+1)+(address) = 256*64+0 = 16384

But now, perhaps, we can finally talk about how to save a register using the stack. Unfortunately, you cannot save a single register. It must be a pair of registers.

The key instruction is:

PUSH register pair

So, if the processor encounters, for example,

PUSH HL

it will then execute:

DEC SP
LD  (SP),H ; high byte
DEC SP
LD  (SP),L ; low byte

Of course, restoring the registers is done in reverse, and the key instruction is:

POP register pair

For example:

POP HL

involves the processor doing the following:

LD  L,(SP) ; low byte
INC SP
LD  H,(SP) ; high byte
INC SP

Executing the PUSH HL instruction does not necessarily have to be followed by POP HL. We can take the value off the stack into any pair of registers, for example DE.

PUSH HL
POP  DE

The processor does not know whether we saved the value of HL or DE. So, if we saved more than one pair and want them to have the same values after restoring them, the POPs must be in the reverse order.

PUSH HL
PUSH DE
  .
  .     ; program
  .
POP  DE
POP  HL

However, these are not all the instructions that use the stack. There is, among others, the instruction

CALL address

for calling subroutines. It is the equivalent of BASIC’s GOSUB command: when RETURN is encountered, execution returns to the main program. Put simply, CALL pushes the address of the next instruction onto the stack, while RET retrieves that address and jumps to it.

  40000 CALL 40100 ;DEC SP
                LD  (SP),40003
  40003  .
         .
         .
  40100 RET ; JP (SP) INC SP

The stack is very useful, but mistakes caused by using it incorrectly are common when writing procedures. Even the most experienced programmers make them. My first piece of advice is to make sure the numbers of PUSH and POP instructions match. Otherwise, for example, if a POP is missing and RET is executed, the processor will jump to the address formed by the unpopped register pair. The most common result is that the program “hangs” or the system resets.

Now I’ll present a short program that should illustrate the points above. To understand it fully, though, we also need to know the ADD HL,DE instruction, which appears here. It adds the value in DE to HL. We’ll take a closer look at Z-80 arithmetic in the next part of this series.

 10        ORG  40000;displaying
                      the screen
 20 TLO    EQU  127;background color
 30 KOLOR  EQU  7 ;screen color
 40 OPOZN  EQU  2 ;delay
     defining constant values
     using the EQU directive

 50        LD   HL,22528;fill
 60        LD   DE,22529;the
 70        LD   BC,767;screen
 80        LD   (HL),TLO;with the
 90        LDIR         ;background
100 START  LD   DE,32
110        LD   HL,22528
120        LD   B,8
130 PETLA  PUSH BC
140        PUSH HL
150        LD   B,OPOZN
160 SLOW   HALT
170        DJNZ SLOW
180        LD   B,3
190 SUPEL  CALL LOOK ; call the
                     subroutine
200        INC  H  ;increase the
   HL register pair by 256 by
   incrementing only the high
   byte of the HL pair
   - move to the next third
210        DJNZ SUPEL
220        POP  HL
230        POP  BC
240        ADD  HL,DE ;move down
                     one line
250        DJNZ PETLA
260        RET   ;exit the
                          procedure
270 LOOK   PUSH HL;subroutine
280        PUSH BC ;that draws
290        LD   B,32 ;a stripe
300 LOOP   LD   (HL),KOLOR
310        INC  HL
320        DJNZ LOOP
330        POP  BC
340        POP  HL
350        RET   ;exit the
                    subroutine

P.S. If my explanations and observations are too complicated, please send me your comments and get in touch.

SCALMA

Share your thoughts about the article