COMP 4550 AVR-GCC Inline Assembler

Autonomous Agents Lab, University of Manitoba [email protected] http://www.cs.umanitoba.ca/~jacky http://aalab.cs.umanitoba.ca AVR-GCC Application Binary Interface (ABI)

● Data types: ○ char is 8 bits, ○ int is 16 bits, ○ long is 32 bits, ○ long long is 64 bits, ○ float and double are 32 bits (this is the only supported floating point format) ● include for uint16_t, ... ● Pointers are 16 bits ● Function pointers are word addresses, ○ to allow addressing the whole 128K program memory space on the ATmega devices with > 64 KB of flash ROM). ● -mint8 option (see Options for the avr-gcc) to make int 8 bits, not supported AVR-GCC Application Binary Interface (ABI) ● Call-saved registers (r2-r17, r28-r29): ○ May be allocated by gcc for local data. Calling C subroutines leaves them unchanged. Assembler subroutines are responsible for saving and restoring these registers, if changed. r29:r28 (Y pointer) is used as a frame pointer (points to local data on stack) if necessary. ● Call-used registers (r18-r27, r30-r31): ○ May be allocated by gcc for local data. You may use them freely in assembler subroutines. Calling C subroutines can clobber any of them - the caller is responsible for saving and restoring. AVR-GCC Application Binary Interface (ABI) ● Fixed registers (r0, r1): ● Never allocated by gcc for local data, but often used for fixed purposes: ● r0 - temporary register, can be clobbered by any C code (except interrupt handlers which save it), may be used to remember something for a while within one piece of assembler code ● r1 - assumed to be always zero in any C code, may be used to remember something for a while within one piece of assembler code, but must then be cleared after use (clr r1). This includes any use of the [f]mul[s[u]] instructions, which return their result in r1:r0. ● Interrupt handlers save and clear r1 on entry, and restore r1 on exit (in case it was non-zero). AVR-GCC Application Binary Interface (ABI) ● Function call conventions: ● Arguments - allocated left to right, r25 to r8. All arguments are aligned to start in even-numbered registers (odd-sized arguments, including char, have one free register above them). This allows making better use of the movw instruction on the enhanced core. ● If too many, those that don't fit are passed on the stack ● Return values: ● 8-bit in r24 (not r25!), 16-bit in r25:r24, up to 32 bits in r22- r25, up to 64 bits in r18-r25. 8-bit return values are zero/sign-extended to 16 bits by the caller (unsigned char is more efficient than signed char - just clr r25). Arguments to functions with variable argument lists (printf etc.) are all passed on stack, and char is extended to int. AVR-GCC asm Instruction

● asm introduces inline asm("\n\ assembler mov %0, %1 \n\ ● Assembly code is single lsr %0\n\ string constant " : "=r" (result) ● Use line continuation to : "r" (value) format ● %0,%1 correspond to ); pseudo registers, which are mapped by the compiler ● Correspond to input, output, clobber registers ● List of output operands (e.g., result) ● List of input operands (e.g., value) General Form

● General form ○ asm(code : output operand list : input operand list : clobber list); ● Input ○ registers that provide values to your code ● Output ○ registers that specify result of your expression ● Clobber ○ registers that are changed because of your code, outputs are always clobbered Volatile

● asm volatile instructs the compiler not to do any optimizations ● Must have at least two colons ● clobber list may be left off ● Special registers ● __SREG__(PORTE) - Port ● __SP_H__,__SP_L__ - Stack pointer ● __tmp_reg__ - R0 tmp variable, no need to restore ● __zero_reg__ - R1 always 0

asm volatile("cli"::); Register Constraints

● a simple upper register R16 to R23 ● b base pointer pair, y,z ● upper register R16 to R31 ● e pointer register pairs x,y,z ● G floating point constant ● I,J 6 bit positive/negative constant ● t – temporary register ● K/L/N/O/P integer constants 0/2/-1/8,16,24/1 ● w – special upper reg ● l lower register ● x/y/z – pointer x,y,z ● M 8 bit constant ● q Stack pointer register SPH:SPL ● r Any register AVR-GCC Inline Assembler

● Read/write register values ● Can specify a digit to refer to previous register ○ asm volatile("swap %0" : "=r" (value) : "0" (value)); ● Read only registers

asm volatile("in %0,%1\n\t" "out %1, %2\n\t" : "=&r" (input) : "I" (_SFR_IO_ADDR(port)), "r" (output)); Register Swap asm volatile("\t mov __tmp_reg__, %A0\n\" "\t mov %A0, %B0\n\" "\t mov %B0, __tmp_reg__\n\" ● 16 bit swap : "=r" (value) ○ %A0 upper 16 bit : "0" (value) ○ %B0 lower 16 bit ); ● 32 bit swap asm volatile("\t mov __tmp_reg__, %A0\n\" ○ %A0..%D0 "\t mov %A0, %D0\n\" ○ %A1..%D1 "\t mov %D0, __tmp_reg__\n\" "\t mov __tmp_reg__, %B0\n\" "\t mov %B0, %C0\n\" "\t mov %C0, __tmp_reg__\n\" : "=r" (value) : "0" (value) ); X,Y,Z Pointers

● Register pairs X,Y,Z are specified by the “e” constraint ● Z %A0=R30, %B0=R31 ● How to create the original register pair ○ ld r24,Z ○ ld r24, %a0 ; Note lower case “a” Clobber Registers

● Clobbers specify registers that are modified ● Atomic increment ● No input operand asm volatile( "\t cli\n\" ● Synchronization "\t ld r24, %a0\n\" ○ store value in memory "\t inc r24\n\" ● Better to use __tmp_reg__ "\t st %a0, r24\n\" ● Specify R24 as clobbered "\t sei\n" : ● Memory clobbers : "e" (ptr) ● *ptr is also changed : "r24" ● special clobber “memory” ); ○ will reload everything ● Better specify ptr as volatile Labels

● C Stub functions to hold temporary variables etc. ● L_dl1%= creates label void delay(uint8_t ms) { uint16_t cnt; ● cnt used as tmp reg asm volatile ( ● Can also label syntax "L_dl1%=:\n\" ● 1:/2: brne 2b "\t mov %A0, %A2\n\" "\t mov %B0, %B2\n\" "L_dl2%=:\n\" "\t sbiw %A0, 1\n\" "\t brne L_dl2%=\n\" "\t dec %1\n\" "\t brne L_dl1%=\n\" : "=&w" (cnt) : "r" (ms), "r" (delay_count) ); } Names

● Names used by the assembler can be defined by the C program ○ unsigned long value asm("clock") = 3686400; ● C uses value, assembler uses clock ● Specify register ○ register unsigned char counter asm("r3"); ● Functions ○ extern long Calc(void) asm ("CALCULATE");