๐ This is our starting point. Let's analyze it line by line.
.org 0 tells the assembler: "put the next code at address 0 of the microcontroller's memory".
At address 0 is the reset vector: when Arduino powers up or resets, it starts here.
rjmp init is a relative jump to the init label. It's used to skip the interrupt vector table that occupies the first addresses.
Imagine memory is a long hallway with many doors. Door 0 is the main entrance. .org 0 says: "let's put a sign at the first door". rjmp init is like saying: "don't enter here, go directly to door number 0x68, where the party is".
sbi 4,5 means: go to the register at address 4 (which we call DDRB) and set bit number 5 to 1.
Bit 5 of DDRB corresponds to physical pin PB5, which on Arduino is pin D13 (the built-in LED).
Setting a bit to 1 in DDRB configures that pin as OUTPUT. It can then send current (turn on LED) or be off.
โ Only bit 5 is 1 โ only pin D13 is in OUTPUT mode. Other pins are in INPUT (high impedance).
sbi 5,5 = register 0x05 (PORTB), bit 5 = 1 โ pin D13 becomes HIGH (5V), LED turns on.
cbi 5,5 = same register, bit 5 = 0 โ pin D13 becomes LOW (0V), LED turns off.
Note: if we hadn't set DDRB first, these instructions would have no effect (the pin would be in INPUT).
โก Pin D13 now has voltage! The onboard LED turns on.
rcall is a subroutine call. It saves the return address on the stack and jumps to the wait1 label. When it encounters ret, it returns.
ldi r17,32 loads value 32 into register r17. This is the outer loop.
Then we enter wait:. Note that r16 is not initialized! The first time the program executes dec r16, r16 is 0 (registers start at 0 on power-up).
dec r16 decrements: 0 - 1 = 255 (because 8 bits go from 0 to 255, below 0 wraps to 255). Then it continues 254, 253... until 0.
When r16 reaches 0, brne wait doesn't jump (because zero = false condition) and we go to dec r17.
r17 is decremented: if not zero, we go back to wait and r16 restarts at 0 (because we never changed it except with dec). So the inner loop repeats 32 times.
After init: we have DDRB configuration (sbi 4,5). Then on, wait, off, wait, and rjmp init.
Note: rjmp init jumps to the beginning, so it reconfigures DDRB every cycle. It's not necessary, but doesn't hurt: setting a bit that's already 1 changes nothing. It's just a bit redundant.
Result: the LED blinks continuously, with a delay between ON and OFF and between OFF and ON.
In the original code, r16 is never initialized. It works because the first time it starts at 0, but it's a bad practice: in more complex programs it could contain any value.
Now the behavior is deterministic and the code is more robust.
Now that you understand the basics, you can explore other projects:
Question: What happens if I omit sbi 4,5 and only do sbi 5,5?
The LED won't turn on. Because sbi 4,5 configures the pin as OUTPUT. If the pin is in INPUT, sbi 5,5 enables the internal pull-up resistor, but doesn't send enough current to light the LED (and the pin isn't in output mode anyway).
| Instruction | Meaning |
|---|---|
.org N |
Put next code at address N |
rjmp label |
Relative jump to label |
sbi address, bit |
Set Bit in I/O register |
cbi address, bit |
Clear Bit in I/O register |
rcall label |
Call subroutine (saves return address) |
ret |
Return from subroutine |
ldi reg, value |
Load immediate value into register (r16-r31) |
dec reg |
Decrement register |
brne label |
Branch if Not Equal (jump if last operation didn't result in zero) |