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).
Notes:
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 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 }
uart.h:
//asynchronous 8n1 serial transmission per AVR AppNote #305 void SerialPutChar (char); char SerialGetChar(void);
uart.s:
//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 UARTDelay: ldi r17, b UARTDelay1: dec r17 brne UARTDelay1 ret // //asynchronous 8n1 serial transmit byte // #define TX_PIN 6 //TX pin is PA6 .global SerialPutChar 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 putchar0: brcc putchar1 ;If carry set cbi _SFR_IO_ADDR(PORTA), TX_PIN ;send a '0' rjmp putchar2 ;else putchar1: sbi _SFR_IO_ADDR(PORTA), TX_PIN ;send a '1' nop putchar2: 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 ;else pop r17 pop r16 ret ;return // //asynchronous 8n1 serial receive byte // #define RX_PIN 5 //RX pin is PA5 .global SerialGetChar SerialGetChar: push r16 push r17 ldi r16, 9 ;8 data bits + 1 stop bit getchar1: sbic _SFR_IO_ADDR(PINA), RX_PIN ;Wait for start bit rjmp getchar1 rcall UARTDelay ;1.5bit delay after start bit edge getchar2: 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 ;else ror r24 ; shift bit into r24 (rec'd byte) rjmp getchar2 ; go get next getchar3: ldi r25, 0x00 ;zero extend r24:r25 for return pop r17 pop r16 ret
References:
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.