Title: AVR Stack and Subroutine Calls
1AVR Stack and Subroutine Calls
- Assembly Language Programming
- University of Akron
- Dr. Tim Margush
2Stack
- The stack is an area of SRAM that grows downward
from a fixed reference point - Special instructions and I/O register support
stack access - RET, CALL, PUSH, POP,
- SP (Stack Pointer)
- The stack requires initialization
- Set SP to an appropriate address (empty stack)
- The stack is managed by application and is used
by the processor interrupt system
3Stack Initialization
- Typically, the stack starts at the highest SRAM
address and grows downward through memory - SP points to the next available byte of stack
storage (top of stack after a push) - ldi R16, high(RAMEND)
- out SPH, R16
- ldi R16, low(RAMEND)
- out SPL, R16
4PUSH and POP
- push Rr
- Register data copied to byte at address in SP
- SP is decremented
- pop Rd
- SP is incremented
- Byte at address in SP is copied to Rd
5PUSH
push R5
A2
SP45D
Top of stack
6PUSH
push R5
SP--
SP45D
SP45C
Top of stack
7POP
pop R4
SP
SP45C
SP45D
Top of stack
8POP
pop R4
SP45D
A2
Top of stack
9Stack Usage
- Temporary storage of information
- Storage of return addresses for procedure calls
and interrupts - Storage for local variables during execution of a
procedure
10Temporary Storage
- push R16 save register
- ldi R16, FF use register
- out PORTB, R16
- pop R16 restore register
11Procedure Call
- Syntax rcall label (relative call)
- This instruction pushes a return address onto the
stack, then executes a jump to the indicated
address - The return address is pushed low byte first
- The procedure should use RET to return to the
statement following the call
12Return
- Syntax ret
- This pops bytes from the stack into PC
effectively resuming execution at the statement
following the call that pushed the bytes onto the
stack
13PC-Relative Addressing
- The rcall instruction (along with some others)
uses PC-relative addressing mode - rjmp k (-2048 lt k lt 2047)
- This range indicates a 12-bit two's complement
code is used for k which is encoded in the
instruction - Opcode 1101 kkkk kkkk kkkk
- The assembler calculates k from the destination
address using the current PC value as a reference - rjmp destlabel
- k destlabel PC 1
14PC-Relative Example
- 03C rcall sub
- 03D ldi r16, 0
-
- 06F sub out A, r16
- 067
- 08E ret
- Displacement is calculated by assembler
- PC is 3C
- k 6F-3C-1
- So k 32
- 1101 0000 0011 0010
15PC-Relative Example
- 024 adder adi r16, FF
- 025 adc r10
-
- 02F ret
-
- 0AF push r10
- 0B0 rcall adder
- 0B1 pop r10
- Displacement is calculated by assembler
- PC is B0
- k 24-B0-1
- So k F73 (-141)
- 1101 1111 0111 0011
16Why PC-Relative?
Saves space, executes faster
- The AVR uses PC-relative because the displacement
fits in a 16-bit instruction - If direct addressing is used, a second word would
need to hold the address - The AVR call instruction uses direct addressing
- call label (long call)
- 1001 010k kkkk 111k kkkk kkkk kkkk kkkk
- Supports 22-bit addresses (8MB memory)
- The ATMega16 has only 16KB, max address is 1FFF
(13 bits), and PC-Relative addressing can access
½ of its program space
17Why PC-Relative?
- Many operating systems allow program relocation
- The program may be placed in a different memory
area each time it is executed - This creates problems when programs use direct
addressing - The loader will need to adjust addresses in the
program at load time - PC-relative addresses however, do not have to be
modified
18Indirect Call
- The AVR processor also supports a call
instruction that uses indirect addressing - Syntax icall (indirect call)
- The address of the procedure must be preset in Z
- This can only access procedures in the first
128KB of program memory - Not a problem for the ATMega16!
19Warning
- add word procedure
- addit
- add r17, r15
- adc r16, r14
- subtract word procedure
- subbit
- sub r17, r15
- sbc r16, r14
- ret
- Do not forget the RET
- If you do forget, the processor will likely fall
through into another part of your program (or
even into a data area of the code segment) with
potentially disastrous results!
Missing RET?
20Procedures
- Use procedures to organize code into logical
groups of statements - Abstraction a good design technique
- Use procedures to reduce duplicate code
- Call the procedure instead of duplicating the
same sequence of steps - Use procedures to create reusable code
- Place useful procedures in include files for easy
reuse in other programs
21Arguments and Parameters
- Assembly language usually does not support
arguments and parameters as part of the procedure
call-return mechanism - The programmer is left to decide how
communication between procedures will be
implemented - Many techniques are possible
22Call By ?
- Call by value
- The caller copies the value of the argument into
storage made available to the function
(parameter) - Function has no access to original argument
- Call by reference
- The caller copies the address of the argument
into storage made available to the function
(parameter) - Function can directly modify the argument via the
pointer
23Call By ?
argument
.dsegb .byte 1
Call by reference
Call by value
- //f1(b)
- lds r16, b
- call f1
- //void f1(int a)
- f1
- out PORTB, r16
- //f2(b)
- ldi XH, high(b)
- ldi XL, low(b)
- call f2
- //void f2(int a)
- ld R23, X
- inc R23
- st X, R23
Address of argument
argument copied into register
Indirect addressing provides R/W access to
argument
function accesses parameter
24Register Parameters
- The procedure assumes specific data will already
be in certain registers when it is called - The caller is responsible for setting up the
registers with the arguments - The procedure may use parameter names to refer to
the registers
25procedure ledOut(a)
- ledOut
- a (R16) is byte to be displayed
- on LEDs (PORTB)
- .def aR16
- com a
- out PORTB, a
- com a
- ret
- .undef a
The argument will be loaded into R16 before the
procedure is called ledOut uses the parameter
name a to refer to the argument The parameter is
undefined at the end of the procedure so it is
not inadvertently used elsewhere in the program
(scopes a to this procedure)
26procedure ledOut(a)
- ledOut
- a (R16) is byte to be displayed
- on LEDs (PORTB)
- .def aR16
- com a
- out PORTB, a
- com a
- ret
- .undef a
Illustrate passing an argument to a procedure
using a register high-level call ledOut
(AA) ldi R16, AA setup argument rcall ledOut
27Ad/Disad - vantages
- Arguments will be in registers for immediate
access by procedure - Simple, fast, and effective, especially for
byte-sized arguments
- The specific register used for the parameter may
be in use by the caller, requiring data shuffling - Too many parameters will tie up too many
registers - Argument may not fit in a register
28Alternatives?
- Put the arguments in RAM where?
- On the stack
- At an agreed upon address
- At an address specified in a register
- Just use global variables
- In some cases, the arguments are placed inline
with the program - Perhaps immediately after the call statement?
- This technique requires some return address
adjustment to prevent a disaster
29Stack Parameters
- The caller pushes arguments before the call
outChar('a',2) ldi R16, 2 push R16 ldi R16,
'a' push R16 rcall outChar
02
SP45E
Arguments must be pushed on stack before call
Top before call
30Stack Parameters
- The caller pushes arguments before the call
outChar('a',2) ldi R16, 2 push R16 ldi R16,
'a' push R16 rcall outChar
SP45D
61
Argument 1 now on top
Push argument 2
31Stack Parameters
- The caller pushes arguments before the call
outChar('a',2) ldi R16, 2 push R16 ldi R16,
'a' push R16 rcall outChar
SP45C
Arguments now on stack
063C
Execute rcall
32Stack Parameters
- The return address is on the top of stack when
the procedure begins
outChar('a',2) ldi R16, 2 push R16 ldi R16,
'a' push R16 rcall outChar
SP45A
Return address now on top of the stack Procedure
starts execution
outChar(char x, int n) outChar
33Stack Parameters
- The stack pointer (SP) is a reference point to
access the arguments
outChar(char x, int n) outChar pointer to
stack frame in YH, SPH in YL, SPL access
arguments ldd R0, Y4 ldd R1, Y3
SP45A
3
4
x is at SP3 n is at SP4
34Stack Parameters
- After return, the caller needs to remove the
parameters from the stack
outChar('a',2) rcall outChar remove
parameters pop r16 pop r16
outChar ret
35Auto Parameter Removal
- What we just did
- The caller allocates parameters on the stack
- The caller removes parameters from the stack
- What we can do
- Let the function remove the parameters before
returning - This eliminates duplicate removal code if there
are calls at many locations
36Stack Cleanup Code
Y45A
- Copy return address into Z
- Add stack frame size to Y
- Copy Y to SP
- Use IJMP to cause return
- ldd ZH,Y1
- ldd ZL,Y2
- adiw Y,4
- out SPH,YH
- out SPL,YL
- ijmp
Z063C
37Inline Parameters
- plus(2987,9873,sum)
- rcall plus
- parameter frame
- .dw 2987
- .dw 9873
- .dw sum address
- we need to return here!
- nop or other instruction
- In this call, the parameters are stored inline
with the program, immediately after the call - If this was read/write storage, the code before
the call could store different info into this
memory allowing the arguments to change on each
call
38Inline Parameters
- When the function starts, the return address is
on the top of the stack. This is the address of
the parameters! - Establish pointer to parameters
- Convert to byte addresses
- void plus(int, int, int)
- plus
- pop ZH pop return addr
- pop ZL
- lsl ZL convert to byte
- rol ZH address
- Z now points to params in program memory
rcall
2987
9873
60
nop
39Inline Parameters
- Z is used to copy parameters into registers
- The addition is completed in R25R24 using R26 as
a temporary register
- load params to registers
- lpm R24, Z
- lpm R25, Z
- lpm R26, Zadd R24, R26
- lpm R26, Z
- adc R25, R26 sum done
- Z now points to address of sum
rcall
2987
9873
60
nop
40Inline Parameters
- The last parameter (address where result will be
stored) is brought into X - Indirect addressing allows the result to be
stored in sram. - Z now points to the return location!
- store result
- lpm XL, Z
- lpm XH, Z
- st X, R24
- st X, R25task complete
- Z now points to return loc
rcall
2987
9873
60
nop
41Inline Parameters
- The indirect jump works well to complete the
return. - The return address must be converted back to a
word address before using ijmp - Work registers could have been saved and restored
easily inside the function
- return using ijmp
- lsr ZH word address
- ror ZL
- ijmp
- Execution resumes at nop
rcall
2987
9873
60
nop
42Procedure Etiquette
- Procedures should not change registers unless it
is an intended effect of the procedure - The stack is a convenient temporary storage area
for registers needed by a procedure to carry out
its task - Procedures usually start with a bunch of pushes
(to save registers) and end with corresponding
pops (to restore registers)
43Complications
- The use of the stack for arguments, parameters,
the return address, temporary storage, and (not
yet mentioned) local variables, really gets
complicated - For greatest flexibility, a second stack may be
created in RAM to support procedure data needs
44Data Stack
- Uses Y as stack pointer
- Y points to top item (if present)
- Uses indirect addressing with pre-decrement for
push and post-increment for pop - Occupies another part of SRAM
- Used for arguments and local storage
45Data Stack Initialization
.equ HSTACK_SIZE 64 ldi YH, high(RAMEND-HSTACK_S
IZE1) ldi YL, low(RAMEND-HSTACK_SIZE1)
- Data stack willlive just below the hardware
stack - Set Y to point to the last byte of the hardware
stack - A constant should be used to set the intended
size of the hardware stack - Y will be decremented before storing the first
item into the stack
46Data Stack Push
- Push requires decrementing Y and storing a
register via indirect addressing - Think dpush Rn
- Write st Y, Rn
- Later we will learn how to create a macro to do
this
47Data Stack Pop
- Copy the top of stack value to a register and
increment Y - Think dpop Rd
- Write ld Rd, Y
- The built-in pre-decrement and post-increment
work well when Y points to the top item, unlike
the hardware stack which uses post-decrement and
pre-increment (not available to the user) of SP
(SP points to the byte below the top item)
48Macro
- A Macro is an assembly feature that allows the
programmer to define new instructions - A macro invocation stands for a sequence of
assembly language instructions - We want a new dpush and dpop instruction
.macro dpush st Y, _at_0 .endm
Macros have names and may use parameters (_at_0, _at_1,
etc)
49Using a Macro
- Invocation dpush R7
- _at_0 is R7
- Expansion is simply
- st Y, R7
- The argument is substituted for the formal
parameter - Macro definitions must appear before they are
used - An include file is a good choice for macros
50Local Storage
- Additional space in the data stack is reserved
for local variables by subtracting the space
required from Y - If a function has a byte and a word of local
storage, we need 3 bytes - sbiw YHYL, 3
- Y now points at the last of these bytes, Each may
be accessed using indirect addressing with
displacement - ldd R4, Y1 the second byte of the 3 byte area
51Function Call
- Caller push arguments onto data stack
- Remember how many bytes this is for later (j)
- Function is called
- Return address placed on hardware stack
- Function allocates local storage
- Subtract k from Y (k bytes needed)
- Function executes
- Uses hardware stack to save and restore registers
- Function adds kj to Y
- The number j is the size of the argument list
- Function returns to caller
- Removes return address from hardware stack
52Example Fibonacci
- The Fibonacci numbers are fun to compute
- fibo(0) fibo(1) 1
- For ngt1 fibo(n) fibo(n-1) fibo(n-2)
- We will write a function that illustrates the use
of the data stack for arguments and local storage - Strictly speaking, we could do all this with just
registers don't laugh at the inefficiency of the
example
53Function Fibo
- int fibo(char n)
- if (nlt1) return 1
- int a fibo(n-1)
- int b fibo(n-2)
- return ab
-
- sample call
- int f fibo(5)
- The stack frame will include a byte for n, and
two words for a and b - Total of 5 bytes
- The byte for n is allocated when the caller
pushes the argument on the stack
54Calling Sequence
- .dseg
- f .byte 2 storage for f
- .cseg
- ldi r16, 5 literal argument
- dpush r16
- rcall fibo return value in R25R24
- sts f1, r25 high byte of result
- sts f, r24 low byte of result
55Function Fibo
- fibo
- sbiw YHYL,4 local storage
- define offsets to
- locals and
- name registers used
- .equ fibo_a0
- .equ fibo_b2
- .equ fibo_n4
- .def retH r25
- .def retL r24
- .def temp r16
- push temp
- if (nlt2)
- ldd temp,Yfibo_n
- cpi temp, 2
- brlo fibo_return_1
- int a fibo(n-1)
- dec temp
- dpush temp
- rcall fibo
- std Yfibo_a, retL
- std Yfibo_a1, retH
56Function Fibo
- int b fibo(n-2)
- dec temp
- dpush temp
- rcall fibo
- std Yfibo_b, retL
- std Yfibo_b1, retH
- return ab
- ldd retL, Yfibo_a
- ldd retH, Yfibo_a1
- ldd temp, Yfibo_b
- add retL, temp
- ldd temp, Yfibo_b1
- adc retH, temp
- rjmp fibo_exit
- fibo_return_1
- ldi retL, 1
- ldi retH, 0
57Function Fibo
- fibo_exit
- pop temp
- adiw YHYL,41
- ret
- .undef retH
- .undef retL
- .undef temp
- The exit section restores temporary registers
from hardware stack then releases local storage
and the argument space from the data stack before
returning