Calling all functions
1. avr-gcc function calls
Brief review of day10 content.
-
Open the GCC Wiki page for
avr-gcc
1.1. Register Layout
- R0
-
When the compiler wants to use a register for some temporary purpose, it first uses R0.
- R1
-
Assumed to always be zero when read.
1.1.1. Call-Saved Registers
R2 — R17, R28, R29
Put it back the way you found it after you’re finished.
-
Save the value of the affected register (to the stack:
push r5
) -
… do your thing …
-
Restore the value of the affected register (from the stack:
pop r5
) -
Then and only then can you
ret
orreti
1.1.2. Call-Used Registers
R18 — R27, R30, R31
The compiler is under no obligation whatsoever to remember what these registers held before writing to them.
Therefore an interrupt service routine (ISR) must save and restore these registers, if used. |
Also note that the pair of registers R31 : R30 is known as Z on the AVR and has sometimes special uses.
1.2. (Stack) Frame Layout

avr-gcc
decides which specific registers to use near the end of the compilation process, hence this talk about “pseudo registers” — it’s a CS thing.
Notice how R29 : R28 are assigned — i.e. don’t use these registers!
2. Example watching avr-gcc do its thing
avrgcc-functions.ino
using the Arduino IDE/*
* ECE 422
* Calling functions
*/
/*
* Global variables placed in SRAMj
*/
volatile char a, b, c, d;
volatile int out; // holds function return values
/*
* The "__attribute__ ((noinline))" business is GCC-specific syntax
* saying to NOT optimize by inlining the code at the point of use.
* We *want* to see the rcall / ret happening!
*
* https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
*/
char __attribute__ ((noinline)) fOne(char first) {
return first + 1;
}
char __attribute__ ((noinline)) fTwo(char first, char second) {
return first + second + 1;
}
char __attribute__ ((noinline)) fThree(char first, char second, char third) {
return first + second + third + 1;
}
char __attribute__ ((noinline)) fFour(char first, char second, char third, char fourth) {
return first + second + third + fourth + 1;
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
a = 2;
b = 4;
c = 16;
d = 64;
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
// call function with one 8b argument
out = fOne(a);
// call function with two 8b arguments
out = fTwo(a, b);
// call function with three 8b arguments
out = fThree(a, b, c);
// call function with four 8b arguments
out = fFour(a, b, c, d);
}
-
Verify/Compile this sketch
-
Sketch → Export compiled Binary
-
Open the
avrgcc-functions.ino.lst
file in a worth text editor and turn on syntax highlighting.
SAVE-AS so you are working on a copy of the file!
By now, you are somewhat familiar with reading assembler syntax.
The .lst
file syntax is a combination of comments, notes, information, and the assembly code that GCC compiled to from the source C/C++ code.
In the Arduino IDE, turn on Verbose output to see the commands that generate the listing file.
avr-objdump
3. Interrupt Service Routines
Introduction to avr-libc’s interrupt handling
It’s nearly impossible to find compilers that agree on how to handle interrupt code.
Even though we are presently using assembly for programming our AVR-based ATtiny85, it is useful to be aware of how a C/C++ compiler deals with interrupts.
Open up the AVR libc documentation about interrupts:
https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
4. References
GCC special attributes https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
Jaywalking Around the Compiler — Jason Sachs, EmbeddedRelated.com. Mixing C and assembly requires understanding the compiler’s assumptions it uses.