After the basics of assembly language were explained in the first part, standard test programs are implemented in this part and the process is explained in detail.
A microcontroller with ATmega328P is required to implement the tutorials easily. Here we recommend a AZ-ATmega328 board with USB-C connection.
Assembly language
To begin with, you should familiarize yourself with the most important commands and the structure of the assembly language.
Commands
This is an overview of the most important Atmel AVR assembler commands:
Data & Register Operators |
|
ldi reg, data |
Loads the specified number into the register |
mov reg1, reg2 |
Copies the content of register2 to register1 |
in reg, IOreg |
Loads the byte of an IO register into a register |
out IOreg, reg |
Loads the byte of a register into the IO register |
clr reg |
Sets all bits of the register to '0' |
ser reg |
Sets all bits of the register to '1' |
sbi reg, bitPos |
Sets the bit of the specified position in the register to the value '1' |
cbi reg, bitPos |
Sets the bit of the specified position in the register to the value '0' |
inc reg |
Increases the numerical value of the content in the register by 1 (DEC) |
dec reg |
Decreases the numerical value of the content in the register by 1 (DEC) |
add reg1, reg2 |
Adds register2 to register1 |
sub reg1, reg2 |
Subtracts register2 from register1 |
|
|
Comparison operators |
|
cpi reg, data |
Compares a register with a constant value and stores the result in the zero flag of the status register |
cp reg, reg |
Compares two registers and stores the result in the zero flag of the status register |
brne label |
If the zero flag of the status register is set to false ('0') by a comparison, the specified label is executed |
breq label |
If the zero flag of the status register is set to true ('1') by a comparison, the specified label is executed |
|
|
Program structure Operators |
|
rcall label |
Calls a label with the option of returning to this point |
ret |
Can be called to return to the saved position of the program counter (PC) after a call |
rjmp label |
Jump to a label |
rcall, rjmp vs. call, jmp |
The prefix r stands for relative. The rough difference is that relative jumps or calls use less memory, but only have a limited range in the memory. The call/jmp instruction, on the other hand, has access to the entire memory. |
|
A complete overview of the commands can be found, for example, in the following Wikipedia article: Atmel AVR instruction set.
Instructions to the assembler
.equ
This can be used to declare a constant value in the program.
Comparable to const byte in the Arduino IDE.
.def
This command can be used to define a name for a register.
Comparable to #define in the Arduino IDE
.include
This is used to include external files in the code.
Comparable to #include in the Arduino IDE.
.org
This instruction is specifically in AVR Assembler to position the code at certain locations in the flash memory.
This is used, for example, at the beginning of every assembler program to start the program after the reset (0x0000) or to jump to the RESET label.
Label
Labels have already been mentioned several times in the command overview. These are sections in the code that can be jumped to using the program structure operators.
These are created in the code as follows:
labelname:
Command ; Comment
Command ; Comment
Command ; Comment
Register
In the ATmega328P there are some peripheral registers, for example for the GPIO ports, the interfaces such as SPI or UART, interrupts or timers. These are described in more detail in the examples.
An important register for understanding how the comparisons work is the status register SREG. This consists of several bits, also called flags here, which are used for operators such as the zero flag during comparison.
You can find a detailed overview of the status register in the ATmega328P data sheet on page 11.
Examples
In order to apply the theoretical knowledge you have acquired in practice, the following is the well-known Blink program that you have probably also loaded onto your UNO as the first program in the Arduino IDE.
LED Blink
This program is intended to make an LED flash.
The assembler program should follow the following sequence:
set the digital pin to 5V (HIGH)
Wait one second
Set digital pin to 0V (LOW)
Wait one second
Switch the IO pins on and off:

Figure 1: Complete pinout Source
In order to be able to control an IO pin of the microcontroller, the IO designation of the chip must first be determined with the help of the pinout.
For the Blink example, we recommend the onboard LED 'L', which is connected to D13. According to the pinout, this is PB5 of the ATmega328P. From this designation it can be determined that it is the 5th register bit of the IO register Port B is involved.
With the sbi and cbi command, this port can be controlled very easily:
sbi PORTB, 5 ; Switch on
Before the IO can be controlled, the pin function must be defined. The DDR register (DataDirectionRegister).
The IO can be set to output mode with the following command:
sbi DDRB, 5
Wait
There is no direct wait function in the assembly language, such as the delay() in the Arduino IDE.
One possible approach would be to block the program flow with instructions that consume a processor clock cycle and increase the content of a register accordingly in the meantime. As the microcontroller has a timer, it is more elegant to use this.
The timer increments its contents with each clock pulse, since the UNO uses a 16 MHz oscillator, this would be 16,000,000 increments per second. To reduce this, there is the so-called prescaler, which can be defined via register TCCR0B. If the maximum prescaler (f/1024) is set here, this corresponds to 15,625 clocks per second.
ldi r16, 0b00000101
out TCCR0B, r16
see data sheet p.87
Timer0 is only 8 bits long, which corresponds to a maximum numerical value of 255. This causes the timer to 'overflow' at clock pulse 256. With this overflow, the value is set to zero and an interrupt is triggered, which points to the memory address 0x0020 (data sheet p.49).
From this memory address, the .org command can then be used to jump to a code section in which this number of overflows is to be saved.
With a waiting time of one second, this number of overflows is approximately 61.
.org 0x0020
rjmp TIMER0OVF
delay:
clr overflows
sec_count:
cpi overflows,61
brne sec_count
ret
TIMER0OVF:
inc overflows
reti
The interrupt function must still be activated in the final program. This can simply be done at the beginning with the command
ldi r16, 0b00000001
sts TIMSK0, r16
be ; global interrupts (SREG)
can be executed.
Final code
Finally, the code sections are put together to create the final Blink program.
Insert these lines into a Blink.asm file.
Alternatively, the file can also be here can be downloaded here.
.include "./m328Pdef.inc" |
Finally, load the compiled .hex file onto the UNO. You can find the procedure in the first part of the blog series.
In this blog post, the basics of assembler programming were deepened and finally applied in the Blink example.
Further examples will be implemented in the following part.
Have fun rebuilding :)
2 Reacties
Bastian Brumbi
Hallo Bernd Buffen,
Sie haben recht cp ist für den Vergleich zweier Register, wogegen cpi ein Register mit einem Konstanten Wert vergleicht.
In der aktuellen Version ist dies nun korrigiert.
Vielen Dank für den Hinweis.
Grüße,
Bastian
Bernd
Hallo,
ist die Beschreibung von CP und CPI im Artikel nicht vertauscht ?
-———
Vergleichsoperatoren:
cp reg, data – Vergleicht ein Register mit einem Konstantwert und Speichert das Ergebnis im Zeroflag des Status Registers
cpi reg, reg -Vergleicht zwei Register und Speichert das Ergebnis im Zeroflag des Status Registers
Gruß
Bernd Buffen