plucky (3) assembler.3avr.gz

Provided by: avr-libc_2.2.1-1_all bug

NAME

       assembler - AVR-LibC and Assembler Programs

       • IntroductionInvoking the CompilerExample ProgramAssembler DirectivesSectionsSymbolsData and AlignmentOperand Modifiers

Introduction

       There might be several reasons to write code for AVR microcontrollers using plain assembler source code.
       Among them are:

       • Code for devices that do not have RAM and are thus not supported by the C compiler.

       • Code for very time-critical applications.

       • Special tweaks that cannot be done in C.

       Usually, all but the first could probably be done easily using the inline assembler facility of the
       compiler.

       Although AVR-LibC is primarily targeted to support programming AVR microcontrollers using the C (and C++)
       language, there's limited support for direct assembler usage as well. The benefits of it are:

       • Use of the C preprocessor and thus the ability to use the same symbolic constants that are available to
         C programs, as well as a flexible macro concept that can use any valid C identifier as a macro (whereas
         the assembler's macro concept is basically targeted to use a macro in place of an assembler
         instruction).

       • Use of the runtime framework like automatically assigning interrupt vectors. For devices that have RAM,
         initializing the RAM variables can also be utilized.

Invoking the Compiler

       For the purpose described in this document, the assembler and linker are usually not invoked manually,
       but rather using the C compiler frontend (avr-gcc) that in turn will call the assembler and linker as
       required.

       This approach has the following advantages:

       • There is basically only one program to be called directly, avr-gcc, regardless of the actual source
         language used.

       • The invokation of the C preprocessor will be automatic, and will include the appropriate options to
         locate required include files in the filesystem.

       • The invokation of the linker will be automatic, and will include the appropriate options to locate
         additional libraries as well as the application start-up code (crtXXX.o) and linker script.

       Note that the invokation of the C preprocessor will be automatic when the filename provided for the
       assembler file ends in .S (the capital letter 's'). This would even apply to operating systems that use
       case-insensitive filesystems since the actual decision is made based on the case of the filename suffix
       given on the command-line, not based on the actual filename from the file system.

       As an alternative to using .S, the suffix .sx is recognized for this purpose (starting with GCC v4.3).
       This is primarily meant to be compatible with other compiler environments that have been providing this
       variant before in order to cope with operating systems where filenames are case-insensitive (and, with
       some versions of make that could not distinguish between .s and .S on such systems).

       Alternatively, the language can explicitly be specified using the -x assembler-with-cpp option.

Example Program

       The following annotated example features a simple 100 kHz square wave generator using an AT90S1200
       clocked with a 10.7 MHz crystal. Pin PD6 will be used for the square wave output.

       #include <avr/io.h>           // Note [1]

       work    =   16                // Note [2]
       tmp     =   17

       inttmp  =   19
       intsav  =   0

       SQUARE  =   PD6               // Note [3]

       #define IO(x) _SFR_IO_ADDR(x)

       // Note [4]:
       // 100 kHz => 200000 edges/s
       tmconst = 10700000 / 200000

       // # clocks in ISR until TCNT0 is set
       fuzz = 8

       .text

       .global main                        // Note [5]
       main:
           rcall   ioinit
       1:  rjmp    1b                      // Note [6]

       .global TIMER0_OVF_vect             // Note [7]
       TIMER0_OVF_vect:
           ldi     inttmp, 256 - tmconst + fuzz
           out     IO(TCNT0), inttmp       // Note [8]

           in      intsav, IO(SREG)        // Note [9]

           sbic    IO(PORTD), SQUARE
           rjmp    1f
           sbi     IO(PORTD), SQUARE
           rjmp    2f
       1:  cbi     IO(PORTD), SQUARE
       2:
           out     IO(SREG), intsav
           reti

       ioinit:
           sbi     IO(DDRD), SQUARE

           ldi     work, _BV(TOIE0)
           out     IO(TIMSK), work

           ldi     work, _BV(CS00)         // tmr0:  CK/1
           out     IO(TCCR0), work

           ldi     work, 256 - tmconst
           out     IO(TCNT0), work

           sei

           ret

       .global __vector_default            // Note [10]
       __vector_default:
           reti

       Note [1]
           As in C programs, this includes the central processor-specific file containing the IO port
           definitions for the device. Note that not all include files can be included into assembler sources.

       Note [2]
           Assignment of registers to symbolic names used locally. Another option would be to use a C
           preprocessor macro instead:

       #define work 16

       Note [3]
           Our bit number for the square wave output. Note that the right-hand side consists of a CPP macro
           which will be substituted by its value (6 in this case) before actually being passed to the
           assembler.

       Note [4]
           The assembler uses integer operations in the host-defined integer size (32 bits or longer) when
           evaluating expressions. This is in contrast to the C compiler that uses the C type int by default in
           order to calculate constant integer expressions.

       In order to get a 100 kHz output, we need to toggle the PD6 line 200000 times per second. Since we use
       timer 0 without any prescaling options in order to get the desired frequency and accuracy, we already run
       into serious timing considerations: while accepting and processing the timer overflow interrupt, the
       timer already continues to count. When pre-loading the TCCNT0 register, we therefore have to account for
       the number of clock cycles required for interrupt acknowledge and for the instructions to reload TCCNT0
       (4 clock cycles for interrupt acknowledge, 2 cycles for the jump from the interrupt vector, 2 cycles for
       the 2 instructions that reload TCCNT0). This is what the constant fuzz is for.

       Note [5]
           External functions need to be declared to be .global. main is the application entry point that will
           be jumped to from the ininitalization routine in crts1200.o.

       Note [6]
           The main loop is just a single jump back to itself. Square wave generation itself is completely
           handled by the timer 0 overflow interrupt service. A sleep instruction (using idle mode) could be
           used as well, but probably would not conserve much energy anyway since the interrupt service is
           executed quite frequently.

       Note [7]
           Interrupt functions can get the usual names that are also available to C programs. The linker will
           then put them into the appropriate interrupt vector slots. Note that they must be declared .global in
           order to be acceptable for this purpose. This will only work if <avr/io.h> has been included. Note
           that the assembler or linker have no chance to check the correct spelling of an interrupt function,
           so it should be double-checked. (When analyzing the resulting object file using avr-objdump or avr-
           nm, a name like __vector_N should appear, with N being a small integer number.)

       Note [8]
           As explained in the section about special function registers, the actual IO port address should be
           obtained using the macro _SFR_IO_ADDR. (The AT90S1200 does not have RAM thus the memory-mapped
           approach to access the IO registers is not available. It would be slower than using in / out
           instructions anyway.)

       Since the operation to reload TCCNT0 is time-critical, it is even performed before saving SREG.
       Obviously, this requires that the instructions involved would not change any of the flag bits in SREG.

       Note [9]
           Interrupt routines must not clobber the global CPU state. Thus, it is usually necessary to save at
           least the state of the flag bits in SREG. (Note that this serves as an example here only since
           actually, all the following instructions would not modify SREG either, but that's not commonly the
           case.)

       Also, it must be made sure that registers used inside the interrupt routine do not conflict with those
       used outside. In the case of a RAM-less device like the AT90S1200, this can only be done by agreeing on a
       set of registers to be used exclusively inside the interrupt routine; there would not be any other chance
       to 'save' a register anywhere.

       If the interrupt routine is to be linked together with C modules, care must be taken to follow the
       register usage guidelines imposed by the C compiler. Also, any register modified inside the interrupt
       sevice needs to be saved, usually on the stack.

       Note [10]
           As explained in Interrupts, a global 'catch-all' interrupt handler that gets all unassigned interrupt
           vectors can be installed using the name __vector_default. This must be .global, and obviously, should
           end in a reti instruction. (By default, a jump to location 0 would be implied instead.)

Assembler Directives

       The directives available in the assembler are described in the GNU assembler (gas) manual at Assembler
       Directives.

       As gas comes from a Unix origin, its directives and overall assembler syntax is slightly different than
       the one being used by other assemblers. Numeric constants follow the C notation (prefix 0x for
       hexadecimal constants, 0b for binary constants), expressions use a C-like syntax.

       Some common directives include:

       Section Ops Description  .section name,'flags',@typ Put the following objects into named section name.
       Set section flags flags and section type to typ  .pushsection ...
       .popsection Like .section, but also pushes the current section and subsection onto the section stack. The
       current section and subsection can be restored with .popsection.  .subsection int Put the following code
       into subsection number int which is some integer. Subsections are located in order of increasing index
       within their input section. The default after switching to a new section by means of .section or
       .pushsection is subsection 0.  .text
       .data
       .bss Put the following code into the .text section, .data section or .bss section, respectively. The
       assembler knows the right section flags and section type, for example the .text directive is basically
       the same like .section .text,'ax',@progbits. The directives support an optional subsection argument, see
       .subsection above.

       Symbol Ops Description  .global sym
       .globl sym Globalize symbol sym so that it can be referred to in other modules. When a symbol is used
       without prior declaration or definition, the symbol is implicitly global. The .global directive can also
       by used to refer to that symbol, so that the linker pulls in code that defines the symbol (provided such
       a symbol definition exists). For example, code that puts objects in the .data section and that assumes
       that the startup code initializes that area, would use .global __do_copy_data.  .weak syms Declare
       symbols syms as weak symbols, where syms is a comma-separated list of symbols. This applies only when the
       symbols are also defined in the same module. When the linker encounters a weak symbol that is also
       defined as .global in a different module, then the linker will use the latter without raising a
       diagnostic about multiple symbol definitions.  .type sym,@kind Set the type of symbol sym to kind.
       Commonly used symbol types are @function for function symbols like main and @object for data symbols.
       This has an affect for disassemblers, debuggers and tools that show function / object properties.  .size
       sym,size Set the size associated with symbol sym to expression size. The linker works on the level of
       sections, it does not even know what functions are. This directive serves book-keeping, and may be useful
       for debuggers, disassemblers or tools that show which function / object consumes how much memory.  .set
       sym, expr
       .equ sym, expr
       sym = expr Set the value of symbol sym to the value of expression expr. When a global symbol is set
       multiple times, the value stored in the object file is the last value stored into the symbol.  .extern
       Ignored for compatibility with other assemblers.  .org Advance the location pointer to a specified offset
       relative to the beginning of the input section. The location counter cannot be moved backwards.
        This is a fairly pointless directive in an assembler environment that uses relocatable object files. The
       linker determines the final location of the objects. See the FAQ on how to relocate code to a fixed
       address.

       Data Ops Description Alias  .byte list Allocate bytes specified by a list of comma-separated expressions.
       .2byte list Similar to .byte, but for 16-bit values. .word  .4byte list Similar to .byte, but for 32-bit
       values. .long  .8byte list Similar to .byte, but for 64-bit values. .qword  .ascii 'string' Allocate a
       string of characters without \0 termination. .asciz 'string' Allocate a \0 terminated string. .float list
       Allocate IEEE-754 single 32-bit floating-point values specified in the comma-separated list. .double list
       Same, but for IEEE-754 double 64-bit floats. .space num[,val] Allocate num bytes with value val where val
       is optional and defaults to zero. .skip  .zero num Insert num zero bytes. Alignment Ops Description Alias
       .balign val Align the following code to val bytes, where val is an absolute expression that evaluates to
       a power of 2. .align  .p2align expo Align the following code to 2expo  bytes.

       Moreover, there is the .macro directive, which starts an assembler macro. The GNU assembler implements a
       powerful macro processor which even supports recursive macro definitions. For an example, see the gas
       documentation for .macro. A gas .macro can further be combined with C preprocessor directives. For some
       real-world examples, see the AVR-LibC sources macros.inc and asmdef.h.

Operand Modifiers

       There are some AVR-specific operators available like lo8(), hi8(), pm(), gs() etc. For an overview see
       the documentation of the operand modifiers in the inline assembly Cookbook.

       Example:

           ldi    r24, lo8(gs(somefunc))
           ldi    r25, hi8(gs(somefunc))
           call      something
           subi r24, lo8(-(my_var))
           sbci r25, hi8(-(my_var))

       This passes the address of function somefunc as the first parameter to function something, and adds the
       address of variable my_var to the 16-bit return value of something.