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.lst
file 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.