Calling assembly functions from C
2. avr-gcc function calls
Review:
2.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.
2.1.1. Call-Saved Registers
R2 — R17, R28, R29
-
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)
2.1.2. Call-Used (clobbered) 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.
3. Example watching avr-gcc do its thing
avrgcc-asm-functions.ino using the Arduino IDE/*
ECE 422
Calling assembly functions from C(++)
*/
/*
Global variables placed in SRAM
*/
volatile char a, b, c, d;
volatile char out; // holds function return values
#ifdef __cplusplus
extern "C" {
#endif
char asmOne(char);
#ifdef __cplusplus
}
#endif
/*
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);
// assembly version with one 8b argument
out = asmOne(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);
}
asmOne.S in the same folder/*
* Pre-defined symbols for our platform
*/
#include <avr/io.h>
/*
* Place this in the section for program code
*/
.section .text
/*
* make label global so that the symbol/label is visible outside of this file
*/
.global asmOne
asmOne:
subi r24, 0xff
ret
foo.c to the sketch’s folderint foo = 1;
| The upper-case ".S" extension tells GCC that this is an input file instead of generated from C/C++. |
| [ Demo ] showing errors when renaming files. |
Compile for the ATtiny85 using the ATtinyCore package.
-
Verify/Compile this sketch
-
Sketch → Export compiled Binary
-
Open the
avrgcc-functions.ino.lstfile in a worth text editor and turn on syntax highlighting.-
if needed, download avrgcc-asm-functions.ino.lst
-
SAVE-AS so you are working on a copy of the file!
Using Notepad++, clean up this file so it is easier for you to read and follow the execution path.
3.1. From source files to .hex
avr-g++ \
-c
-g \
-Os \
-w \
-std=gnu++11 \
-fpermissive -fno-exceptions -ffunction-sections -fdata-sections \
-fno-threadsafe-statics \
-flto \
-w -x c++ \
-E -CC \
-mmcu=attiny85 \
-DF_CPU=1000000L \
-DCLOCK_SOURCE=0 \
-DARDUINO=10813 \
-DARDUINO_AVR_ATTINYX5 \
-DARDUINO_ARCH_AVR \
-DDISABLEMILLIS \
-DNEOPIXELPORT=PORTB \
-I/home/dan/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/cores/tiny \
-I/home/dan/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/variants/tinyX5 \
/tmp/arduino_build_271954/sketch/avrgcc-asm-functions.ino.cpp \
-o /dev/null \
-DARDUINO_LIB_DISCOVERY_PHASE`
avr-gcc -c -g -x assembler-with-cpp -flto -mmcu=attiny85 -DF_CPU=1000000L -DCLOCK_SOURCE=0 -DARDUINO=10813 -DARDUINO_AVR_ATTINYX5 -DARDUINO_ARCH_AVR -DDISABLEMILLIS -DNEOPIXELPORT=PORTB -I/home/dan/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/cores/tiny -I/home/dan/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/variants/tinyX5 /tmp/arduino_build_271954/sketch/asmOne.S -o /tmp/arduino_build_271954/sketch/asmOne.S.o /home/dan/.arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=attiny85 -DF_CPU=1000000L -DCLOCK_SOURCE=0 -DARDUINO=10813 -DARDUINO_AVR_ATTINYX5 -DARDUINO_ARCH_AVR -DDISABLEMILLIS -DNEOPIXELPORT=PORTB -I/home/dan/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/cores/tiny -I/home/dan/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/variants/tinyX5 /tmp/arduino_build_271954/sketch/avrgcc-asm-functions.ino.cpp -o /tmp/arduino_build_271954/sketch/avrgcc-asm-functions.ino.cpp.o
In the Arduino IDE, turn on Verbose output to see the commands that generate the listing file.
avr-objdump
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.