Wello guys,
Im trying to manipulate the stack pointer and after the return of the "go_to_func" function the program will return to the start of "func" function and turn on the PORTD bit 3. I had tried the code below and it's dosen't work, the program still returns to main and turned on the PORTD bit 2. Am i missing something or i have a wrong perspective about how the stack works ?
PS: Im using the Atmel Studio 7.0 for Atmega328p ( Arduino uno )
#include <avr/io.h>
typedef unsigned char uint8;
typedef unsigned short int uint16;
/*
call go_to_func SP -> | somewhere in main |
*sp = (uint16_t)function; SP -> | start of func |
*/
void func( void )
{
/* PORTD ==> | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | */
PORTD = 0x08 ;
}
void go_to_func( void (*function)(void) )
{
uint16 *sp = (uint16*)SP ;
*sp = (uint16)function;
/* after the return i want the SP to point to the start of func() */
}
int main(void)
{
/* DDRD ==> | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | */
DDRD |= 0x0C ;
go_to_func(func);
/* PORTD ==> | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | */
PORTD = 0x04 ;
while (1)
{
}
}
Update: Hi i make an update where i made the functions noinline so in the assembly file the call apear now, but when i pass the "func" address to the "go_to_func" i expect to pass the value 90 which is the start of "func" but the program pass the value 72 ( at this line --> ldi r24, 0x48 )
#define CALL_FCT __attribute__ ((noinline))
void CALL_FCT func( void )
{
/* PORTD ==> | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | */
PORTD = 0x08 ;
90: 88 e0 ldi r24, 0x08 ; 8
92: 8b b9 out 0x0b, r24 ; 11
94: 08 95 ret
00000096 <go_to_func>:
}
void CALL_FCT go_to_func( void (*function)(void) )
{
func_addr = (uint16)function ;
96: 90 93 01 01 sts 0x0101, r25 ; 0x800101 <_edata+0x1>
9a: 80 93 00 01 sts 0x0100, r24 ; 0x800100 <_edata>
SP = (uint16)(&func_addr);
9e: 80 e0 ldi r24, 0x00 ; 0
a0: 91 e0 ldi r25, 0x01 ; 1
a2: 9e bf out 0x3e, r25 ; 62
a4: 8d bf out 0x3d, r24 ; 61
a6: 08 95 ret
000000a8 <main>:
}
int CALL_FCT main(void)
{
/* DDRD ==> | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | */
DDRD |= 0x0C ;
a8: 8a b1 in r24, 0x0a ; 10
aa: 8c 60 ori r24, 0x0C ; 12
ac: 8a b9 out 0x0a, r24 ; 10
go_to_func(func);
ae: 88 e4 ldi r24, 0x48 ; 72
b0: 90 e0 ldi r25, 0x00 ; 0
b2: 0e 94 4b 00 call 0x96 ; 0x96 <go_to_func>
/* PORTD ==> | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | */
PORTD = 0x04 ;
b6: 84 e0 ldi r24, 0x04 ; 4
b8: 8b b9 out 0x0b, r24 ; 11
ba: ff cf rjmp .-2 ; 0xba <main+0x12>
000000bc <_exit>:
bc: f8 94 cli
000000be <__stop_program>:
be: ff cf rjmp .-2 ; 0xbe <__stop_program>
My first recommendation: go to the output directory (e.g. debug) and locate there a '.lss' file, try to find there the disassembling of go_to_func
procedure, to see, what exactly happened on the processor when go_to_func
is being called.
While your idea of accessing SP pointer is correct (casting it's value to a uint16_t poitner), but your suggestion that the stack will remains containing the return address at it's top - is completely wrong.
The stack is used not only to store return addresses, when routines are called, but also to store local variables and preserve registers. So, if compiler wants to allocate some variables or preserver some registers in stack while routine is running, the SP will not be pointing to the return address anymore. Also, compiler may use the "stack frame": the initial value of SP is stored in some registers when the routine starts, and then completely restored at the exit.
UPD: Or, as in your example, the routine is inlined, there is no calls or returns are performed, and modifying SP gives you not what you're expecting.
So, it is not guaranteed how the SP pointer is used and changed while running C code. So, to perform operation you needed, you may want to rewrite the routine completely on assembler with full control of how the stack pointer is used.
By the way: if you are working on multi-tasking, you may rather want to change between different stacks, rather than use the same stack, changing the return address in it.