NOCC AVR Assembler

The AVR assembler in NOCC provides a fairly straightforward way of generating code for Atmel's ( 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.






expr [ , expr [ ... ] ]

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

.const 5, "hello", 0x00


expr [ , expr [ ... ] ]

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

.const16 0xbeef, 0xf00d


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).



name = reg-expr

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

.def XH = r27


End of macro definition.



name = expr

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

.equ RAMSTART = 0x100


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




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

.include ""


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

Start of macro definition.

.macro BitSet (Port, Bit)



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

.mcu "atmega328"



Specifies the origin (address) for what follows.

.org 0



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

.space 2


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


Values (constants)

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







8-bit ASCII character


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.


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
   5 ; stuff
   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-byte of the given value


lo-byte of the given value


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)
   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 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
   8 .macro BitSet (Port, Bit)
   9         sbi     Port, Bit
  10 .endmacro
  12 ; ...
  14 reset:
  15         ShortWait
  16         BitSet (PORTD, 5)

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"
   3 .include ""
   5 .text
   6 .include ""
   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
  17         sbi     DDRB, 5                 ; PB5 output
  18         cbi     PORTB, 5                ; PB5 low (LED off)
  20 loop:
  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
  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)