Mixing C and Assembly in AVR GCC and AVR Studio 4

First, forget trying to learn the inline assembler syntax. I’ve read and re-read the AVR-GCC Inline Assembler Cookbook, and for the moment I can’t recall one useful detail.

From the Cookbook:

“Because of a lack of documentation, especially for the AVR version of the compiler, it may take some time to figure out the implementation details by studying the compiler and assembler source code.”

It’s far easier to incorporate an assembly file (.s) into your AVR Studio project. Simply include the file in your project dependencies on the left AVR GCC Window: Source Files (and any associated Header Files).

Adding Source File to AVRStudio4 Project


Arguments are allocated left to right, starting in register R25 through R8. All arguments are aligned to start in even-numbered registers (odd-sized arguments, including char, have one free register above them), for example a single 16-bit value is passed via the R25:R24 register pair. A 32-bit value would be passed via R25:R24:R23:R22 register combination.

Notice below in my assembly function SerialPutChar() the passed byte (a char) is accessed via the R24 register (R25 is assumed empty).

If you need to pass more than what fits inside R25 to R8, the excess is passed on the stack.

Return values are expected to be passed in a similar fashion. An 8-bit value is passed via R24, a 16-bit value in R25:R24, up to 32-bits in R22:R23:R24:R25. 8-bit return values are zero/sign-extended to 16-bits by the called function.

Notice in my example below, in SerialGetChar() I pass the received byte back via the R24 register (R25 is cleared by the instruction: ldi r25, 0x00).

The passing and returning of values is demonstrated here.

It is good practice to save and restore any values inside registers before and after using them.

Study the use of the _SFR_IO_ADDR() macro in the following example.

Here is an example of accessing a byte-sized C variable in an assembly file:

  lds    r24, hi_ticks
  subi   r24, 0xff
  sts    hi_ticks, r24

The 8-bit variable hi_ticks as defined in the C file:

//global variable
volatile uint8_t hi_ticks; //overflow counter

Use the lo8() and hi8() macros to access multi-byte values.

Remember to include the defines file <avr/io.h>.

Remember to include your function prototypes.

Remember to define your functions globally:

  .global SerialPutChar

Example C Code Calling an Assembler Function:

  char c;

  while (1) {
    SerialPutChar('?'); //anybody out there?
    SerialPutChar(10); //NL
    SerialPutChar(13); //CR
    c = SerialGetChar(); //get user input


//asynchronous 8n1 serial transmission per AVR AppNote #305
void SerialPutChar (char);
char SerialGetChar(void);


//UART Functions per AVR Application Note #305
#include <avr/io.h>
#include "uLogTimer.h"

//half bit delay
#define b 31				//38400 bps@8MHz with 0.3% error
//#define b 66				//19200 bps@8MHz with 0.6% error
		ldi  r17, b
		dec  r17
		brne UARTDelay1

//asynchronous 8n1 serial transmit byte
#define TX_PIN 6				//TX pin is PA6

.global SerialPutChar
		push r16
		push r17
		ldi  r16, 10		;1 start + 8 data + 1 stop bits (bit count)
		com  r24			;Invert it (r24 = xmit byte)
		sec				;Start bit
		brcc putchar1		;If carry set
		cbi  _SFR_IO_ADDR(PORTA), TX_PIN ;send a '0'
		rjmp putchar2		;else
		sbi  _SFR_IO_ADDR(PORTA), TX_PIN ;send a '1'
		rcall UARTDelay		;1/2 bit delay +
		rcall UARTDelay		;1/2 bit delay = 1bit delay
		lsr   r24			;Get next bit
		dec   r16			;If not all bits sent
		brne  putchar0		;send next
		pop   r17
		pop   r16
		ret				;return

//asynchronous 8n1 serial receive byte
#define RX_PIN 5				//RX pin is PA5

.global SerialGetChar
		push  r16
		push  r17
		ldi   r16, 9		;8 data bits + 1 stop bit
		sbic  _SFR_IO_ADDR(PINA), RX_PIN ;Wait for start bit
		rjmp  getchar1
		rcall UARTDelay		;1.5bit delay after start bit edge
		rcall UARTDelay		;1 bit delay from here
		rcall UARTDelay
		clc				;clear carry
		sbic  _SFR_IO_ADDR(PINA), RX_PIN ;if RX pin high
		sec				;
		dec   r16			;If bit is stop bit
		breq  getchar3		;   return
		ror   r24			;   shift bit into r24 (rec'd byte)
		rjmp  getchar2		;   go get next
		ldi   r25, 0x00		;zero extend r24:r25 for return
		pop   r17
		pop   r16


This is essential reading on GCC Compiler register usage.

Here is a link to an example program which mixes C and Assembly from the AVR-libc User Manual.

This document is a good comparison of AVR and GCC Assembly syntax.

The ATMEL AVR 8-bit Instruction Set Manual.

My example is an adaptation of the ATMEL Application Note AVR305: Half Duplex Compact Software UART.

About Jim Eli

µC experimenter
This entry was posted in Uncategorized and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s