You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
15 KiB
HTML
189 lines
15 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
|
|
<title>avr-libc: avr-libc and assembler programs</title>
|
|
<link href="dox.css" rel="stylesheet" type="text/css">
|
|
</head>
|
|
<body>
|
|
<center>
|
|
<table width="80%">
|
|
<tr>
|
|
<td align="left"><a href="http://www.nongnu.org/avr-libc/">AVR Libc Home Page</a></td>
|
|
<td align="center" colspan=4><img src="avrs.png" alt="AVRs" align="middle" border="0"></td>
|
|
<td align="right"><a href="https://savannah.nongnu.org/projects/avr-libc/">AVR Libc Development Pages</a></td>
|
|
</tr>
|
|
<tr>
|
|
<td align="center" width="13%"><a href="index.html">Main Page</a></td>
|
|
<td align="center" width="13%"><a href="pages.html">User Manual</a></td>
|
|
<td align="center" width="13%"><a href="modules.html">Library Reference</a></td>
|
|
<td align="center" width="13%"><a href="FAQ.html">FAQ</a></td>
|
|
<td align="center" width="13%"><a href="globals.html">Alphabetical Index</a></td>
|
|
<td align="center" width="13%"><a href="group__demos.html">Example Projects</a></td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
<hr width="80%">
|
|
<!-- Generated by Doxygen 1.5.6 -->
|
|
<div class="contents">
|
|
<h1><a class="anchor" name="assembler">avr-libc and assembler programs </a></h1><h2><a class="anchor" name="ass_intro">
|
|
Introduction</a></h2>
|
|
There might be several reasons to write code for AVR microcontrollers using plain assembler source code. Among them are:<p>
|
|
<ul>
|
|
<li>Code for devices that do not have RAM and are thus not supported by the C compiler.</li><li>Code for very time-critical applications.</li><li>Special tweaks that cannot be done in C.</li></ul>
|
|
<p>
|
|
Usually, all but the first could probably be done easily using the <a class="el" href="inline_asm.html">inline assembler</a> facility of the compiler.<p>
|
|
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:<p>
|
|
<ul>
|
|
<li>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).</li><li>Use of the runtime framework like automatically assigning interrupt vectors. For devices that have RAM, <a class="el" href="mem_sections.html#sec_dot_init">initializing the RAM variables</a> can also be utilized.</li></ul>
|
|
<h2><a class="anchor" name="ass_tools">
|
|
Invoking the compiler</a></h2>
|
|
For the purpose described in this document, the assembler and linker are usually not invoked manually, but rather using the C compiler frontend (<code>avr-gcc</code>) that in turn will call the assembler and linker as required.<p>
|
|
This approach has the following advantages:<p>
|
|
<ul>
|
|
<li>There is basically only one program to be called directly, <code>avr-gcc</code>, regardless of the actual source language used.</li><li>The invokation of the C preprocessor will be automatic, and will include the appropriate options to locate required include files in the filesystem.</li><li>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 (<code>crt</code><em>XXX</em><code>.o</code>) and linker script.</li></ul>
|
|
<p>
|
|
Note that the invokation of the C preprocessor will be automatic when the filename provided for the assembler file ends in <code></code>.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.<p>
|
|
Alternatively, the language can explicitly be specified using the <code>-x assembler-with-cpp</code> option.<h2><a class="anchor" name="ass_example">
|
|
Example program</a></h2>
|
|
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.<p>
|
|
<div class="fragment"><pre class="fragment"><span class="preprocessor">#include <<a class="code" href="io_8h.html">avr/io.h</a>></span> ; Note [1]
|
|
|
|
work = 16 ; Note [2]
|
|
tmp = 17
|
|
|
|
inttmp = 19
|
|
|
|
intsav = 0
|
|
|
|
SQUARE = PD6 ; Note [3]
|
|
|
|
; Note [4]:
|
|
tmconst= 10700000 / 200000 ; 100 kHz => 200000 edges/s
|
|
fuzz= 8 ; # clocks in <a class="code" href="group__avr__interrupts.html#gd28590624d422cdf30d626e0a506255f">ISR</a> until TCNT0 is <span class="keyword">set</span>
|
|
|
|
.section .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 _SFR_IO_ADDR(TCNT0), inttmp ; Note [8]
|
|
|
|
in intsav, _SFR_IO_ADDR(SREG) ; Note [9]
|
|
|
|
sbic _SFR_IO_ADDR(PORTD), SQUARE
|
|
rjmp 1f
|
|
<a class="code" href="group__deprecated__items.html#g014ef751e83f97569c06f3cdd888f3f7">sbi</a> _SFR_IO_ADDR(PORTD), SQUARE
|
|
rjmp 2f
|
|
1: <a class="code" href="group__deprecated__items.html#g08ee265dc07048dbb5a8b6c84551d520">cbi</a> _SFR_IO_ADDR(PORTD), SQUARE
|
|
2:
|
|
|
|
out _SFR_IO_ADDR(SREG), intsav
|
|
<a class="code" href="group__avr__interrupts.html#g3b991e8168db8fc866e31f9a6d10533b">reti</a>
|
|
|
|
ioinit:
|
|
<a class="code" href="group__deprecated__items.html#g014ef751e83f97569c06f3cdd888f3f7">sbi</a> _SFR_IO_ADDR(DDRD), SQUARE
|
|
|
|
ldi work, <a class="code" href="group__avr__sfr.html#g11643f271076024c395a93800b3d9546">_BV</a>(TOIE0)
|
|
out _SFR_IO_ADDR(TIMSK), work
|
|
|
|
ldi work, <a class="code" href="group__avr__sfr.html#g11643f271076024c395a93800b3d9546">_BV</a>(CS00) ; tmr0: CK/1
|
|
out _SFR_IO_ADDR(TCCR0), work
|
|
|
|
ldi work, 256 - tmconst
|
|
out _SFR_IO_ADDR(TCNT0), work
|
|
|
|
<a class="code" href="group__avr__interrupts.html#gad5ebd34cb344c26ac87594f79b06b73">sei</a>
|
|
|
|
ret
|
|
|
|
.global __vector_default ; Note [10]
|
|
__vector_default:
|
|
<a class="code" href="group__avr__interrupts.html#g3b991e8168db8fc866e31f9a6d10533b">reti</a>
|
|
|
|
.end
|
|
</pre></div><p>
|
|
<dl class="user" compact><dt><b>Note [1]</b></dt><dd></dd></dl>
|
|
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.<p>
|
|
<dl class="user" compact><dt><b>Note [2]</b></dt><dd></dd></dl>
|
|
Assignment of registers to symbolic names used locally. Another option would be to use a C preprocessor macro instead:<p>
|
|
<div class="fragment"><pre class="fragment"><span class="preprocessor"> #define work 16 </span>
|
|
</pre></div><p>
|
|
<dl class="user" compact><dt><b>Note [3]</b></dt><dd></dd></dl>
|
|
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.<p>
|
|
<dl class="user" compact><dt><b>Note [4]</b></dt><dd></dd></dl>
|
|
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 <code>int</code> by default in order to calculate constant integer expressions. <br>
|
|
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 <code>TCCNT0</code> register, we therefore have to account for the number of clock cycles required for interrupt acknowledge and for the instructions to reload <code>TCCNT0</code> (4 clock cycles for interrupt acknowledge, 2 cycles for the jump from the interrupt vector, 2 cycles for the 2 instructions that reload <code>TCCNT0</code>). This is what the constant <code>fuzz</code> is for.<p>
|
|
<dl class="user" compact><dt><b>Note [5]</b></dt><dd></dd></dl>
|
|
External functions need to be declared to be <code></code>.global. <code>main</code> is the application entry point that will be jumped to from the ininitalization routine in <code>crts1200.o</code>.<p>
|
|
<dl class="user" compact><dt><b>Note [6]</b></dt><dd></dd></dl>
|
|
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 <code>sleep</code> 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.<p>
|
|
<dl class="user" compact><dt><b>Note [7]</b></dt><dd></dd></dl>
|
|
Interrupt functions can get the <a class="el" href="group__avr__interrupts.html#avr_signames">usual names</a> 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 <code></code>.global in order to be acceptable for this purpose. This will only work if <code><<a class="el" href="io_8h.html">avr/io.h</a>></code> 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 <code>avr-objdump</code> or <code>avr-nm</code>, a name like <code>__vector_<em>N</em></code> should appear, with <em>N</em> being a small integer number.)<p>
|
|
<dl class="user" compact><dt><b>Note [8]</b></dt><dd></dd></dl>
|
|
As explained in the section about <a class="el" href="group__avr__sfr__notes.html">special function registers</a>, the actual IO port address should be obtained using the macro <code>_SFR_IO_ADDR</code>. (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 <code>in</code> / <code>out</code> instructions anyway.) <br>
|
|
Since the operation to reload <code>TCCNT0</code> is time-critical, it is even performed before saving <code>SREG</code>. Obviously, this requires that the instructions involved would not change any of the flag bits in <code>SREG</code>.<p>
|
|
<a class="anchor" name="ass_isr"></a> <dl class="user" compact><dt><b>Note [9]</b></dt><dd></dd></dl>
|
|
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 <code>SREG</code>. (Note that this serves as an example here only since actually, all the following instructions would not modify <code>SREG</code> either, but that's not commonly the case.) <br>
|
|
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. <br>
|
|
If the interrupt routine is to be linked together with C modules, care must be taken to follow the <a class="el" href="FAQ.html#faq_reg_usage">register usage guidelines</a> imposed by the C compiler. Also, any register modified inside the interrupt sevice needs to be saved, usually on the stack.<p>
|
|
<dl class="user" compact><dt><b>Note [10]</b></dt><dd></dd></dl>
|
|
As explained in <a class="el" href="group__avr__interrupts.html">Interrupts</a>, a global "catch-all" interrupt handler that gets all unassigned interrupt vectors can be installed using the name <code>__vector_default</code>. This must be <code></code>.global, and obviously, should end in a <code>reti</code> instruction. (By default, a jump to location 0 would be implied instead.)<h2><a class="anchor" name="ass_pseudoops">
|
|
Pseudo-ops and operators</a></h2>
|
|
The available pseudo-ops in the assembler are described in the GNU assembler (gas) manual. The manual can be found online as part of the current binutils release under <a href="http://sources.redhat.com/binutils/.">http://sources.redhat.com/binutils/.</a><p>
|
|
As gas comes from a Unix origin, its pseudo-op and overall assembler syntax is slightly different than the one being used by other assemblers. Numeric constants follow the C notation (prefix <code>0x</code> for hexadecimal constants), expressions use a C-like syntax.<p>
|
|
Some common pseudo-ops include:<p>
|
|
<ul>
|
|
<li><code></code>.byte allocates single byte constants</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code></code>.ascii allocates a non-terminated string of characters</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code></code>.asciz allocates a \0-terminated string of characters (C string)</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code></code>.data switches to the .data section (initialized RAM variables)</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code></code>.text switches to the .text section (code and ROM constants)</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code></code>.set declares a symbol as a constant expression (identical to <code></code>.equ)</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code></code>.global (or <code></code>.globl) declares a public symbol that is visible to the linker (e. g. function entry point, global variable)</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code></code>.extern declares a symbol to be externally defined; this is effectively a comment only, as gas treats all undefined symbols it encounters as globally undefined anyway</li></ul>
|
|
<p>
|
|
Note that <code></code>.org is available in gas as well, but is a fairly pointless pseudo-op in an assembler environment that uses relocatable object files, as it is the linker that determines the final position of some object in ROM or RAM.<p>
|
|
Along with the architecture-independent standard operators, there are some AVR-specific operators available which are unfortunately not yet described in the official documentation. The most notable operators are:<p>
|
|
<ul>
|
|
<li><code>lo8</code> Takes the least significant 8 bits of a 16-bit integer</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code>hi8</code> Takes the most significant 8 bits of a 16-bit integer</li></ul>
|
|
<p>
|
|
<ul>
|
|
<li><code>pm</code> Takes a program-memory (ROM) address, and converts it into a RAM address. This implies a division by 2 as the AVR handles ROM addresses as 16-bit words (e.g. in an <code>IJMP</code> or <code>ICALL</code> instruction), and can also handle relocatable symbols on the right-hand side.</li></ul>
|
|
<p>
|
|
Example: <div class="fragment"><pre class="fragment">
|
|
ldi r24, lo8(pm(somefunc))
|
|
ldi r25, hi8(pm(somefunc))
|
|
call something
|
|
</pre></div><p>
|
|
This passes the address of function <code>somefunc</code> as the first parameter to function <code>something</code>. </div>
|
|
|
|
<hr width="80%">
|
|
<p><center>Automatically generated by Doxygen 1.5.6 on 4 Dec 2008.</center></p>
|
|
|
|
</body>
|
|
</html>
|