UMBC CMSC 211

UMBC | CSEE


The Stack

The hardware stack and stack pointer sp are necessary to get subprograms to work properly. Actually early CPUs did not have them, and as a result, they were more limited than what we have today. For instance, recursion was not possible.

A stack is a data structure like a queue, but data can only be added or removed at one end, so it is Last-In, First-Out (LIFO) There are special instructions built into the CPU to work with the stack. The sp register points to the newest 16-bit (or 32-bit double word when using the extended registers in the later models) value that is on the stack, which is the next item to be removed. You determine the size of the stack with the .stack directive. The assembler builds a program by putting the .code segment first, followed by the .data segment, and finally, the .stack segment. When putting an item on the stack, it starts at the highest address and grows down.

The instructions are:

Either a memory location (word or double word only, as appropriate), constant, or a register can be specified. If we have set up the code segment as:

X DW 1111
Y DW 2222
Z DW ?
We can have a stack that looks like:
?  
?  
?  
In this case, the SP register really points to what is above the first location. If we execute:
  push X
We now have a stack that looks like:
1111 <- SP
?  
?  
If we then execute:
  push Y
We now have a stack that looks like:
1111  
2222 <- SP
?  
If we finally execute:
  pop Z
We now have a stack that looks like:
1111 <- SP
2222  
?  
The location for the variable Z is set to 2222 and the SP register points to the previous entry. Note that stack location holding 2222 is considered unused and will be overwritten by the next push instruction. You can not count on items popped from the stack remaining in unused stack memory because the operating system also uses the stack.)

The hardware stack is implemented as a normal block of memory of the size you specified in the .STACK pseudo-op.

If you do push X followed by pop X, nothing is changed.

Saving and restoring registers is particularly important when using subprograms. You have the responsibility to save and restore important data in the registers before you call a subprogram and then you are responsible for restoring those registers afterwards.

Subprograms should save and restore any registers that they use, unless they are returning values in certain registers! This means you have to write the instructions to do it!


UMBC | CSEE