AVR Introduction

        .cr     avr     To load this cross overlay

The Atmel AVR family of micro controllers is clearly a whole new generation of micro controllers because they are unlike most other 8-bitters we knew before. All members of the AVR family have a very similar 32 byte long array of general purpose registers, called the Register File. These registers combine the traditional functions of Accumulator, index registers, temporary registers and arithmetic registers. This will increase the processing power because data doesn't have to be moved around that much anymore before it can be used in operations.

All family members are equipped with a varying amount of In System Programmable FLASH memory. A very easy to build ISP programmer is all the hardware that is needed during development, which makes programming these device really comfortable. Linux users will most likely find the program avrdude in their software repository. It is a simple command line tool, which plays nice with many AVR ISP programmers, including th STK200 and the Raspberry Pi.

Arduino Arduino made the AVR popular

The AVR family has many members and is still growing. The family can be divided into four major groups (see .FA directive):

  • Tiny AVR
  • AVR
  • Mega AVR
  • XMega AVR

Apart from the on board peripheral differences these families mainly differ in the amount of available instructions and instruction / addressing mode combinations. Only one SB-Assembler cross overlay exists to serve them all. The .FA directive is used to select the family of your device.

I did not include any header files, defining all the on-board peripherals, registers and bits, for each of the family members. You can easily create them yourself for the derivative you are currently using.

Every instruction in the AVR is 16-bits wide, while some also require an extra 16-bit operand. Therefore the assembler may behave a bit odd if you're not used to this because all supported target files can only store one byte per address. In code memory the address in the target file is always double the value of the program counter. Instruction words are always stored in the target file with their Low Byte on an even address and their High Byte on an odd address.

A good reference explaining all possible AVR instructions is Atmel's doc0856.pdf document.

Please note: The AVR cross overlay was never officially released for Version 2 of the SB-Assembler. This was because of some nasty bugs which were caused by some shortcomings in the main Assembler body. Version 2 never was good at saving word sized instructions to a byte sized target file.
Even though a Version 2 AVR cross overlay does exist, I will not release it because of these bugs. These bugs make the behaviour of the cross overlay a bit different from Version 3 and could cause incompatibility problems.

Programming Model

Below you see a diagram showing you all the registers in the AVR's Register File. You will be looking for an Accumulator register in vain because any register in the Register File can be used as a traditional Accumulator.
The register file is also accessible as normal RAM memory from address $0000 to $001F.

AVR Programming model

The Status Register

The Status Register is actually part of I/O address space. The Status Register is not automatically saved or restored during interrupts. It's the responsibility of the programmer to do that.

I: Global Interrupt Enable

The Global Interrupt Enable bit must be set (one) for the interrupts to be enabled. The I-bit is cleared by hardware after an interrupt has occurred, and is set by the RETI instruction to enable subsequent interrupts.

T: Bit Copy Storage

The Bit Copy instructions BLD (Bit LoaD) and BST (Bit STore) use the T-bit as source and destination for the operated bit.

H: Half Carry Flag

The Half Carry Flag H indicates a half carry in some arithmetic operations which is useful for decimal arithmetic.

S: Sign Bit

The S-bit is always an Exclusive OR between the Negative Flag N and the Two's Complement Overflow Flag V.

V: Two's Complement Overflow Flag

The Two's Complement Overflow Flag V supports two's complement arithmetics.

N: Negative Flag

The Negative Flag N indicates a negative result in an arithmetic or logic operation.

Z: Zero Flag

The Zero Flag Z indicates a zero result in an arithmetic or logic operation.

C: Carry Flag

The Carry Flag C indicates a carry in an arithmetic or logic operation.

The Stack Pointer SP

The 16-bit Stack Pointer SPH:SPL is also located in I/O space. The Stack itself is located in SRAM. It grows down in memory when data or return addresses are pushed on the stack.
The Stack pointer always points to the next "free" byte of stack space. SPH:SPL is decremented after writing the data in SRAM during a PUSH. SPH:SPL is incremented before reading the data from SRAM during a POP. Two bytes are pushed on the stack during subroutine and interrupt calls. Two bytes are pulled from the stack during return from subroutines or return form interrupts.
On processors with 256 bytes or less of RAM only SPL is affected by Stack operations, in which case SPH can be used for other purposes.

The Program Counter

The program counter is 16-bits wide and can address up to 64k of instruction words. This means that the program counter always points to a pair of bytes. There is no way to increment the program counter to point to two half pairs.
It is possible for future expansions to use a 22-bit program counter, enabling the processor to address up to 4M words of flash memory. The CALL, JMP, EICALL and EIJMP instructions of the MegaAVR devices are capable of jumping across the entire 22-bit address space.

The I/O Memory space contains 64 addresses for CPU peripheral functions as Control Registers, Timer/Counters, A/D-convertors, and other I/O functions. The I/O Memory can be accessed directly (value must be between $00 and $3F), or as the Data Space locations following those of the Register File (value must be between $20 and $5F).
Please consult your device's document to learn how to use all the available I/O registers.

Timing

SB-Assembler Version 3 can show you the cycle times of each instruction when the TON list flag is switched on. The numbers presented are the number clock cycles the processor needs to execute the instruction.
Instruction times of some instructions may vary for a couple of reasons. It may vary because of the difference between 16 bit or 22 bit destinations, or it may vary because of a condition being true or false. Please consult the AVR documentation in those cases to be sure what times to use.

Reserved Words

Expect strange things to happen if you use one of the names below for self assigned labels:

  • Any name starting with R, followed by a number between 0 and 31 (e.g. R2, or R31).
  • The single characters X, Y or Z may also cause confusion to the SB-Assembler or yourself.

Target Files

I guess a little explanation about the target file behaviour of the AVR cross overlay is in place here. All supported target files can only store one byte per address in the file, whereas an AVR processor stores one word per address. This causes the target file address always to be double the Program Counter address. Easy enough, for as long as we are writing program instructions.
But what if we write a piece of data into our program memory? Blocks of data may save an odd number of bytes to the target file, which will have dramatic effects on the program if we continue with more instructions after the data block. Therefore a few precaution mechanisms are built into the AVR cross overlay.

Please note that the assembler only behaves differently while writing to code memory. EEPROM segments and RAM segments write in byte sized quantities, and are therefore not affected.

All AVR instructions always save a multiple of 16-bit words to the target file. The low byte of these 16-bit words is always saved to the even target address, the high byte is written to the next odd address. This is even true if a previous data generating directive stored an odd number of bytes to the code target file. In such a case an extra padding byte with the value of $00 is written to the target file to force the target address to become even again.

Only data generating directives can save an odd number of bytes to a code target file. Any other directive or instruction will cause a boundary sync, which will save this extra padding byte again if necessary.
Data generating directives do not cause a boundary sync. Therefore you can create a large contiguous data block by writing multiple lines containing data generating directives.

Assigning a label on a program line also causes an automatic boundary sync. Therefore you can only assign labels on word boundaries.
If you plan to address data in code memory in byte sizes be sure to multiply the associated label value by 2!

Please note that padding bytes will never be listed in the list file.

Also note that some AVR devices can handle programs that exceed the 64k byte limit of most file formats. So be sure to use a file format that supports a larger address range. Most programmers will accept the extended Intel HEX format (INS parameter).

.CS     Code Segment

Syntax:

        .CS

Function:

This directive switches the assembler to code segment, equivalent to the .SM CODE directive. Thus data, generated by other directives, is stored in the code segment, incrementing the program counter only every other byte (2 bytes are stored per memory location)!

Actually this directive is a left over from Version 2 to make the source files somewhat compatible with each other. So you might as well use the more common .SM CODE directive to select code memory.

Switching over to Code memory either way will cause a boundary sync to be executed. This means that if an odd number of bytes were saved to code memory, an extra padding byte with the value of $00 will be saved to make the number of bytes even again.

.DE     Define

Syntax:

LABEL   .DE    Rn

Function:

This directive assigns a name to a register, just like the AVR .DEF directive would have done. In fact the .DE directive is quite similar to the standard .SE directive. The only difference is that the .DE directive accepts only a legal register name as parameter (like R0 or R17), while the .SE directive accepts any legal expression.
This automatically means that you may reassign other values to the LABEL with subsequent .DE or .SE directives in your program.

.DS     Data Segment

Syntax:

        .DS

Function:

This directive switches the assembler to data segment, equivalent to the .SM RAM directive. Thus data, generated by other directives, is discarded because you can't program fixed data in RAM. Only the bytes are counted in order to assign labels to their proper address.
Instructions can not be decoded while in a data segment because you can't store instructions in RAM memory.

This directive is another leftover from Version 2 to make the source files of the 2 versions a bit more compatible. Therefore you might as well use the .SM RAM directive instead.

In RAM segment all bytes are counted as bytes, so words are counted as two separate bytes.

.ES     EEPROM Segment

Syntax:

         .ES

Function:

This directive switches the assembler to EEPROM segment, equivalent to the .SM EEPROM directive. Thus data, generated by other directives, is saved one byte at a time to the EEPROM segment. Words saved to the EEPROM segment are saved one byte at a time in little Endian model (low byte first).
Instructions can not be decoded while in EEPROM segment because you can't store instructions in EEPROM memory.

This directive is another leftover from Version 2 to make the source files of the 2 versions a bit more compatible. Therefore you might as well use the .SM RAM directive instead.

.EV     Even

Syntax:

        .EV

Function:

This directive will force the target file address counter to the next even address by adding a padding byte with the value $00 if necessary.

Explanation:

In program memory all instructions are stored as 16-bit words, thus they will always end on an even target address boundary.
Only data generating directives can cause an odd number of bytes to be saved to the code target file. You can now use the .EV directive to force the target address to become an even value again.
Assigning a new label in your source file will automatically realign the program counter to an even number of bytes. Writing instructions to the target file also realigns the program counter automatically if necessary.

Please note that all directives will also realign the program counter, except for the data generating directives. Data generating directives are the only directives which can create an odd number of bytes to be stored in code memory.

The use of the .EV directive was sometimes necessary in Version 2. In Version 3 the boundary sync is fully automated, which makes the .EV directive more or less obsolete.

.FA     Family

Syntax:

         .FA  TINY | AVR | MEGA | XMEGA

Function:

This directive is used to specify to which AVR family your target device belongs to.

Explanation:

Currently four main AVR families exist: TinyAVR, AVR, MegaAVR and XMega. The main difference between these families is the number of supported instructions and addressing modes. Naturally ROM, RAM, EEPROM and peripherals differ between the family members too, but that doesn't concern the assembler.
The AVR family is the traditional family of AVR processors, before the existence of the Tiny, Mega and XMega members were released.

Please refer to the product data sheet to see what instructions and addressing modes your device supports.
Devices of any of the 4 families don't necessarily have all the instructions of its family available. This is due to the evolution of the particular family. A nice overview of the differences can be found on Wikipedia, scroll to "Instruction set inheritance" to see the list.
So even though the assembler will accept all instructions of the selected family doesn't actually mean that your particular device can understand them all.

At first I thought it was a good idea to divide the AVR processors into these 4 families, until I learned that Atmel messed up the naming of their processors. For instance a device named ATtiny, might be a real tiny, a classic core or even a ATmega core. Big sigh! So here's the translation:

  • What the SB-Assembler calls TINY is listed on Wikipedia as "Reduced Core".
  • What the SB-Assembler calls AVR is listed on Wikipedia as "Minimal Core", and "Classic Core".
  • What the SB-Assembler calls MEGA is listed on Wikipedia as "Enhanced Core".
  • What the SB-Assembler calls XMEGA is listed on Wikipedia as "XMEGA Core".

I deliberately didn't split the families into individual members (devices), otherwise I would have to create a new cross overlay every time a new unsupported device was released by Atmel. This means that I didn't put any limitations on the amount of ROM, RAM and EEPROM you can address. Please refer to the product data sheet to see what the maximum available memory is on your target device and be sure not to use memory that isn't available.

The .FA directive is followed by the desired family member name.

If you fail to specify the device family an AVR Family not set. Assuming XMega family warning message will be given when the first instruction is decoded by the assembler. This warning message informs you that the XMega family is assumed per default.

.MS     Memory Size

Syntax:

        .MS  expression

Function:

This directive sets the maximum address value for the code segment. When properly set the SB-Assembler will warn the programmer when the program runs out of memory.
The memory size for RAM and EEPROM segments can not be set. It's the programmer's responsibility not to exceed the RAM or EEPROM limits.

Explanation:

The AVR devices come with a varying amount of Flash memory. It goes without saying that you can not run a program that needs more memory than what the selected device has to offer. Therefore it is a good idea to set the maximum amount of Flash memory in the beginning of your source file.
The expression should result in a value up to a maximum of $400000 (22-bit address pointer). In fact any value up until $400000 will be accepted by the assembler, even ridiculous values like 123, after all it's your party.

Per default the memory size is set to $400000 words.

Forward referenced labels are not allowed in the expression.

Be aware that the AVR cross assembler stores two bytes per instruction to the target file. This makes the target file twice the size of the maximum program counter!
Therefore a 64k words program will require a target file of 128k bytes. Be sure to select a target file format that can handle the required size if your device has a program memory capacity above 32k words.

Extra Error Messages

Only one extra warning message exists while the AVR cross overlay is loaded.

AVR Family not set. Assuming XMega family.

This warning is given while the first AVR mnemonic is decoded only when the programmer failed to specify the proper AVR family.
It is only a warning which is simply avoided by placing a .FA directive somewhere in your program header, after loading the AVR cross overlay and before you start using AVR instructions. As the warning implies, the XMega family is assumed if you fail to specify the proper device family.

Special Features

Target Files

Since the AVR stores 2 bytes at each state of the program counter and all target files store only one byte of code per address, there are some things to consider. Program words are stored in the target file with the low byte in the even address and the high byte in the following odd addresses.
Please note that this only applies to program Flash memory and not to EEPROM memory.
Most programmers use the Intel Hex format. Files intended for the program Flash by convention have the .HEX extension, while files intended for the EEPROM have the .EEP extension.

List Files

Other Atmel AVR assemblers produce a list file showing the high byte of the instruction words first. The SB-Assembler AVR cross will list the instruction words in the order in which they are stored in the target file, i.e. low byte first.

Predefined Names

I haven't included any header files with predefined register names for each AVR derivative. It is very easy to do that yourself if you want to. You may also set the device family using the .FA directive and the program address limit using the .MS directive in such a header files.

MOVW Instruction

The MOVW instruction, which is not available on all derivatives BTW, moves a register pair to another register pair. Each pair is always an even numbered register and its next odd numbered neighbour. For the SB-Assembler it doesn't matter which one of the two registers you specify in the operand field of the MOVW instruction.
Atmel's AVR assembler uses a syntax like MOVW R17:16,R1:0 . This syntax is not understood by the SB-Assembler. Simply forget the :16 and :0 and the SB-Assembler will know what you mean.

Overlay Initialization

Whenever the AVR cross overlay is (re)loaded it is initialized to the following settings:

  • The family is set to XMega
  • The maximum program memory is set to $3FFFFF
  • Code Segment is selected
  • Little Endian mode is selected

Differences Between Other Assemblers

There are some differences between the SB-Assembler and other assemblers for the AVR family of processors. These differences require you to adapt existing source files before they can be assembled by the SB-Assembler. This is not too difficult though and is the (small) price you have to pay for having a very universal cross assembler.

  • Don't forget to select the appropriate AVR family your processor belongs to, using the .FA directive.
  • Atmel tends to write register pairs as R25:24. The SB-Assembler wants you to use the even numbered register of a pair instead.
  • Some of the directives are unique to the SB-Assembler, allowing the SB-Assembler to do things other assemblers can't do.
  • The obvious differences in notation of directives common to all SB-Assembler crosses. Also the order in which labels, directives and operands appear differ from the original AVR assembler syntax.
  • Immediate addressing mode doesn't necessarily require the # prefix symbol to be used.
  • Don't forget that the SB-Assembler does not allow spaces in or between operands. Only Version 3 will allow one space after each comma separating operands in the operand field.

Useful Links

AVR Assembler The original AVR Assembler online documentation from Atmel.
AVR dude An open source AVR programming tool which runs on just about any platform and can control just about any programming device.
Fuse Calc An extremely useful on-line fuse bit calculator for the AVR controller family.
STK200 Build your own STK200 parallel port programmer.