NOCC AVR Assembler

The AVR assembler in NOCC provides a fairly straightforward way of generating code for Atmel's (http://www.atmel.com/) AVR 8-bit microcontroller devices. The assembler syntax mostly follows the style of that presented in the AVR documentation. Some rudimentary high-level things are available, such as macros, functions and some conveniences for dealing with 16-bit quantities. I'm using this to program Arduino boards at the moment, so mainly ATmega328s (Arduino Uno) and ATmega1280s (Arduino Mega). The current compiler framework (r4436+) generates two Intel hex-style image files as output: one for the flash, the other for the EEPROM.

Language lexicon

The source language is intended to be straightforward, and moderately easy to program. Register names are expressed as r0 through r31 (or as the X,Y,Z combo registers where appropriate); constant values are written as-is; some built-in expression evaluation to make programming a little more palatable.

Assembler directives

These are used to do just about everything that isn't an instruction or label.

Directive

Arguments

Description

Example

.const

expr [ , expr [ ... ] ]

Constant data (in text or eeprom sections), given as a comma-separated list of 8-bit values).

.const 5, "hello", 0x00

.const16

expr [ , expr [ ... ] ]

Similar to .const, but writes in 16-bit quantities rather than 8-bit ones.

.const16 0xbeef, 0xf00d

.data

Indicates that what follows should go into the data (SRAM) section. Note that no code or constant data can be placed here (not building-in SRAM initialisation, yet).

.data

.def

name = reg-expr

Defines (specifically) a register name, that will undergo literal substitution (for reg-expr).

.def XH = r27

.endmacro

End of macro definition.

.endmacro

.equ

name = expr

Defines an assembler program constant, that will undergo literal substitution for the given expression expr.

.equ RAMSTART = 0x100

.eeprom

Indicates that what follows should go into the eeprom (EEPROM) section.

.eeprom

.include

string

Includes another file at the indicated point. The compiler's IPATHs will be searched for this file by default.

.include "atmega328.inc"

.macro

name [ ( param-name [ , ... ] ) ]

Start of macro definition.

.macro BitSet (Port, Bit)

.mcu

string

Sets the MCU type (string), case insensitive match against a list of MCUs the compiler knows about.

.mcu "atmega328"

.org

uint16

Specifies the origin (address) for what follows.

.org 0

.space

uint16

Reserves the specified amount of space (in bytes). In the text section, this will be padded to 16-bits.

.space 2

.text

Indicates that what follows should go into the text (code) section.

.text

Values (constants)

The AVR assembler's lexer understands various constant number bases:

hexadecimal

0x100

decimal

42

binary

01100101b

8-bit ASCII character

'z'

Characters can be escaped in the usual C-style (newline, etc.). When specifying constant data (.const), literal strings are permitted, equivalent to a list of individual characters. Such string constants are not null-terminated by default. References to labels (that are not the target of a branch of call instruction) are also treated as constant values.

Expressions

The assembler/compiler understands a simple C-style expression syntax. In the majority of cases, such expressions must be constant (i.e. the compiler can work out what the value is). Parsing is done strictly left-to-right, so unbracketed expressions will be parsed as-is (no operator precedence). The inclusion of expressions in the language is largely to make configuration constants readable, e.g.:

   1 .equ    SPCR_MSTR_BIT = 4
   2 .equ    SPCR_DIV_4 = 0x00
   3 .equ    SPCR_DIV_SHIFT = 0
   4 
   5 ; stuff
   6 
   7 ldi     r16, (1 << SPCR_MSTR_BIT) | (SPCR_DIV_4 << SPCR_DIV_SHIFT)
   8 out     SPCR, r16

The supported expressions are:

+ , - , * , / , %

integer operators

<< , >>

bitwise shifts

& , | , ^

bitwise and, or and exclusive-or

~

bitwise not

hi(...)

hi-byte of the given value

lo(...)

lo-byte of the given value

Labels

Labels are introduced by writing the name followed by a colon, and are referred to just by name, e.g.:

   1 the_label:
   2         ldi     r16, lo(the_label)      ; load (flash memory) address of the label
   3         ldi     r17, hi(the_label)
   4 
   5         rjmp    the_label               ; jump to the label

Labels defined in this way have global scope, and must be unique. Numbered local labels (such as those supported by the GNU assembler) can be defined with .LN:, with no upper-limit on N. Such labels are referred to by the relative position in the source file, forwards (2f) or backwards (1b). E.g.:

   1 .L0:
   2         ; some code
   3         cp      r3, r8
   4         brlt    1f                      ; branch if (r3 < r8)
   5         rjmp    0b                      ; loop
   6 .L1:
   7         ; some more code

Macros

Macros work in the same way as pre-processor macros in C/C++, i.e. undergo literal substitution before any sort of semantic processing. Actual parameters must be valid expressions, however. For example:

   1 .macro ShortWait
   2         nop
   3         nop
   4         nop
   5         nop
   6 .endmacro
   7 
   8 .macro BitSet (Port, Bit)
   9         sbi     Port, Bit
  10 .endmacro
  11 
  12 ; ...
  13 
  14 reset:
  15         ShortWait
  16         BitSet (PORTD, 5)
  17 

Quick start

Assuming you have an Arduino Uno (ATmega328p-based board), NOCC successfully compiled and a tool such AVRDUDE for talking to the board, write a simple blinkenlight program (or download test_avr25.asm):

   1 .mcu    "atmega328"
   2 
   3 .include "atmega328.inc"
   4 
   5 .text
   6 .include "atmega328-imap.inc"
   7 
   8 
   9 VEC_reset:
  10         ; setup stack-pointer
  11         ldi     r16, hi(RAMEND)
  12         out     SPH, r16
  13         ldi     r16, lo(RAMEND)
  14         out     SPL, r16
  15         sei
  16 
  17         sbi     DDRB, 5                 ; PB5 output
  18         cbi     PORTB, 5                ; PB5 low (LED off)
  19 
  20 loop:
  21 
  22         ldi     r16, 0x10
  23 .L0:
  24         ldi     r17, 0xff
  25 .L1:
  26         ldi     r18, 0xff
  27 .L2:
  28         nop
  29         nop
  30         nop
  31         nop
  32         dec     r18
  33         brne    2b                      ; inner loop
  34         dec     r17
  35         brne    1b                      ; middle loop
  36         dec     r16
  37         brne    0b                      ; outer loop
  38 
  39         sbic    PORTB, 5                ; skip if PB5 low
  40         rjmp    3f
  41         ; if here, means LED was off, so turn on
  42         sbi     PORTB, 5
  43         rjmp    loop
  44 .L3:
  45         ; if here, means LED was on, so turn off
  46         cbi     PORTB, 5
  47         rjmp    loop

Compile this into binary images with something along the lines of:

/path/to/nocc -c test_avr25.asm

If everything worked okay (you may see some debugging output from NOCC) there should be two new files, test_avr25.flash.hex and test_avr25.lst. The second of these is uninteresting (and doesn't contain very much at present). The first is the flash image that can be programmed with:

/path/to/avrdude -F -v -p m328p -P/dev/ttyACM0 -carduino -b 115200 -D -Uflash:w:tests/test_avr25.flash.hex:i

Depending on your particular system, you may want/need to substitute /dev/ttyACM0 for the port where the Arduino Uno's USB serial interface appears. If the download worked, your Arduino Uno should be blinking its "L" LED :-) .

NOCC/AVRasm (last edited 2013-11-08 22:01:20 by frmb)