File: assembler.dox

package info (click to toggle)
avr-libc 1%3A1.8.0-2
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 18,444 kB
  • sloc: ansic: 164,744; asm: 8,048; makefile: 7,456; sh: 3,907; pascal: 443; python: 45
file content (330 lines) | stat: -rw-r--r-- 12,339 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* Copyright (c) 2002,2004,2005,2009 Joerg Wunsch
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
   * Neither the name of the copyright holders nor the names of
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. */

/* $Id: assembler.dox 1876 2009-02-11 18:57:36Z joerg_wunsch $ */

/** \page assembler avr-libc and assembler programs

\section ass_intro 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
\ref inline_asm "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,
  \ref sec_dot_init "initializing the RAM variables" can also
  be utilized.

\section ass_tools 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 (\c 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,
  \c 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
  (<tt>crt</tt><em>XXX</em><tt>.o</tt>) and linker script.

Note that the invokation of the C preprocessor will be automatic when
the filename provided for the assembler file ends in \c .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.

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

\section ass_example 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.

\code
#include <avr/io.h>		; 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 ISR until TCNT0 is set

	.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
	sbi	_SFR_IO_ADDR(PORTD), SQUARE
	rjmp	2f
1:	cbi	_SFR_IO_ADDR(PORTD), SQUARE
2:

	out	_SFR_IO_ADDR(SREG), intsav
	reti

ioinit:
	sbi	_SFR_IO_ADDR(DDRD), SQUARE

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

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

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

	sei

	ret

	.global __vector_default		; Note [10]
__vector_default:
	reti

	.end
\endcode

\par 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.

\par Note [2]

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

\code #define work 16 \endcode

\par 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.

\par 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 \c int 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 \c TCCNT0 register, we therefore have to account
for the number of clock cycles required for interrupt acknowledge and
for the instructions to reload \c TCCNT0 (4 clock cycles for interrupt
acknowledge, 2 cycles for the jump from the interrupt vector, 2 cycles
for the 2 instructions that reload \c TCCNT0).  This is what the
constant \c fuzz is for.

\par Note [5]

External functions need to be declared to be \c .global.  \c main is
the application entry point that will be jumped to from the
ininitalization routine in \c crts1200.o.

\par 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 \c 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.

\par Note [7]

Interrupt functions can get the \ref avr_signames "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 \c .global in order to be acceptable for this purpose.
This will only work if <tt>&lt;avr/io.h&gt;</tt> 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 \c avr-objdump or
\c avr-nm, a name like <tt>__vector_<em>N</em></tt> should appear,
with \e N being a small integer number.)

\par Note [8]

As explained in the section about
\ref avr_sfr_notes "special function registers",
the actual IO port address should be obtained using the macro
\c _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 \c in / \c out instructions anyway.)
<br>
Since the operation to reload \c TCCNT0 is time-critical, it is even
performed before saving \c SREG.  Obviously, this requires that the
instructions involved would not change any of the flag bits in \c SREG.

\anchor ass_isr
\par 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 \c SREG.
(Note that this serves as an example here only since actually, all the
following instructions would not modify \c SREG 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 \ref faq_reg_usage "register usage guidelines"
imposed by the C compiler.  Also, any register modified inside the
interrupt sevice needs to be saved, usually on the stack.

\par Note [10]

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

\section ass_pseudoops Pseudo-ops and operators

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 http://sources.redhat.com/binutils/.

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 \c 0x for
hexadecimal constants), expressions use a C-like syntax.

Some common pseudo-ops include:

- \c .byte allocates single byte constants

- \c .ascii allocates a non-terminated string of characters


- \c .asciz allocates a \\0-terminated string of characters (C string)

- \c .data switches to the .data section (initialized RAM variables)

- \c .text switches to the .text section (code and ROM constants)

- \c .set declares a symbol as a constant expression (identical to
  \c .equ)

- \c .global (or \c .globl) declares a public symbol that is visible
  to the linker (e. g. function entry point, global variable)

- \c .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

Note that \c .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.

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:

- \c lo8 Takes the least significant 8 bits of a 16-bit integer

- \c hi8 Takes the most significant 8 bits of a 16-bit integer

- \c pm 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 \c IJMP or \c ICALL
  instruction), and can also handle relocatable symbols on the
  right-hand side.

Example:
\verbatim
	ldi	r24, lo8(pm(somefunc))
	ldi	r25, hi8(pm(somefunc))
	call	something
\endverbatim

This passes the address of function \c somefunc as the first parameter
to function \c something.

*/