Mastering ARM Assembly: Instructions & Programming Examples
ARM Assembly Programming Examples
Sum of First 10 Integers
This ARM assembly program calculates the sum of the first 10 natural numbers.
AREA INTSUM, CODE, READONLY
ENTRY
MOV R0, #10 ; Initialize counter R0 with 10
MOV R1, #0 ; Initialize sum R1 with 0
MOV R2, #1 ; Initialize current number R2 with 1
LOOP
ADD R1, R1, R2 ; Add current number (R2) to sum (R1)
ADD R2, #1 ; Increment current number (R2)
SUBS R0, #1 ; Decrement counter (R0) and update flags
BNE LOOP ; If R0 is not zero, branch back to LOOP
HERE
B HERE ; Infinite loop to stop execution
END
Factorial Calculation in ARM Assembly
This program computes the factorial of a number (e.g., 5!) using ARM assembly instructions.
AREA FACT, CODE, READONLY
ENTRY
MOV R0, #5 ; Number for which to calculate factorial (e.g., 5)
MOV R1, #1 ; Initialize result R1 with 1
CMP R0, #0 ; Compare R0 with 0
BEQ STOP ; If R0 is 0, branch to STOP (factorial of 0 is 1)
MOV R1, R0 ; If R0 is not 0, initialize R1 with R0 (e.g., 5)
FACT
SUBS R0, #1 ; Decrement R0 (e.g., 4, 3, 2, 1)
CMP R0, #0 ; Compare R0 with 0
BEQ STOP ; If R0 is 0, branch to STOP
MUL R2, R1, R0 ; Multiply R1 (current product) by R0 (current number)
MOV R1, R2 ; Move the product to R1
B FACT ; Branch back to FACT
STOP
NOP ; No operation, program stops here
HERE
B HERE ; Infinite loop to stop execution
END
Summing 10 Integers from an Array
This ARM assembly code demonstrates how to sum 10 integer numbers stored in a data array.
AREA RESET, CODE, READONLY
ENTRY
MOV R1, #10 ; Initialize counter R1 with 10 (number of elements)
LDR R2, =ARRAY ; Load the base address of ARRAY into R2
MOV R4, #0 ; Initialize sum R4 with 0
NEXT
LDR R3, [R2], #4 ; Load word from memory at R2 into R3, then increment R2 by 4
ADD R4, R4, R3 ; Add the loaded value (R3) to the sum (R4)
SUBS R1, R1, #1 ; Decrement counter R1 and update flags
BNE NEXT ; If R1 is not zero, branch back to NEXT
STOP
B STOP ; Infinite loop to stop execution
ARRAY
DCD 12, 15, 3, 1, 7, 8, 9, 4, 6, 10 ; Define constants (the array elements)
END
ARM Assembly Program: Multiply Two 16-bit Numbers
An ARM assembly language program (ALP) to multiply two 16-bit binary numbers.
AREA MULTIPLY, CODE, READONLY
ENTRY
MOV R1, #0X6400 ; Load first 16-bit number (25600 decimal) into R1
MOV R2, #0X3200 ; Load second 16-bit number (12800 decimal) into R2
MUL R3, R1, R2 ; Multiply R1 by R2, store result in R3
HERE
B HERE ; Infinite loop to stop execution
END
ARM Instruction Set: Data Processing & Control Flow
ARM Data Processing: Arithmetic & Logical Instructions
This section discusses the arithmetic and logical instructions within the ARM data processing instruction set, crucial for mathematical operations and bit manipulation.
1. Arithmetic Instructions
These instructions are used to perform mathematical operations like addition, subtraction, and their variants with carry.
ADD
: Add two numbers.
Example:ADD R0, R1, R2
→R0 = R1 + R2
ADC
: Add with Carry. Adds two numbers plus the carry bit from a previous operation.SUB
: Subtract.
Example:SUB R0, R1, R2
→R0 = R1 - R2
SBC
: Subtract with Carry. Subtracts considering the carry (borrow) bit.RSB
: Reverse Subtract.
Example:RSB R0, R1, R2
→R0 = R2 - R1
RSC
: Reverse Subtract with Carry.
Example:RSC R0, R1, R2
→R0 = R2 - R1 - Carry
2. Logical Instructions
These instructions are used to manipulate individual bits in data, which is very common in embedded systems for checking, setting, or clearing specific bits.
AND
: Bitwise AND.
Example:AND R0, R1, R2
→R0 = R1 & R2
ORR
: Bitwise OR.
Example:ORR R0, R1, R2
→R0 = R1 | R2
EOR
: Bitwise Exclusive OR (XOR).
Example:EOR R0, R1, R2
→R0 = R1 ^ R2
BIC
: Bit Clear (AND with NOT). Clears specific bits.
Example:BIC R0, R1, R2
→R0 = R1 & (~R2)
MVN
: Move NOT. Performs bitwise negation.
Example:MVN R0, R1
→R0 = ~R1
Essential ARM Instructions: MOV, CMP, MUL
A discussion of fundamental ARM instructions: MOV
for data transfer, CMP
for comparisons, and MUL
for multiplication.
1. MOV Instruction (Move Data)
The MOV
instruction is used to copy a value into a register.
- Syntax:
MOV destination_register, source
- Example:
MOV R0, #10
(Copies the immediate value 10 into register R0)
2. Comparison Instructions
Comparison instructions are used to compare values. They do not store a result in a register but update the status flags (e.g., N, Z, C, V) in the CPSR (Current Program Status Register). These flags are then used for conditional operations like if
statements or while
loops.
- Example:
CMP R1, R2
(Compares the value in R1 with R2, setting flags based onR1 - R2
)
3. Multiply Instruction
The MUL
instruction is used to multiply two values stored in registers.
- Example:
MUL R0, R1, R2
(Multiplies the value in R1 by R2, storing the 32-bit result in R0)
ARM Multiple Register Transfer: LDM & STM
These instructions efficiently move multiple data values between registers and memory with a single instruction. They are particularly useful for saving or loading a group of registers at once, such as during function calls or context switching.
LDM – Load Multiple Registers
LDM
(Load Multiple) loads several registers from memory.
- Syntax:
LDM Rn, {Rlist}
- Example:
LDM R0, {R1, R2, R3}
(Loads values from memory starting at the address in R0 into registers R1, R2, and R3)
STM – Store Multiple Registers
STM
(Store Multiple) stores several registers into memory.
- Syntax:
STM Rn, {Rlist}
- Example:
STM R5, {R1, R2, R3}
(Stores the values from registers R1, R2, and R3 into memory starting at the address in R5)
ARM Branch Instructions: Control Program Flow
Branch instructions are fundamental for controlling the flow of a program in ARM assembly, enabling jumps from one part of the code to another. They are essential for implementing loops, conditional statements, and function calls.
Basic Branch (B)
The basic B
instruction performs an unconditional jump to a specified label.
- Syntax:
B label
- Example:
B loop_start
(Jumps to the instruction atloop_start
)
Branch with Condition (B<condition>)
ARM allows conditional branches based on the status flags set by previous operations (e.g., CMP
, SUBS
). If the condition is met, the branch occurs; otherwise, execution continues to the next instruction.
- Syntax:
B<condition> label
- Common Conditions:
EQ
(Equal),NE
(Not Equal),GT
(Greater Than),LT
(Less Than),GE
(Greater Than or Equal),LE
(Less Than or Equal) - Example:
CMP R0, #5 BEQ found ; If R0 equals 5, branch to 'found'
Branch with Link (BL) for Function Calls
The BL
(Branch with Link) instruction is used for calling subroutines or functions. It saves the return address (the address of the instruction immediately following BL
) in the Link Register (LR
or R14
) before branching. This allows the function to return control to the caller using MOV PC, LR
or BX LR
.
- Syntax:
BL function_name
- Example:
BL add_numbers
(Calls theadd_numbers
subroutine)
Branch and Exchange (BX) for Mode Switching
The BX
(Branch and Exchange) instruction is used to branch to the address stored in a register. It is also commonly used for switching between ARM and Thumb instruction sets, as well as returning from subroutines when the return address is in LR
.
- Syntax:
BX register
- Example:
BX LR
(Branches to the address in the Link Register, often used for function returns)
ARM Multiple Register Transfer: LDM & STM (Continued)
This section reiterates the functionality of multiple register transfer instructions, which are vital for efficient data movement between the ARM processor’s registers and memory.
LDM – Load Multiple Registers
LDM
(Load Multiple) loads several registers from memory in a single operation.
- Syntax:
LDM Rn, {Rlist}
- Example:
LDM R0, {R1, R2, R3}
(Loads values from memory starting at the address in R0 into registers R1, R2, and R3)
STM – Store Multiple Registers
STM
(Store Multiple) stores the contents of several registers into memory.
- Syntax:
STM Rn, {Rlist}
- Example:
STM R5, {R1, R2, R3}
(Stores the values from registers R1, R2, and R3 into memory starting at the address in R5)
ARM Single Register Transfer: LDR & STR
In ARM architecture, single register load and store instructions are used to move individual data values between memory and registers. These are fundamental for accessing data stored in RAM.
- Load: Copies data from a specific memory address into a register.
- Store: Copies data from a register to a specific memory address.
These are called Single Register Transfer instructions because only one register is involved in the data transfer at a time.
LDR – Load Register Instruction
LDR
(Load Register) is used to load a value (typically a word, 32-bit) from memory into a register.
- Syntax:
LDR Rd, [Rn]
(Loads the word at the address in Rn into Rd) - Example:
LDR R0, [R1]
(Loads the value from the memory address pointed to by R1 into R0)
STR – Store Register Instruction
STR
(Store Register) is used to store a value from a register into memory.
- Syntax:
STR Rd, [Rn]
(Stores the word from Rd to the memory address in Rn) - Example:
STR R2, [R3]
(Stores the value from R2 into the memory address pointed to by R3)
ARM Stack Operations: Pushing & Popping Data
A stack is a crucial memory area used for temporary data storage, including return addresses for subroutines, local variables, and saving/restoring register values. It operates on a LIFO (Last In, First Out) principle. The Stack Pointer (SP
, typically R13
) register always points to the current top of the stack.
Pushing Data to Stack (Store)
To store multiple registers onto the stack, typically using a Full Descending (FD
) stack model where the stack grows downwards (towards lower memory addresses) and the stack pointer points to the last item pushed.
- Syntax:
STMFD sp!, {registers}
(!
indicates write-back toSP
) - Example:
STMFD SP!, {R4, R5, LR}
(Pushes R4, R5, and LR onto the stack, decrementing SP after each push)
Popping Data from Stack (Load)
To load multiple registers from the stack, reversing the push operation. For a Full Descending stack, this means incrementing the stack pointer after each load.
- Syntax:
LDMFD sp!, {registers}
(!
indicates write-back toSP
) - Example:
LDMFD SP!, {R4, R5, PC}
(Pops R4, R5, and PC from the stack, incrementing SP after each pop. Loading PC effectively returns from a subroutine.)
ARM Software Interrupt (SWI) Instruction
A software interrupt (SWI
) is a special instruction that signals the processor to temporarily halt its current execution and transfer control to a predefined handler routine, typically part of the operating system (OS) or a firmware service. It’s used to request system-level functions like input/output operations or memory management, allowing user applications to access privileged services in a controlled manner.
Working Principle
- The
SWI
instruction is executed in a program. - The processor:
- Saves the current program status (CPSR) in SPSR (Saved Program Status Register).
- Stores the return address (address of the instruction after
SWI
) in the Link Register (LR
). - Switches to Supervisor mode (a privileged processor mode).
- Jumps to a predefined memory address where the
SWI
handler (a part of the OS) is located.
- The
SWI
handler performs the requested task based on theSWI
number. - Once done, it returns control to the original program using the saved return address.
- ARM Instruction:
SWI <number>
- Example:
SWI 0x01
(Calls the system service associated with interrupt number 0x01)
Loading Constants in ARM Assembly
In ARM programming, it’s often necessary to load a fixed value (a constant) into a register. This process is known as loading a constant, and ARM provides several methods depending on the size and nature of the constant.
1. Using the MOV Instruction
The MOV
instruction can directly load certain constant values into a register. However, due to the ARM instruction format, MOV
can only handle immediate values that can be represented by an 8-bit number rotated by an even number of bits. This limits the range of constants that can be loaded directly.
- Syntax:
MOV Rd, #constant
- Example:
MOV R0, #100
(Loads the immediate value 100 into R0)
2. Using the MVN Instruction (Move NOT)
The MVN
instruction performs a bitwise negation of its operand before moving it to the destination register. This can be useful for loading constants that are the bitwise complement of a small immediate value, effectively allowing a wider range of constants to be loaded in a single instruction.
- Syntax:
MVN Rd, #constant
- Example:
MVN R0, #0
(Loads~0
, which is0xFFFFFFFF
, into R0)
3. Using LDR Pseudo-instruction
If a constant is too large or complex to be loaded directly with MOV
or MVN
, the assembler provides the LDR
pseudo-instruction. The assembler will automatically convert this into a valid instruction sequence, typically by placing the constant in a literal pool (a section of memory near the code) and then using a true LDR
instruction to load it from memory.
- Syntax:
LDR Rd, =constant
- Example:
LDR R0, =0xFF000000
(Loads the large constant0xFF000000
into R0)