While trying to analyze a simple Assembly file generated through the msp430-gcc, I stumbled upon a set of instructions that I don't understand dealing with the frame pointer and the MSP430's stack pointer.
C Program:
#include "msp430g2553.h"
int main()
{
int i;
for(i = 0; i < 3; i++);
}
Assembly minus directives:
main:
mov r1, r4 ;Stores address of stack pointer in r4(frame pointer)
add #2, r4 ; ?
sub #2, r1 ; subtract 2 to allocate int i
mov #0, -4(r4) ; assign 0 to i
jmp .L2 ; start loop
.L3:
add #1, -4(r4) ; Adds one to int i
.L2:
cmp #3, -4(r4) ; Compare to #3
jl .L3 ; jump to .L3 if true
add #2, r1 ; deallocate int i
.Lfe1:
.size main,.Lfe1-main
I tried commenting the code to trace the program's execution, but I don't understand the line add #2, r4
. What exactly is happening here, and why is int i
being referenced at -4(r4)
?
2 Answers 2
It looks like the compiler has allocated space for the local variable on the stack, rather than using a spare register.
You asked specifically about these lines:
mov #0, -4(r4) ; assign 0 to i
add #1, -4(r4) ; Adds one to int i
We see an unusual disassembly of -4(r4)
as the destination for two instructions. This is called register indexed addressing and you can read more about it in the msp-gcc manual:
MSP430 addressing modes
Indexed. The operand is in memory at address
Rn+x
Therefore the destination for these instructions is located 4 bytes before the address stored in r4
. It's a bit like the equivalent in C: *(pointer - 4)
.
Although it is a simple program you might like to recompile with varying levels of optimisation. You should see GCC start to do interesting things like omitting the function prologue where it isn't required and perhaps using a register for the i
variable.
| |
|----------------|
| |
B ===> |----------------| ||
| return address | ||
A ===> |----------------| || Stack
| i | || grows
C ===> |----------------| || downward
| | ||
|----------------| \/
| |
|----------------|
| |
When main
begins executing, the hardware stack pointer, r1
, points to address "A", where the return address of the function that called main
resides.
r4
is being used as the stack frame pointer, and for whatever reason, the software designers chose to have this point to address "B", which was the top of the stack before the return address was pushed. This means that positive offsets will access data, etc., belonging to the caller, and negative offsets will access data local to main
, including the return address. It's likely that if main
were to return a value, it would be stored at address "B" (offset of 0 from r4
), where it would be the last thing on the stack after main
returns.
main
has one local variable, i
, which is allocated on the stack. Rather than pushing an initial value for i
, the software simply decrements the stack pointer by 2 to create the space for it at address "C". Now, if main
calls any other functions, the return address will be stored just below i
on the stack. However, the offset from "B" to "C" is -4, which is why the code uses that value every time it needs to access i
.
It's a little bit odd that r4
itself was not pushed onto the stack before being written to (thereby saving the previous stack frame pointer), but perhaps this is something that is done by the caller on this system.