# Basic AVR assembly language¶

Read time: 18 minutes (4565 words)

To get started writing AVR assembly language programs, you need a reference that details the instructions you can use. The chip we will be using is an Atmel ATmega328P, found on the Arduino Uno boards.

Here is the “datasheet” for this chip:

And an instruction set summary:

Now, we can lay out a code file.

## Basic File Layout¶

The basic layout of a program file looks like this :

• Comments begin with a semicolon
• We will declare labels as usual
• Directives tell the assembler what to do (usually start with a dot)
• Assembly files are named something.S (note the capital S)

Use a comment block at the top of the file to identify the project, and who created it.

; Hello Blinky World for AVR
; Author: Roie R. Black
; Date: July 14, 2015


Put any additional documentation you need here as well. For simple code files, letting the user know how to build the project is a good idea!

### Chip Definitions¶

We need to define a few things for most programs we write for this chip. We will go over all of that later. For now, we will place all of this definition logic in a separate include file, to make it easy to change as needed.

#include "config.h"


### Data layout¶

	.section .data
dummy: 	.byte 0		; dummy global variable


This is a dummy declaration, we will not need this variable in the code. It is here just to show you how to set up a variable.

Note

There are no other data types, only bytes! In general, we do not do number crunching in these machines!

### Code layout¶

        .section .text
.global     main
.extern     delay
.org        0x0000

main:


The line that defines the “org” (origin) of this program code, which is the address where code will be placed in the chip. Since there will be no other code in the chip when we run our program, we start this code at address zero in the program code area.

Before we can actually run any real processing code, we have some chip initialization to do!

### Initialize the chip¶

The first chunk of code we need sets up the system stack pointer, so we can call procedures. We set up a stack at the top of available memery:

Warning

Pay attention to those names in all caps surrounded by parentheses, and with a leading underscore in front of them. This is a scheme I came up with to simplify something I have not liked in writing code for this chip. We will go over that issue in our next lecture. For now, the name in caps is a standard AVR register name, the underscore and parentheses have been added to streamline the code we write, to make it easier to read.

#### Clearing the Status register¶

The SREG register is used to record various conditions that are generated as we process instructions. We need to clear that flag before we start our real code:

main:
; clear the SREG register
eor     r1, r1                  ; cheap zero
out     SREG, r1                ; clear flag register


That line that uses the eor instruction is th fastest way to clear all the bits in a register. The XOR operator is applied bit by bit, and always results in a zero. It is common in AVR code to leave the zero in r1 for use whenever we need a zero * We have enough registers to sacrifice one!

#### Setting Up a Stack¶

We need a special area where we will save return addresses as we call procedures, We will look at that in our next lecture. For now, we just initialize a special register to so it is pointing to the top of available memory in this chip:

        ; set up the stack
ldi         r28, (RAMEND & 0x00ff)
ldi         r29, (RAMEND >> 8)
out         _(SPH), r29
out         _(SPL), r28


Now that we have the stack set up, we are free to use procedures in our code.

#### Setting up the clock¶

	; initialize the CPU clock to run at full speed
ldi         r24, 0x80
sts         CLKPR, r1               ; run at full speed


This code is tricky. There is a special lock on the register that controls the speed of the processor. Writing accidentally into this register could be dangerous. So, the designers made it hard to access. Basically, you write an 0x80 into the register, then immediately write in the value you want on the next instruction. The first write unlocks the register, and the next one puts in a new value. Right after that, the register is re-locked.

## Initializing the LED pin¶

There is one more thing we need to do! Before we can make the LED blink, we need to set up the output pin on the chip that drives the LED so it is an output. Here is the code that does this:

        ; set up the LED port
sbi         LED_DIR, LED_PIN        ; set LED pin to output



The names used here are defined in our “config.h” file, so we can change them if needed.

Here is that file:

## Hello, World - Arduino style¶

Finally, we can write the real code for this project. Everything up to this point is setup, and we need to do almost exactly that same thing for every project we build!

Since the Arduino has no console to display test on, the equivalent of “Hello, World” is simple” blink a light at some defined rate!

        out         _(SPL), r28

; initialize the CPU clock to run at full speed
ldi         r24, 0x80


See the unusual label here? Labels can be numbers in this assembler. We can refer to a numbered label with an “f” (look forward in the code) or “b” (look backward in the code). The first such numbered label is the one we are referring to. This avoids making up silly labels to mark a line of code for later reference. * No more silly names needed

## Toggling the LED on/off¶

toggle:
in          r24, LED_PORT           ; get current bits
ldi         r25, (1 << LED_PIN)     ; LED is pin 5
eor         r24, r25                ; flip the bit
out         LED_PORT, r24           ; write the bits back
ret


The EOR instruction is the same as XOR in the pentium. We are “toggling” a single bit to make the light blink!

This is all the code we need - except for the delay code

Here is the complete project code:

  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 ; Hello Blinky World for AVR ; Author: Roie R. Black ; Date: July 14, 2015 #include "config.h" .section .data dummy: .byte 0 ; dummy global variable .section .text .global main .extern delay .org 0x0000 main: ; clear the SREG register eor r1, r1 ; cheap zero out SREG, r1 ; clear flag register ; set up the stack ldi r28, (RAMEND & 0x00ff) ldi r29, (RAMEND >> 8) out _(SPH), r29 out _(SPL), r28 ; initialize the CPU clock to run at full speed ldi r24, 0x80 sts CLKPR, r24 ; allow access to clock setup sts CLKPR, r1 ; run at full speed ; set up the LED port sbi LED_DIR, LED_PIN ; set LED pin to output cbi LED_PORT, LED_PIN ; start with the LED off ; enter the blink loop 1: call toggle call delay rjmp 1b toggle: in r24, LED_PORT ; get current bits ldi r25, (1 << LED_PIN) ; LED is pin 5 eor r24, r25 ; flip the bit out LED_PORT, r24 ; write the bits back ret 
 1 2 3 4 5 6 7 8 9 #include "config.h" .global delay .section .text delay: ldi r26, 255 1: dec r26 brne 1b ret 
 1 2 3 4 5 6 7 8 9 #include #define LED_PIN 5 #define LED_PORT _(PORTB) #define LED_DIR _(DDRB) // include this line to avoid SFR_REG issues #define _(s) _SFR_IO_ADDR(s)