0
votes

I have mixed C code with assembler to understand task switching on AVR. This is only for my personal academic use and I know there is freeRTOS but I want to understand. At the moment I'm struggling on how the return address is stored on the stack.

Assume there are three functions:

extern void call(void(*)() fn);

void f1() {
 // Do something
 while(1){};
}

void f2() {
 // Do something
 while(1){};
}

int main() {
 caller(&f1);
 while(1){};
}

The Assembler file looks like this:

.section .text

.global caller
caller:
  // Register r25 and r24 contain the pointer
  pop r0
  pop r0
  push r25
  push r24
  ret

The AVR instruction set documentation states that two bytes (for PC with 16bit) are pushed on the stack when a call instruction is executed. When using avr-gcc in combination with assembler, this does not work as expected. As I understand the return address on the stack this assembler code should return to the address of f2 but this doesn't happen.

Is there anything I forgot about how gcc passes function pointers? I know this is no real world code but the intention is to help me understand about the return behaviour. The listing for calling the caller function tells me that r25 and r24 are loaded with some address.

1
> "As I understand the return address on the stack this assembler code should return to the address of f2 but this doesn't happen". Why would you think that? The return address should point to the next instruction after the call (while(1){}).jwdonahue
Look at the disassembly of main(). Step into the code with a debugger and look at the actual instruction pointer values before and after the call to caller.jwdonahue
Without modification: yes but I älteres the state of the Return addressGustavo
Post an minimal reproducible example. Nothing in your post indicates that you've modified anything on the stack.jwdonahue
Oh, I think I see it now. Is your processor working in little or big endian mode?jwdonahue

1 Answers

0
votes

Is there anything I forgot about how gcc passes function pointers?

No. It's about how you are pushing the values:

push r25
push r24
ret

You have to push the low part of the address (R24) first. Cf. for example how avr-gcc is implementing indirect jump in the case it cannot get hold of the Z register: