Example Program #1: LED
Before diving into all the details of assembly, it might be helpful to look at a simple program to give you some context. For this program to work, an LED should be connected in series to PINB0 of your microcontroller.
Below is a simple program which will set the output of PINB0 high on an ATmega328P microcontroller.
.include "m328pdef.inc"
.cseg
.org 0x00
ldi r16,(1<<PINB0) ; load 00000001 into register 16
out DDRB,r16 ; write register 16 to DDRB
out PORTB,r16 ; write register 16 to PORTB
loop: rjmp loop ; stay in infinite loop
Code Breakdown
Just like when programming in C, assemblers have a directive to include the contents of another file in your code, written as "m328pdef.inc" which contains definitions specific to the microcontroller that we are using (of course if you are using a different microcontroller you will need a different include file). Note that in assembly, directives are preceded with a period. . In this case the file we include is
.include "m328pdef.inc"
The next few lines introduce the directives and .
.cseg
.org 0x00
The directive indicates that what follows is part of the code segment and should be placed in flash memory (as opposed to data memory or EEPROM).
Next we use the 0x00 to put our code at the beginning of flash memory. This ensures it is executed immediately when the microcontroller starts. directive to specify the origin of our code segment. In this case we specify the address
Note: By default, 0x00 is not strictly necessary. However, it is good practice to always specify the origin to make it obvious where the code is being placed. will start at the beginning of flash so the line
Now we get to our first instruction.
ldi r16,(1<<PINB0) ; load 00000001 into register 16
The instruction ldi - load immediate, lets us load an 8-bit constant value into one of the general purpose working registers between 16 and 31. The general purpose working registers are special memory locations directly connected to the microcontroller's arithmetic logic unit (ALU). We will have much more to cover later, but for now know that any time you need the CPU to operate on a value it must be placed in one of these registers. Here we use the ldi instruction to place the 8-bit value 00000001 into register 16.
Next we have the instruction out.
out DDRB,r16 ; write register 16 to DDRB
out PORTB,r16 ; write register 16 to PORTB
The instruction out lets us take a value loaded into one of the general purpose working registers and write it to one of the registers mapped to I/O memory. Here we write the contents of r16 (which is loaded with 00000001) to DDRB which will set PINB0 to output. We then write the same value to PORTB which will set the output of PINB0 high.
It is important to note that we cannot directly write a constant to an I/O register with the instruction out. For example, the following will not work
out DDRB,0b00000001 ; incorrect
Only certain instructions work with constants which will usually be indicated by the word immediate in the name. out can only take a general purpose register as an operand which is why we had to first load the value we wanted to write with out into r16.
Remember: Only certain instructions can use a constant as an operand. This will usually be indicated by the word immediate in the name.
Now we get to the final line of our program:
loop: rjmp loop ; stay in infinite loop
Here we define a label . This label will mark the address in code where it appears. Labels are always followed by a colon when they are defined.
The rjmp - relative jump. rjmp controls program flow by causing the microcontroller to jump to the address that's given as an operand. label is followed by the instruction
Here we set the label rjmp instruction, jump to the label, read the rjmp instruction again, jump the the label again - and so on ad infinitum. as the jump target. By doing this we have setup an infinite loop. In each cycle the microcontroller will read the
Conclusion
And there you have it! A simple assembly language program. We have seen how to use assembler directives like ldi and write values to I/O registers using the instruction out. Finally, we saw how to define labels and control program flow with the instruction rjmp. , and . We also saw how to load constants to general purpose working registers using the instruction
Now that we've seen an example program, let's take a closer look at the structure of an assembly language program.
Post a Comment