Introduction
The stack is a Last In First Out (LIFO) data structure that programmers use to temporarily store and retrieve data. An analogy is a stack of plates, where the last plate you put on the stack will be the first one you would pull off. In programming terms, the last piece of data we put on the stack will be the first piece of data that we pull off. While the stack is often spoken of as a data structure, it also refers to the area of memory where the stack data is stored.
The most common use of the stack is for subroutines and interrupts. For instance, when a subroutine is called from the main program, the values of the current registers in the program can be stored or pushed on the stack. The subroutine can then use the registers as needed without destroying essential data (which is stored on the stack). When the subroutine is finished and control is passed back to the main program, the registers for the main program can then be restored by pulling values off the stack. Note that the programmer must keep careful track of the pushes and pulls to make sure that the right data is being stored or retrieved.
The stack pointer "SP" is the register which holds the address of the top of the stack. On the HC12 and HCS12, the SP points to the last byte placed on the stack. That is, the SP register contains the memory address of the last byte put on the stack, and data is pushed or pulled to/from this address. The key point is that the stack pointer SP is a register which always contains a memory address, and this memory address is basically the "active" region of the stack.
The two main instructions that use the stack are push and pull instructions. During a push instruction such as PSHA, we store a data value on the stack. To do this, the SP is first decremented by one to point to a lower address, and then the value in register A is put in the stack memory at this lower address. During a pull instruction such as PULB, the procedure is reversed and we retrieve a data value from the stack. The register B is first loaded with the contents of the memory location pointed to by the SP, and then the SP is incremented by one to point to a higher address.
The SP should always be initialized, and after initialization will usually will point to the last available address in RAM +1. On the HCS12, the last available address in user RAM is RAM_END = $3BFF (recall the DBug12 uses $3C00 - $3FFF for its stack). This gives RAM_END +1 = $3C00, so the SP should be initialized using "LDS #$3C00". During the first PSHA, for instance, the SP would decrement by one from $3C00 to $3BFF and the contents of Register A would be put there (at $3BFF).
HCS12 Stack Example
The code below illustrates the use of the stack. The program simply swaps the value of the A and B accumulator using reversing the order of pulls. You can follow this very nicely by loading it into the Dragon12 and using DBug12. Set the PC to $1000 (">PC 1000") and then single step through the program using the trace (">t") command. Watch memory with the command ">md 3bff" to see how data is added to the stack, and follow the registers to see how the contents of A and B change and how SP changes. You can do the same in the Code Warrior Simulator.
;****************************************************************
;*
;* UTEP EE3376 "swap.abs.s1"
;* Oct 18, 2003
;* The code below demonstrates a simple use of the stack.
;* The values in registers A and B are swapped.
;* Program assumes Code Warrior is used.
;*
;****************************************************************
; export symbols
XDEF Entry ; export 'Entry' symbol
ABSENTRY Entry ; for absolute assembly: mark this as application entry point
RAM EQU $1000 ; absolute address to place variables (and code/data here)
STACK EQU $3C00 ; end of user RAM (+1) on HCS12
;------------------------------------------------------
; code section
ORG RAM ;set PC to $1000
Entry:
LDS #STACK ; initialize stack pointer
LDAA #$12 ; initial data in A
LDAB #$24 ; initial data in B
; now, do the swap via the stack
PSHA ; first push data on the stack
PSHB
PULA ; then reverse the pull sequence
PULB
swi ; end the program
;****************************************************************
The graphic below illustrates the program above. Note that the stack pointer moves down in address as we add data to the stack using push commands. The direction of stack growth is indicated on the diagram. Notice that moving vertically upwards corresponds to moving down in addresses. This is how Motorola likes to present it - the stack grows up as addresses go down, and the stack top is at the lowest address where the SP currently points to. Remember that the stack is a section of RAM. Once we put data on the stack, it stays in RAM until we explicitly overwrite it or power off. When we pull data from the stack, we don't actually remove the data, but just move the stack pointer to a higher address.