4. Advanced Arithmetic Operations
As a refresher, here are some fundamental AVR arithmetic and logical instructions.
| Mnemonic | Operand | Description | Example | Notes |
|---|---|---|---|---|
| ADD | Rd, Rr | Add | ADD R1, R2 |
Rd = Rd + Rr |
| ADC | Rd, Rr | Add with Carry | ADC R1, R2 |
Rd = Rd + Rr + C (Carry flag) |
| ADIW | Rd, K | Add Immediate to Word | ADIW R24, 40 |
R[d+1]:Rd = R[d+1]:Rd + K |
| SUB | Rd, Rr | Subtract | SUB R16, R17 |
Rd = Rd - Rr |
| SBC | Rd, Rr | Subtract with Carry | SBC R16, R17 |
Rd = Rd - Rr - C |
| SUBI | Rd, K | Subtract Immediate | SUBI R16, 67 |
Rd = Rd - K |
| SBIW | Rd, K | Substract Immediate from Word | SBIW R24, 40 |
R[d+1]:Rd = R[d+1]:Rd - K |
| SBCI | Rd, K | Subtract Immediate with Carry | SBCI R17, 0 |
Rd = Rd - K - C |
| INC | Rd | Increment | INC R16 |
Rd = Rd + 1 |
| DEC | Rd | Decrement | DEC R16 |
Rd = Rd - 1 |
| MUL | Rd, Rr | Multiply Unsigned | MUL R16, R17 |
R1:R0 = Rd × Rr (16-bit result) |
| MULS | Rd, Rr | Multiply Signed | MULS R16, R17 |
R1:R0 = Rd × Rr (signed) |
| NEG | Rd | Negate (Two's Complement) | NEG R16 |
Rd = 0x00 - Rd |
| AND | Rd, Rr | Logical AND | AND R1, R2 |
Rd = Rd AND Rr |
| ANDI | Rd, K | AND Immediate | ANDI R16, 0x0F |
Rd = Rd AND K |
| OR | Rd, Rr | Logical OR | OR R1, R2 |
Rd = Rd OR Rr |
| ORI | Rd, K | OR Immediate | ORI R16, 0x80 |
Rd = Rd OR K |
| EOR | Rd, Rr | Exclusive OR | EOR R16, R17 |
Rd = Rd XOR Rr |
| COM | Rd | One's Complement | COM R16 |
Rd = 0xFF - Rd (inverts all bits) |
| NEG | Rd | Two's Complement | NEG R16 |
Rd = -Rd (Signed) |
| CLR | Rd | Clear Register | CLR R16 |
Rd = 0 |
| SER | Rd | Set Register | SER R16 |
Rd = 0xFF (R16-R31 only) |
Register Pair Arithmetic
To perform arithmetic operations with 16 bit words (0 - 65535), we can utilize the Carry Flag to perform ripple carry arithmetic operations.
16 Bit Word Addition
Suppose you want to add two big 16 bit numbers 26983 and 4882 stored in two register pairs R16:R17 and R18:R19. You can add the lower byte first then add the higher byte with a carry addition.
; DEC 26983 = HEX 0x6967
LDI R16, 0x69 ; upper byte
LDI R17, 0x67 ; lower byte
; DEC 4882 = HEX 0x1312
LDI R18, 0x13 ; upper byte
LDI R19, 0x12 ; lower byte
ADD R17, R19 ; add lower byte
ADC R16, R18 ; add upper byte + carry from lower
; R16:R17 = 31865
16 Bit Word Substraction
A similar thing can be done for substracting two 16 bit numbers.
; DEC 26983 = HEX 0x6967
LDI R16, 0x69 ; upper byte
LDI R17, 0x67 ; lower byte
; DEC 4882 = HEX 0x1312
LDI R18, 0x13 ; upper byte
LDI R19, 0x12 ; lower byte
SUB R17, R19 ; substract lower byte
SBC R16, R18 ; substract upper byte - borrow (C) from lower
; R16:R17 = 22101
Immediate Arithmetic
There are 3 immediate arithmetic instructions: ADIW, SBIW, and SUBI. In AVR there is no "Add with immediate" instruction. To do so, you can utilize the SUBI Rd, K instruction to substract a negative immediate number to achieve addition.
Suppose you want to add 67 to 61 stored in R16. You can do the following
LDI R16, 61
SUBI R16, -67 ; - -61 = + 61
; R16 = 128
Notice how there are only immediate instructions for word addition and substraction? Recall that word immediate addressing (ADIW & SBIW) can only operate on 6 bit values (0 - 63). How can we perform immediate arithmetic operations on bigger numbers like 26983?
Word Immediate Arithmetic
To perform addition/substraction operation with immediate numbers greater than 63, we simply split the word operation into two byte immediate operations similar to the previously explained 16 bit operations.
16 Bit Immediate Addition
; DEC 4882 = HEX 0x1312
LDI R16, 0x13 ; upper byte
LDI R17, 0x12 ; lower byte
.EQU num, 0x5457 ; immediate constant DEC 21591
SUBI R17, lo8(-num) ; 0x12 - -0x57 = 0x12 + 0x57 ; add lower byte
SBCI R16, hi8(-num) ; 0x13 - -0x54 - C = 0x13 + 0x54 + C ; add upper byte
; R16:R17 = 26473
16 Bit Immediate Substraction
LDI R16, 0x67 ; yeah you get the idea atp
LDI R17, 0x69
.EQU num, 0x1312
SUBI R17, lo8(num) ; this time we simply just substract it
SBCI R16, hi8(num) ; no need to be negative :)
; R16:R17 = 26983
Multiplying Numbers
There are multiple multiplication instructions in AVR, each supporting different data formats such as MUL Rd, Rr for unsigned numbers, MULS Rd, Rr for signed numbers, and MULSU Rd, Rr for multiplying a signed with an unsigned number.
Generally, the way they behave are similar: they multiply Rd with Rr then storing a 16 bit result in R1:R0.
LDI R16, 23
LDI R17, 9
MUL R16, R17
; R1:R0 = 23 * 9 = 207 (16 bits wide)