This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

proper use of stack pointer (SP) in assembly language



I would like to allocate some temporary words onto the stack for use in an assembly language function, and just want to make sure I do it right. Is it something like the code I have shown below? (Please consider putting a short example like this into the next revision of the compiler docs SPRU514.)

It looks like there are at least two ways to allocate stack space ("MOV *SP++, something" for initialized allocation = "push"; "ADDB SP, #something" for uninitialized allocation), at least two ways to deallocate stack space ("MOV something, *--SP" for a "pop", "SUBB SP, #something" for deallocation), and there are some subtleties with the *-SP[n] addressing mode:

  • when is *-SP[0] ever valid? (if you push 3 onto the stack using MOV *SP++, #3, isn't it now stored in *-SP[1] since the stack pointer has been postincremented?)
  • If I access a 32-bit value on the stack using "MOVL ACC, *-SP[2]" or "MOVL *-SP[2], ACC", does the CPU use *-SP[1] and *-SP[2], or does it use *-SP[2] and *-SP[3]?
  • If my assembly function isn't going to call another function, are there any cases where stack pointer alignment matters?


; uint16_t myfunc(uint16_t *x)
; x is an array of 2 integers
; we return x[0] + x[1], clamped within the range [0, 31]

_myfunc:
; allocate 7 words onto the stack, for pedagogical purposes
; (we could do this more easily w/o the stack)
; *-SP[1]    output
; *-SP[2:3]    31
; *-SP[4:5]    0
; *-SP[6]    x[1]
; *-SP[7]    x[2]

    SETC   SXM

    ; allocate+initialize local stack storage
    MOV    *SP++, *+XAR4[0]
    MOV    *SP++, *+XAR4[1]
    MOV    ACC, #0
    MOVL   *SP++, ACC
    MOV    ACC, #31
    MOVL   *SP++, ACC
    ; allocate an uninitialized word onto the stack
    ADDB   SP, #1

    ; perform computation
    MOV    ACC, *-SP[6]
    ADD    ACC, *-SP[7]
    MINL   ACC, *-SP[2]
    MAXL   ACC, *-SP[4]

    MOV    *-SP[1], AL

    ; now deallocate local stack storage
    ; move output into the accumulator
    MOV    AL, *--SP
    SUBB      SP,#6
   
    LRETR

  • Jason R Sachs said:
    when is *-SP[0] ever valid? (if you push 3 onto the stack using MOV *SP++, #3, isn't it now stored in *-SP[1] since the stack pointer has been postincremented?)

    Yes, a good rule of thumb is the SP should point to the next open spot. 

    Jason R Sachs said:
    If I access a 32-bit value on the stack using "MOVL ACC, *-SP[2]" or "MOVL *-SP[2], ACC", does the CPU use *-SP[1] and *-SP[2], or does it use *-SP[2] and *-SP[3]?

    A 32-bit value will always be even aligned in memory.  That is it takes up an even address and the next (higher address) odd location.   Even if you write the value to an odd address, it will then take up the previous even + the odd.   

    Where you need to be careful is that you don't write a 32-bit value to an odd location and by doing so overwrite a previous 16-bit value (at the previous even address)

    Example:

    0x0000  <- 32-bit write to even address will occupy address 0x0000 and 0x0001
    0x0001 
    0x0002  <- 16 bit write uses address 0x0002
    0x0003  <- 32-bit write to odd address: will overwrite address 0x0002 and also use 0x0003

    The compiler keeps the SP aligned to an even address to help manage this easier.   A function will allocate a stack frame using ADDB at the beginning of a function and de-allocating it using SUBB at the end.  The stack frame is always an even number of words - even if a hole is created of 1 word to do so.  The compiler then knows the SP points to an even address and therefore knows how to fill values onto the stack.

    Jason R Sachs said:
    If my assembly function isn't going to call another function, are there any cases where stack pointer alignment matters?

    .

    Only the above case as you push values onto the stack - i.e. if you push a 16-bit at an even address followed by a 32-bit at an odd address.

    For an interrupt care is taken by the hardware to make sure the interrupt's stack is aligned by incrementing the SP by one and performing a ASP (align) instruction so that previous stack values are not corrupted in either case (i.e. odd alignment at the time of an interrupt or even alignment at the time of an interrupt).

    Hope this helps,

    Lori