ARM Assembly #6 - Arithmetic Operations
In this post, we’ll explore the basic arithmetic operations in ARM Assembly —
addition, subtraction, and their variants that include carry (C flag).
So far, we’ve learned how to move values into registers and how to shift them.
Now it’s time to perform actual arithmetic operations with those values.
Addition and Subtraction in ARMv4
ARM provides the following arithmetic instructions:
| Instructions | Descriptions |
|---|---|
add |
Add |
adc |
Add with carry |
sub |
Subtract |
sbc |
Subtract with carry |
rsb |
Reverse subtract |
rsc |
Reverse subtract with carry |
From now on, we’ll refer specifically to ARMv4, as this series is based on that architecture.
All these instructions are binary operations that take two operands:
result = first-operand op second-operand
<opcode> = <Rd>, <Rn>, <shifter-operand>
| Field | Meaning |
|---|---|
<opcode> |
Operations (e.g., add, sub) |
<Rd> |
Destination register (result) |
<Rn> |
First operand |
<shifter-operand> |
Second operand (immediate or shifted register) |
Just like other data processing instructions, these also use
the shifter operand mechanism introduced earlier.
1. ADD : Addition
Assembly Syntax: add <Rd>, <Rn>, <Shifter-Operand>
Methemetical form: C = A + B
C: <Rd>
A: <Rn>
B: <Shifter-Operand>
Unlike mathematics, where C = A + B and C = B + A are equivalent,
ARM assembly requires the first operand (<Rn>) to be a register,
so the order cannot be swapped.
Valid
add r0, r1, #2
Invalid
add r0, #2, r1 @ The first operand must be a register.
Example: ADD
add.s
.text
.global _start:
_start:
mov r0, #3
add r1, r0, #3
add r2, r0, lsl #2
b .Explanation:
mov r0, #3:R0 = 3
add r1, r0, #3:R1 = R0 + 3R1= 3 + 3R1 = 6
add r2, r0, r0 lsl #2:R2 = R0 + (3 << 2)R2 = 3 + (3 << 2)R2 = 3 + 12R2 = 15
Here, the second operand r0 lsl #2 is a shifter operand,
so ARM shifts r0 left by 2 bits before performing the addition.
2. SUB : Subtraction
Assembly Syntax: sub <Rd>, <Rn>, <Shifter-Operand>
Methemetical form: C = A - B
C: <Rd>
A: <Rn>
B: <Shifter-Operand>
sub subtracts the second operand from the first operand.
Example: SUB
sub.s
.text
.global _start:
_start:
mov r0, #4
sub r1, r0, #0x1
b .Explanation:
mov r0, #4:R0 = 4
sub r1, r0, #0x1:R1 = R0 - 0x1R1 = 4 - 1R1 = 3
3. RSB : Reverse Subtract
Assembly Syntax: rsb <Rd>, <Rn>, <Shifter-Operand>
Mathmetical form: C = B - A
C: <Rd>
A: <Rn>
B: <Shifter-Operand>
rsb performs subtraction in the reverse direction:
it subtracts the first operand from the second one.
RSB stands for Reverse Subtract.
Example: RSB
rsb.s
.text
.global _start:
_start:
mov r0, #5
mov r1, #2
rsb r2, r1, r0
b .Explanation:
mov r0, #5:R0 = 5
mov r1, #2:R1 = 2
rsb r2, r1, r0:R2 = R0 - R1R2 = 5 - 2R2 = 3
If sub r2, r1, r0 were used instead, the result would be -3.
4. ADC/SBC/RBC : Operations with Carry
So far, we’ve handled simple addition and subtraction. However, computers must also handle carry (C flag) — the overflow or borrow that occurs when results exceed bit limits.
ARM provides separate instructions for these cases:
| Instruction | Desciption |
| :-: | :-: |
| adc | Add with carry (includes carry flag) |
| sbc | Subtract with carry (includes borrow) |
| rsc | Reverse subtract with carry |
Example: ADC (Add with Carry)
adc.s
.text
.global _start
_start:
mov r0, #0x3
movs r0, r0, lsr #1
mov r1, #2
adc r2, r0, r1
b .
Explanation:
mov r0, #0x3R0 = 3
movs r0, r0, lsr #1R0 = R0 >> 1R0 = 3 >> 1R0 = 1- because of the
Ssuffix, the bit shifted out (1) sets the Carry flag.
mov r1, #2R1 = 2
adc r2, r0, r1R2 = R0 + R1 + CarryR2 = 1 + 2 + 1R2 = 4
GDB Debugging Output
(gdb) x/5i 0x10000
=> 0x10000 <_start>: mov r0, #3
0x10004 <_start+4>: lsrs r0, r0, #1
0x10008 <_start+8>: mov r1, #2
0x1000c <_start+12>: adc r2, r0, r1
0x10010 <_start+16>: b 0x10010 <_start+16>
(gdb) p ($cpsr >> 29) & 1
$1 = 0
(gdb) i r r0 r1 r2
r0 0x0 0
r1 0x0 0
r2 0x0 0
(gdb) si
0x00010004 in _start ()
(gdb) i r r0 r1 r2
r0 0x3 3
r1 0x0 0
r2 0x0 0
(gdb) p ($cpsr >> 29) & 1
$2 = 0 @ Carry flag = 0
(gdb) si
0x00010008 in _start ()
(gdb) p ($cpsr >> 29) & 1
$3 = 1
(gdb) i r r0 r1 r2
r0 0x1 1
r1 0x0 0
r2 0x0 0
(gdb) si
0x0001000c in _start ()
(gdb) i r r0 r1 r2
r0 0x1 1
r1 0x2 2
r2 0x0 0
(gdb) si
0x00010010 in _start ()
(gdb) p ($cpsr >> 29) & 1
$4 = 1 @ Carry flag = 1
(gdb) i r r0 r1 r2
r0 0x1 1
r1 0x2 2
r2 0x4 4
(gdb)
As shown, the Carry flag was set during the shift,
and adc correctly included it in the addition.
Summary
Here’s a quick summary of what we covered:
| Type | Instuctions | Description |
|---|---|---|
| Addition | add, adc |
adc adds with the carry flag |
| Subtraction | sub, sbc |
sbc subtracts with borrow |
| Reverse Subtraction | rsb, rsc |
Performs subtraction in reverse order |