ELET2100 - Microprocessors I
The STACK
The stack is one of the most important things you
must know when programming. Think of the stack as a deck of cards. When you put
a card on the deck, it will be the top card. Then you put another card, then
another. When you remove the cards, you remove them backwards, the last card
first and so on. The stack works the same way, you put (push) words
(addresses or register pairs) on the stack and then remove (pop) them
backwards. That's called LIFO, Last In First Out.
The 8085 uses a 16 bit register to know where the
stack top is located, and that register is called the SP (Stack Pointer). There
are instructions that allow you to modify it’s contents but you should
NOT change the contents of that register if you don't know what you're doing!
PUSH & POP
As you may have guessed, push and pop “pushes” bytes on the stack and
then takes them off. When you push something, the stack counter will decrease
with 2 (the stack "grows" down, from higher addresses to lower) and
then the register pair is loaded onto the stack. When you pop, the register
pair is first lifted of the stack, and then SP increases by 2.
N.B: Push and Pop only operate on words (2 bytes ie: 16 bits).
You can push (and pop) all register pairs: BC, DE,
HL and PSW (Register A and Flags). When you pop PSW, remember that all flags
may be changed. You can't push an immediate value. If you want, you'll have to
load a register pair with the value and then push it. Perhaps it's worth noting
that when you push something, the contents of the registers will still be the
same; they won't be erased or something. Also, if you push DE, you can pop it
back as HL (you don't have to pop it back to the same register where you got it
from).
The stack is also updated when you CALL and RETurn
from subroutines. The PC (program counter which points at the next instruction
to be executed) is pushed to the stack and the calling address is loaded into
PC. When returning, the PC is loaded with the word popped from the top of the
stack (TOS).
So, when is this useful? It's almost always used
when you call subroutines. For example, you have an often used value stored in
HL. You have to call a subroutine that you know will destroy HL (with destroy I
mean that HL will be changed to another value, which you perhaps don't know).
Instead of first saving HL in a memory location and then loading it back after
the subroutine, you can push HL before calling and directly after the calling
pop it back. Of course, it's often better to use the pushes and pops inside the
subroutine. All registers you know will be changed are often pushed in the
beginning of a subroutine and then popped at the end, in reverse order! Don't
forget - last in first out. If you want to only push one 8 bit register,
you still have to push it's "friend". Therefore, be aware that if you
want to store away D with pushing and popping, remember that E will also be
changed back to what it was before. In those cases, if you don't want that to
happen, you should try first to change register (try to store the information
in E in another register if you can) or else you have to store it in a
temporary variable.
Before executing a program, you should keep track
of your pushes and pops, since they are responsible for 99% of all computer
crashes! For example, if you push HL and then forget to pop it back, the next
RET instruction will cause a jump to HL, which can be anywhere in the ROM/RAM
and the ccomputer will crash. Note however, it’s also a way to jump to
the location stored in HL, but then you should really use the JMP instruction,
to do the same thing.
Push and pop doesn't change any flags, so you can
use them between a compare and jump instructions, depending on a condition,
which is often very useful.
Acknowledgement
Jimmy Mårdell