Provided by: avr-libc_2.0.0+Atmel3.7.0-1_all bug

NAME

       inline_asm - Inline Assembler Cookbook

       AVR-GCC
        Inline Assembler Cookbook

       About this Document

       The GNU C compiler for Atmel AVR RISC processors offers, to embed assembly language code
       into C programs. This cool feature may be used for manually optimizing time critical parts
       of the software or to use specific processor instruction, which are not available in the C
       language.

       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. There are also a few sample programs available in the net.
       Hopefully this document will help to increase their number.

       It's assumed, that you are familiar with writing AVR assembler programs, because this is
       not an AVR assembler programming tutorial. It's not a C language tutorial either.

       Note that this document does not cover file written completely in assembler language,
       refer to avr-libc and assembler programs for this.

       Copyright (C) 2001-2002 by egnite Software GmbH

       Permission is granted to copy and distribute verbatim copies of this manual provided that
       the copyright notice and this permission notice are preserved on all copies. Permission is
       granted to copy and distribute modified versions of this manual provided that the entire
       resulting derived work is distributed under the terms of a permission notice identical to
       this one.

       This document describes version 3.3 of the compiler. There may be some parts, which hadn't
       been completely understood by the author himself and not all samples had been tested so
       far. Because the author is German and not familiar with the English language, there are
       definitely some typos and syntax errors in the text. As a programmer the author knows,
       that a wrong documentation sometimes might be worse than none. Anyway, he decided to offer
       his little knowledge to the public, in the hope to get enough response to improve this
       document. Feel free to contact the author via e-mail. For the latest release check
       http://www.ethernut.de/.

       Herne, 17th of May 2002 Harald Kipp harald.kipp-at-egnite.de

       Note
           As of 26th of July 2002, this document has been merged into the documentation for avr-
           libc. The latest version is now available at http://savannah.nongnu.org/projects/avr-
           libc/.

GCC asm Statement

       Let's start with a simple example of reading a value from port D:

       asm("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD)) );

       Each asm statement is devided by colons into (up to) four parts:

       1.  The assembler instructions, defined as a single string constant:

       "in %0, %1"

       2.  A list of output operands, separated by commas. Our example uses just one:

       "=r" (value)

       3.  A comma separated list of input operands. Again our example uses one operand only:

       "I" (_SFR_IO_ADDR(PORTD))

       4.  Clobbered registers, left empty in our example.

       You can write assembler instructions in much the same way as you would write assembler
       programs. However, registers and constants are used in a different way if they refer to
       expressions of your C program. The connection between registers and C operands is
       specified in the second and third part of the asm instruction, the list of input and
       output operands, respectively. The general form is

       asm(code : output operand list : input operand list [: clobber list]);

       In the code section, operands are referenced by a percent sign followed by a single digit.
       %0 refers to the first %1 to the second operand and so forth. From the above example:

       %0 refers to '=r' (value) and
        %1 refers to 'I' (_SFR_IO_ADDR(PORTD)).

       This may still look a little odd now, but the syntax of an operand list will be explained
       soon. Let us first examine the part of a compiler listing which may have been generated
       from our example:

               lds r24,value
       /* #APP */
               in r24, 12
       /* #NOAPP */
               sts value,r24

       The comments have been added by the compiler to inform the assembler that the included
       code was not generated by the compilation of C statements, but by inline assembler
       statements. The compiler selected register r24 for storage of the value read from PORTD.
       The compiler could have selected any other register, though. It may not explicitely load
       or store the value and it may even decide not to include your assembler code at all. All
       these decisions are part of the compiler's optimization strategy. For example, if you
       never use the variable value in the remaining part of the C program, the compiler will
       most likely remove your code unless you switched off optimization. To avoid this, you can
       add the volatile attribute to the asm statement:

       asm volatile("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD)));

       Alternatively, operands can be given names. The name is prepended in brackets to the
       constraints in the operand list, and references to the named operand use the bracketed
       name instead of a number after the % sign. Thus, the above example could also be written
       as

       asm("in %[retval], %[port]" :
           [retval] "=r" (value) :
           [port] "I" (_SFR_IO_ADDR(PORTD)) );

       The last part of the asm instruction, the clobber list, is mainly used to tell the
       compiler about modifications done by the assembler code. This part may be omitted, all
       other parts are required, but may be left empty. If your assembler routine won't use any
       input or output operand, two colons must still follow the assembler code string. A good
       example is a simple statement to disable interrupts:

       asm volatile("cli"::);

Assembler Code

       You can use the same assembler instruction mnemonics as you'd use with any other AVR
       assembler. And you can write as many assembler statements into one code string as you like
       and your flash memory is able to hold.

       Note
           The available assembler directives vary from one assembler to another.

       To make it more readable, you should put each statement on a seperate line:

       asm volatile("nop\n\t"
                    "nop\n\t"
                    "nop\n\t"
                    "nop\n\t"
                    ::);

       The linefeed and tab characters will make the assembler listing generated by the compiler
       more readable. It may look a bit odd for the first time, but that's the way the compiler
       creates it's own assembler code.

       You may also make use of some special registers.

       Symbol Register  __SREG__ Status register at address 0x3F  __SP_H__ Stack pointer high
       byte at address 0x3E  __SP_L__ Stack pointer low byte at address 0x3D  __tmp_reg__
       Register r0, used for temporary storage  __zero_reg__ Register r1, always zero

       Register r0 may be freely used by your assembler code and need not be restored at the end
       of your code. It's a good idea to use __tmp_reg__ and __zero_reg__ instead of r0 or r1,
       just in case a new compiler version changes the register usage definitions.

Input and Output Operands

       Each input and output operand is described by a constraint string followed by a C
       expression in parantheses. AVR-GCC 3.3 knows the following constraint characters:

       Note
           The most up-to-date and detailed information on contraints for the avr can be found in
           the gcc manual.

           The x register is r27:r26, the y register is r29:r28, and the z register is r31:r30

       ConstraintUsed forRange aSimple upper registersr16 to r23 bBase pointer registers pairsy,
       z dUpper registerr16 to r31 ePointer register pairsx, y, z qStack pointer registerSPH:SPL
       rAny registerr0 to r31 tTemporary registerr0 wSpecial upper register pairsr24, r26, r28,
       r30 xPointer register pair Xx (r27:r26) yPointer register pair Yy (r29:r28) zPointer
       register pair Zz (r31:r30) GFloating point constant0.0 I6-bit positive integer constant0
       to 63 J6-bit negative integer constant-63 to 0 KInteger constant2 LInteger constant0
       lLower registersr0 to r15 M8-bit integer constant0 to 255 NInteger constant-1 OInteger
       constant8, 16, 24 PInteger constant1 Q(GCC >= 4.2.x) A memory address based on Y or Z
       pointer with displacement.  R(GCC >= 4.3.x) Integer constant.-6 to 5

       The selection of the proper contraint depends on the range of the constants or registers,
       which must be acceptable to the AVR instruction they are used with. The C compiler doesn't
       check any line of your assembler code. But it is able to check the constraint against your
       C expression. However, if you specify the wrong constraints, then the compiler may
       silently pass wrong code to the assembler. And, of course, the assembler will fail with
       some cryptic output or internal errors. For example, if you specify the constraint 'r' and
       you are using this register with an 'ori' instruction in your assembler code, then the
       compiler may select any register. This will fail, if the compiler chooses r2 to r15. (It
       will never choose r0 or r1, because these are uses for special purposes.) That's why the
       correct constraint in that case is 'd'. On the other hand, if you use the constraint 'M',
       the compiler will make sure that you don't pass anything else but an 8-bit value. Later on
       we will see how to pass multibyte expression results to the assembler code.

       The following table shows all AVR assembler mnemonics which require operands, and the
       related contraints. Because of the improper constraint definitions in version 3.3, they
       aren't strict enough. There is, for example, no constraint, which restricts integer
       constants to the range 0 to 7 for bit set and bit clear operations.

       Mnemonic Constraints Mnemonic Constraints  adc r,r add r,r  adiw w,I and r,r  andi d,M asr
       r  bclr I bld r,I  brbc I,label brbs I,label  bset I bst r,I  cbi I,I cbr d,I  com r cp
       r,r  cpc r,r cpi d,M  cpse r,r dec r  elpm t,z eor r,r  in r,I inc r  ld r,e ldd r,b  ldi
       d,M lds r,label  lpm t,z lsl r  lsr r mov r,r  movw r,r mul r,r  neg r or r,r  ori d,M out
       I,r  pop r push r  rol r ror r  sbc r,r sbci d,M  sbi I,I sbic I,I  sbiw w,I sbr d,M  sbrc
       r,I sbrs r,I  ser d st e,r  std b,r sts label,r  sub r,r subi d,M  swap r

       Constraint characters may be prepended by a single constraint modifier. Contraints without
       a modifier specify read-only operands. Modifiers are:

       Modifier Specifies  = Write-only operand, usually used for all output operands.  + Read-
       write operand  & Register should be used for output only

       Output operands must be write-only and the C expression result must be an lvalue, which
       means that the operands must be valid on the left side of assignments. Note, that the
       compiler will not check if the operands are of reasonable type for the kind of operation
       used in the assembler instructions.

       Input operands are, you guessed it, read-only. But what if you need the same operand for
       input and output? As stated above, read-write operands are not supported in inline
       assembler code. But there is another solution. For input operators it is possible to use a
       single digit in the constraint string. Using digit n tells the compiler to use the same
       register as for the n-th operand, starting with zero. Here is an example:

       asm volatile("swap %0" : "=r" (value) : "0" (value));

       This statement will swap the nibbles of an 8-bit variable named value. Constraint '0'
       tells the compiler, to use the same input register as for the first operand. Note however,
       that this doesn't automatically imply the reverse case. The compiler may choose the same
       registers for input and output, even if not told to do so. This is not a problem in most
       cases, but may be fatal if the output operator is modified by the assembler code before
       the input operator is used. In the situation where your code depends on different
       registers used for input and output operands, you must add the & constraint modifier to
       your output operand. The following example demonstrates this problem:

       asm volatile("in %0,%1"    "\n\t"
                    "out %1, %2"  "\n\t"
                    : "=&r" (input)
                    : "I" (_SFR_IO_ADDR(port)), "r" (output)
                   );

       In this example an input value is read from a port and then an output value is written to
       the same port. If the compiler would have choosen the same register for input and output,
       then the output value would have been destroyed on the first assembler instruction.
       Fortunately, this example uses the & constraint modifier to instruct the compiler not to
       select any register for the output value, which is used for any of the input operands.
       Back to swapping. Here is the code to swap high and low byte of a 16-bit value:

       asm volatile("mov __tmp_reg__, %A0" "\n\t"
                    "mov %A0, %B0"         "\n\t"
                    "mov %B0, __tmp_reg__" "\n\t"
                    : "=r" (value)
                    : "0" (value)
                   );

       First you will notice the usage of register __tmp_reg__, which we listed among other
       special registers in the Assembler Code section. You can use this register without saving
       its contents. Completely new are those letters A and B in %A0 and %B0. In fact they refer
       to two different 8-bit registers, both containing a part of value.

       Another example to swap bytes of a 32-bit value:

       asm volatile("mov __tmp_reg__, %A0" "\n\t"
                    "mov %A0, %D0"         "\n\t"
                    "mov %D0, __tmp_reg__" "\n\t"
                    "mov __tmp_reg__, %B0" "\n\t"
                    "mov %B0, %C0"         "\n\t"
                    "mov %C0, __tmp_reg__" "\n\t"
                    : "=r" (value)
                    : "0" (value)
                   );

       Instead of listing the same operand as both, input and output operand, it can also be
       declared as a read-write operand. This must be applied to an output operand, and the
       respective input operand list remains empty:

       asm volatile("mov __tmp_reg__, %A0" "\n\t"
                    "mov %A0, %D0"         "\n\t"
                    "mov %D0, __tmp_reg__" "\n\t"
                    "mov __tmp_reg__, %B0" "\n\t"
                    "mov %B0, %C0"         "\n\t"
                    "mov %C0, __tmp_reg__" "\n\t"
                    : "+r" (value));

       If operands do not fit into a single register, the compiler will automatically assign
       enough registers to hold the entire operand. In the assembler code you use %A0 to refer to
       the lowest byte of the first operand, %A1 to the lowest byte of the second operand and so
       on. The next byte of the first operand will be %B0, the next byte %C0 and so on.

       This also implies, that it is often neccessary to cast the type of an input operand to the
       desired size.

       A final problem may arise while using pointer register pairs. If you define an input
       operand

       "e" (ptr)

       and the compiler selects register Z (r30:r31), then

       %A0 refers to r30 and
        %B0 refers to r31.

       But both versions will fail during the assembly stage of the compiler, if you explicitely
       need Z, like in

       ld r24,Z

       If you write

       ld r24, %a0

       with a lower case a following the percent sign, then the compiler will create the proper
       assembler line.

Clobbers

       As stated previously, the last part of the asm statement, the list of clobbers, may be
       omitted, including the colon seperator. However, if you are using registers, which had not
       been passed as operands, you need to inform the compiler about this. The following example
       will do an atomic increment. It increments an 8-bit value pointed to by a pointer variable
       in one go, without being interrupted by an interrupt routine or another thread in a
       multithreaded environment. Note, that we must use a pointer, because the incremented value
       needs to be stored before interrupts are enabled.

       asm volatile(
           "cli"               "\n\t"
           "ld r24, %a0"       "\n\t"
           "inc r24"           "\n\t"
           "st %a0, r24"       "\n\t"
           "sei"               "\n\t"
           :
           : "e" (ptr)
           : "r24"
       );

       The compiler might produce the following code:

       cli
       ld r24, Z
       inc r24
       st Z, r24
       sei

       One easy solution to avoid clobbering register r24 is, to make use of the special
       temporary register __tmp_reg__ defined by the compiler.

       asm volatile(
           "cli"                       "\n\t"
           "ld __tmp_reg__, %a0"       "\n\t"
           "inc __tmp_reg__"           "\n\t"
           "st %a0, __tmp_reg__"       "\n\t"
           "sei"                       "\n\t"
           :
           : "e" (ptr)
       );

       The compiler is prepared to reload this register next time it uses it. Another problem
       with the above code is, that it should not be called in code sections, where interrupts
       are disabled and should be kept disabled, because it will enable interrupts at the end. We
       may store the current status, but then we need another register. Again we can solve this
       without clobbering a fixed, but let the compiler select it. This could be done with the
       help of a local C variable.

       {
           uint8_t s;
           asm volatile(
               "in %0, __SREG__"           "\n\t"
               "cli"                       "\n\t"
               "ld __tmp_reg__, %a1"       "\n\t"
               "inc __tmp_reg__"           "\n\t"
               "st %a1, __tmp_reg__"       "\n\t"
               "out __SREG__, %0"          "\n\t"
               : "=&r" (s)
               : "e" (ptr)
           );
       }

       Now every thing seems correct, but it isn't really. The assembler code modifies the
       variable, that ptr points to. The compiler will not recognize this and may keep its value
       in any of the other registers. Not only does the compiler work with the wrong value, but
       the assembler code does too. The C program may have modified the value too, but the
       compiler didn't update the memory location for optimization reasons. The worst thing you
       can do in this case is:

       {
           uint8_t s;
           asm volatile(
               "in %0, __SREG__"           "\n\t"
               "cli"                       "\n\t"
               "ld __tmp_reg__, %a1"       "\n\t"
               "inc __tmp_reg__"           "\n\t"
               "st %a1, __tmp_reg__"       "\n\t"
               "out __SREG__, %0"          "\n\t"
               : "=&r" (s)
               : "e" (ptr)
               : "memory"
           );
       }

       The special clobber 'memory' informs the compiler that the assembler code may modify any
       memory location. It forces the compiler to update all variables for which the contents are
       currently held in a register before executing the assembler code. And of course,
       everything has to be reloaded again after this code.

       In most situations, a much better solution would be to declare the pointer destination
       itself volatile:

       volatile uint8_t *ptr;

       This way, the compiler expects the value pointed to by ptr to be changed and will load it
       whenever used and store it whenever modified.

       Situations in which you need clobbers are very rare. In most cases there will be better
       ways. Clobbered registers will force the compiler to store their values before and reload
       them after your assembler code. Avoiding clobbers gives the compiler more freedom while
       optimizing your code.

Assembler Macros

       In order to reuse your assembler language parts, it is useful to define them as macros and
       put them into include files. AVR Libc comes with a bunch of them, which could be found in
       the directory avr/include. Using such include files may produce compiler warnings, if they
       are used in modules, which are compiled in strict ANSI mode. To avoid that, you can write
       __asm__ instead of asm and __volatile__ instead of volatile. These are equivalent aliases.

       Another problem with reused macros arises if you are using labels. In such cases you may
       make use of the special pattern %=, which is replaced by a unique number on each asm
       statement. The following code had been taken from avr/include/iomacros.h:

       #define loop_until_bit_is_clear(port,bit)  \
               __asm__ __volatile__ (             \
               'L_%=: ' 'sbic %0, %1' '\n\t'      \
                        'rjmp L_%='               \
                        : /* no outputs */
                        : 'I' (_SFR_IO_ADDR(port)),
                          'I' (bit)
               )

       When used for the first time, L_%= may be translated to L_1404, the next usage might
       create L_1405 or whatever. In any case, the labels became unique too.

       Another option is to use Unix-assembler style numeric labels. They are explained in How do
       I trace an assembler file in avr-gdb?. The above example would then look like:

       #define loop_until_bit_is_clear(port,bit)
               __asm__ __volatile__ (
               '1: ' 'sbic %0, %1' '\n\t'
                        'rjmp 1b'
                        : /* no outputs */
                        : 'I' (_SFR_IO_ADDR(port)),
                          'I' (bit)
               )

C Stub Functions

       Macro definitions will include the same assembler code whenever they are referenced. This
       may not be acceptable for larger routines. In this case you may define a C stub function,
       containing nothing other than your assembler code.

       void delay(uint8_t ms)
       {
           uint16_t cnt;
           asm volatile (
               "\n"
               "L_dl1%=:" "\n\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\t"
               : "=&w" (cnt)
               : "r" (ms), "r" (delay_count)
               );
       }

       The purpose of this function is to delay the program execution by a specified number of
       milliseconds using a counting loop. The global 16 bit variable delay_count must contain
       the CPU clock frequency in Hertz divided by 4000 and must have been set before calling
       this routine for the first time. As described in the clobber section, the routine uses a
       local variable to hold a temporary value.

       Another use for a local variable is a return value. The following function returns a 16
       bit value read from two successive port addresses.

       uint16_t inw(uint8_t port)
       {
           uint16_t result;
           asm volatile (
               "in %A0,%1" "\n\t"
               "in %B0,(%1) + 1"
               : "=r" (result)
               : "I" (_SFR_IO_ADDR(port))
               );
           return result;
       }

       Note
           inw() is supplied by avr-libc.

C Names Used in Assembler Code

       By default AVR-GCC uses the same symbolic names of functions or variables in C and
       assembler code. You can specify a different name for the assembler code by using a special
       form of the asm statement:

       unsigned long value asm("clock") = 3686400;

       This statement instructs the compiler to use the symbol name clock rather than value. This
       makes sense only for external or static variables, because local variables do not have
       symbolic names in the assembler code. However, local variables may be held in registers.

       With AVR-GCC you can specify the use of a specific register:

       void Count(void)
       {
           register unsigned char counter asm("r3");

           ... some code...
           asm volatile("clr r3");
           ... more code...
       }

       The assembler instruction, 'clr r3', will clear the variable counter. AVR-GCC will not
       completely reserve the specified register. If the optimizer recognizes that the variable
       will not be referenced any longer, the register may be re-used. But the compiler is not
       able to check wether this register usage conflicts with any predefined register. If you
       reserve too many registers in this way, the compiler may even run out of registers during
       code generation.

       In order to change the name of a function, you need a prototype declaration, because the
       compiler will not accept the asm keyword in the function definition:

       extern long Calc(void) asm ("CALCULATE");

       Calling the function Calc() will create assembler instructions to call the function
       CALCULATE.

Links

       For a more thorough discussion of inline assembly usage, see the gcc user manual. The
       latest version of the gcc manual is always available here: http://gcc.gnu.org/onlinedocs/