A MORON'S GUIDE TO STARTING IN AVR ASSEMBLER v1.1
BASIC AVR ARCHITECTURE
THE INCLUDE FILE DIRECTIVE
INSTALLING THE ASSEMBLER
BEEP: OUR FIRST PROGRAM
A BUTTERFLY BEEP
ADAPTATIONS FOR OTHER AVR CHIPS
THE ORIGIN DIRECTIVE
THE LOW & HIGH EXPRESSIONS
THE STACK POINTER
THE DATA DIRECTION REGISTERS
THE CLR & SER COMMANDS
ACTIVATING THE SPEAKER
SUBROUTINES AND THE RCALL & RET COMMANDS
THE ADD & SUBTRACT COMMANDS
JUMPING & BRANCHING
THE PAUSE ROUTINE
THE NOP INSTRUCTION
THE AND INSTRUCTION
THE OR INSTRUCTION
THE EOR INSTRUCTION
AN INVERSE OR "NOT" OPERATION
THE SBI & CBI INSTRUCTIONS
THE PUSH & POP INSTRUCTIONS
Conceptually Assembly Language Programming is very simple, you typically move a byte of data into a register, you do something to it, then you write it out. Practically all languages convert their programs to assembly because assembly is close to what the hardware understands, so there is almost a one-to-one relationship between assembly instructions and hardware binary code (machine language).
Assembly may appear difficult to the novice because of the initial steep learning curve, however because of the limited number of instructions, once over-the-hump it is very simple. Before you can start you need a good understanding of the architecture of the machine, the instruction set, the assembler's syntax and a basic understanding of programming principles such as looping and subroutines before you can create your first program.
One of the benefits of learning Assembly is that they are all very similar, so once you learn one, the rest can be picked up easily since most processors do the exact same thing at the hardware level, basically moving bytes around.
Assembly allows you to create the smallest and fastest code possible, however it can be long and tedious for large projects.
BASIC AVR ARCHITECTURE
In its simplest form the AVRs are made of Registers, Ports and RAM:
[REGISTERS] [PORTS] [RAM]
There are 32 registers which can be though of as fast RAM so it is where most of the work is done. As I mention previously, most of Assembly is moving data into these registers and doing something with them.
RAM is where we store our programs and data.
The ports are how we communicate with the outside world but they appear to the MCU as additional registers.
The registers are one byte each (eight bits) and are internal to the MCU so they operate quickly. We can think of them as a work space to get things done before they are sent off somewhere. So before we can write assembly programs for the AVR, a good understanding of the registers is important.
There are 32 internal registers in the AVRs typically referred to as R0-R31. The most often used is R16-R31 because they are easier to use. They can be loaded directly with a constant. For example, if you want to load 100 into register R16 with 100 you would use:
LDI R16,100 ;LDI = LoaD Immediate 100 into Register 16
LDI means LoaD Immediate, R16 is the register and 100 is our constant. Anything after the semi-colon ";" is a comment which is ignored by the Assembler.
If you want to move the value of 100 into one of the Registers R0-R15 you CANNOT do this:
LDI R1,100 ;This is an ERROR!!!
To move 100 to one of the registers R0-R15 you would do something like:
LDI R16,100 ;Load 100 into Register 16 MOV R1,R16 ;Move the contents of Register 16 to Register 1
This moves the 100 into Register R16 first, then we move it from R16 then into R0. Note that the operands are read right-to-left. The MOV is from R16 to R1 and not the reverse, even though it may appear to read that way.
So we can see that Registers R16-R31 are easier to use because they are half the work to load. Out of these, Registers R26-R31 are used as two-byte pointers by more advanced commands, so we should stick to the ten Registers R16-R25 as our main workspace to start.
We can use the .DEF command to give our registers meaningful names.
.DEF A = R16 .DEF B = R18 .DEF N = R20 .DEF STUDENT_SCORE = R25
Now we can load the R16 Register with 100 using the command:
Constants are values that do not change value while the program is running. They are defined at the time your program is assembled into machine language (binary code) and do not change when your program is executed.
Constants can be given a name with the .SET (or .EQU) command. In our last example we loaded the R16 register with the value of 100. Instead of using the constant 100 we could give it a name like PERFECT_SCORE with the statements:
.SET PERFECT_SCORE = 100 .EQU PERFECT_SCORE = 100
Then later in the program we can load R16 with 100 using the command:
Constants can be represented in a number of ways. They can be defined as hexadecimal, octal, binary, etc. All of the following define PERFECT_SCORE as 100:
.SET PERFECT_SCORE = 100 ;Decimal notation .SET PERFECT_SCORE = (2000+500)/25 ;2500 divided by 25 = 100 .SET PERFECT_SCORE = 0x0064 ;Hexadecimal notation .EQU PERFECT_SCORE = $64 ;Hexadecimal notation .EQU PERFECT_SCORE = 0b0110_0100 ;Binary notation .EQU PERFECT_SCORE = 0144 ;Octal Notation .EQU PERFECT_SCORE = 'd' ;ASCII Notation
As we have seen before, a constant can be loaded directly into the Registers from R16 to 31. All of the following will load R16 with 100:
LDI R16,100 LDI R16,PERFECT_SCORE LDI R16,(2000+500)/25 LDI R16,$64 LDI R16,0b0110_0100 LDI R16,'d' LDI A,PERFECT_SCORE ;if you have defined A = R16
THE INCLUDE FILE DIRECTIVE
The AVRs include a large family of chips. To help us produce code for the various processors, ATMEL provides a file for each one that contains a series of standard .DEF and .EQU definitions tailored to that specific chip. For example here is a small clip from the M69DEF.INC file for the ATmega169 processor that is used in the AVR Butterflys that defines the R26-R31 Registers as two-byte pointers called X, Y and Z:
; ***** CPU REGISTER DEFINITIONS ************* .def XH = r27 .def XL = r26 .def YH = r29 .def YL = r28 .def ZH = r31 .def ZL = r30
The .INCLUDE directive tells the assembler to read in a file as part of our program. For example at the top of a program for the Butterfly you will typically see:
.INCLUDE "M169DEF.INC" ;BUTTERFLY DEFS
Or for a program for the ATtiny13:
You could even create your own libraries of commonly used routine or constants and include them yourself.
INSTALLING THE ASSEMBLER
The best way to learn Assembler is by doing Assembler, so if you have not done it yet, we simply download the Studio 4 Software from Atmel.com and install it in Windows. Last time I downloaded it, I had to register first, which is a simple and quick process. If you are using another operating system, I assume you have already figured out how to install the assembler and programmer. We run a cable from a COM port on your PC to either your programmer or chip, and you are ready to go.
We are going to enter an assembly program called BEEP for the Butterfly. If you are not using the Butterfly, keep reading along we'll get into how to adapt the program for other chips soon enough.
If this is the first time you run the software, select "Atmel AVR Assembler." Select "Assembler 2" if asked. Enter a file name of BEEP or something similar, then you will be presented with a list of chips for which to assemble for, if using the Butterfly choose ATmega169.
FOR THE REMAINING CHAPTERS I INVITE YOU TO DOWNLOAD THE PDF FILE BELOW
(You must login to see the Files)