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