logo
Free, unlimited AI code reviews that run on commit
git-lrc git-lrc GitHub Install Now We'd appreciate a star git-lrc - Free, unlimited AI code reviews that run on commit | Product Hunt git-lrc - Free, unlimited AI code reviews that run on commit | Product Hunt

assembler - AVR-LibC and Assembler Programs

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',@typPutthefollowingobjectsintonamedsectionname.Setsectionflagsflagsandsectiontypetotyp.pushsection....popsectionLike.section,butalsopushesthecurrentsectionandsubsectionontothesectionstack.Thecurrentsectionandsubsectioncanberestoredwith.popsection..subsectionintPutthefollowingcodeintosubsectionnumberintwhichissomeinteger.Subsectionsarelocatedinorderofincreasingindexwithintheirinputsection.Thedefaultafterswitchingtoanewsectionbymeansof.sectionor.pushsectionissubsection0..text.data.bssPutthefollowingcodeintothe.textsection,.datasectionor.bsssection,respectively.Theassemblerknowstherightsectionflagsandsectiontype,forexamplethe.textdirectiveisbasicallythesamelike.section.text,'ax',@progbits.Thedirectivessupportanoptionalsubsectionargument,see.subsectionabove.

       Symbol Ops Description  .global sym
       .globl  symGlobalizesymbolsymsothatitcanbereferredtoinothermodules.Whenasymbolisusedwithoutpriordeclarationordefinition,thesymbolisimplicitlyglobal.The.globaldirectivecanalsobyusedtorefertothatsymbol,sothatthelinkerpullsincodethatdefinesthesymbol(providedsuchasymboldefinitionexists).Forexample,codethatputsobjectsinthe.datasectionandthatassumesthatthestartupcodeinitializesthatarea,woulduse.global__do_copy_data..weaksymsDeclaresymbolssymsasweaksymbols,wheresymsisacomma-separatedlistofsymbols.Thisappliesonlywhenthesymbolsarealsodefinedinthesamemodule.Whenthelinkerencountersaweaksymbolthatisalsodefinedas.globalinadifferentmodule,thenthelinkerwillusethelatterwithoutraisingadiagnosticaboutmultiplesymboldefinitions..typesym,@kindSetthetypeofsymbolsymtokind.Commonlyusedsymboltypesare@functionforfunctionsymbolslikemainand@objectfordatasymbols.Thishasanaffectfordisassemblers,debuggersandtoolsthatshowfunction/objectproperties..sizesym,sizeSetthesizeassociatedwithsymbolsymtoexpressionsize.Thelinkerworksonthelevelofsections,itdoesnotevenknowwhatfunctionsare.Thisdirectiveservesbook-keeping,andmaybeusefulfordebuggers,disassemblersortoolsthatshowwhichfunction/objectconsumeshowmuchmemory..setsym,expr.equsym,exprsym=exprSetthevalueofsymbolsymtothevalueofexpressionexpr.Whenaglobalsymbolissetmultipletimes,thevaluestoredintheobjectfileisthelastvaluestoredintothesymbol..externIgnoredforcompatibilitywithotherassemblers..orgAdvancethelocationpointertoaspecifiedoffsetrelativetothebeginningoftheinputsection.Thelocationcountercannotbemovedbackwards.Thisisafairlypointlessdirectiveinanassemblerenvironmentthatusesrelocatableobjectfiles.Thelinkerdeterminesthefinallocationoftheobjects.SeetheFAQonhowtorelocatecodetoafixedaddress.

       Data Ops Description Alias  .byte listAllocatebytesspecifiedbyalistofcomma-separatedexpressions..2bytelistSimilarto.byte,butfor16-bitvalues..word.4bytelistSimilarto.byte,butfor32-bitvalues..long.8bytelistSimilarto.byte,butfor64-bitvalues..qword.ascii'string'Allocateastringofcharacterswithout\0termination..asciz'string'Allocatea\0terminatedstring..floatlistAllocateIEEE-754single32-bitfloating-pointvaluesspecifiedinthecomma-separatedlist..doublelistSame,butforIEEE-754double64-bitfloats..spacenum[,val]Allocatenumbyteswithvaluevalwherevalisoptionalanddefaultstozero..skip.zeronumInsertnumzerobytes.AlignmentOpsDescriptionAlias.balignvalAlignthefollowingcodetovalbytes,wherevalisanabsoluteexpressionthatevaluatestoapowerof2..align.p2alignexpoAlignthefollowingcodeto2expobytes.

       Moreover,  there is the .macrodirective,whichstartsanassemblermacro.TheGNUassemblerimplementsapowerfulmacroprocessorwhichevensupportsrecursivemacrodefinitions.Foranexample,seethegasdocumentationfor.macro.Agas.macrocanfurtherbecombinedwithCpreprocessordirectives.Forsomereal-worldexamples,seetheAVR-LibCsourcesmacros.incandasmdef.h.

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 usualnames 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> hasbeenincluded.Notethattheassemblerorlinkerhavenochancetocheckthecorrectspellingofaninterruptfunction,soitshouldbedouble-checked.(Whenanalyzingtheresultingobjectfileusingavr-objdumporavr-nm,anamelike__vector_Nshouldappear,withNbeingasmallintegernumber.)Note[8]
           As  explained  in  the section about specialfunctionregisters, 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
       registerusageguidelines 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.)

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  inlineassembler  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,
         initializingtheRAMvariables 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)andlinkerscript.

       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 -xassembler-with-cppoption.

Name

       assembler - AVR-LibC and Assembler Programs

       • IntroductionInvokingtheCompilerExampleProgramAssemblerDirectivesSectionsSymbolsDataandAlignmentOperandModifiers

Operand Modifiers

       There are some AVR-specific operators available like lo8(), hi8(), pm(), gs() etc. For  an  overview  see
       the documentation of the operandmodifiers 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.

AVR-LibC                                          Version 2.2.1                                  assembler(3avr)

See Also