Calling assembly functions from C

1. Example

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

Save as 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);
}
Save as 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
Also save this as foo.c to the sketch’s folder
int 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.

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