assembler - AVR-LibC and Assembler Programs
Contents
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
• Introduction
• InvokingtheCompiler
• ExampleProgram
• AssemblerDirectives
• Sections
• Symbols
• DataandAlignment
• OperandModifiersOperand 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)
