Hello! Welcome back to your interview preparation course.
In the last lesson, we covered the ARM Procedure Call Standard (AAPCS), which defines the "rules of the road" for how functions interact. We learned how arguments are passed, values are returned, and who is responsible for saving registers.
Now that we understand the rules, it's time to learn the language itself. This lesson will equip you with the fundamental vocabulary of ARM assembly: the instructions for moving data and performing basic calculations. Mastering these is the first and most crucial step toward writing and understanding any low-level code on an ARM processor.
1. The Foundation: A Load-Store Architecture
Before we dive into specific instructions, we must understand a core design principle of ARM processors: they have a load-store architecture. This might be different from other architectures you've encountered.
What does this mean?
- Data processing instructions (like
ADD,SUB,AND, etc.) can only operate on values held in registers or immediate (constant) values embedded directly in the instruction. They cannot directly access memory. - Memory access is restricted to a specific set of instructions, primarily
LDR(Load Register) andSTR(Store Register), which move data between memory and the processor's registers.
Think of it this way: to perform an operation on data that resides in memory, you must first explicitly load it into a register, perform the operation using that register, and then, if needed, explicitly store the result back to memory. This is a key principle of RISC (Reduced Instruction Set Computer) design, aiming for simpler, faster instructions.
For a brief overview of this concept, let's start with a short video clip.
ARM Assembly: Lesson 3 (LDR, STR)
The video 'ARM Assembly: Lesson 3 (LDR, STR)' by LaurieWired begins by clearly explaining the load-store architecture concept.
Please watch the first minute of the video (00:00 - 00:59).
2. Data Movement Instructions
Data movement instructions are the most frequently used in any program. They move data between registers, load immediate values, and transfer data to and from memory.
2.1 Moving Immediate Values into Registers: MOV
The MOV (Move) instruction is the simplest way to get a value into a register. It's typically used to load a constant or copy a value from one register to another.
The basic syntax is:MOV <DestinationRegister>, <Source>
The source can be an immediate value (a constant) or another register.
MOV R0, #42: Loads the decimal value 42 into register R0. The#symbol signifies an immediate value.MOV R1, R0: Copies the content of register R0 into register R1.
Let's see this in action.
ARM Assembly: Lesson 1 (MOV, Exit Syscall)
The video 'ARM Assembly: Lesson 1 (MOV, Exit Syscall)' by LaurieWired provides a clear, practical demonstration of the MOV instruction.
Please watch from 10:50 to 12:35 to see the syntax for moving an immediate value, and then from 15:59 to 16:47 to see how the register values change in the debugger.
2.2 Moving Data To and From Memory: LDR and STR
As we established, LDR and STR are the gateways to memory.
LDR(Load Register): Reads data from a memory location into a register.STR(Store Register): Writes data from a register to a memory location.
The most common way to specify the memory address is register indirect addressing, where a register holds the memory address you want to access. The syntax uses square brackets [] to denote that you are accessing the memory at the address held in the register.
LDR R1, [R0]: Reads a 32-bit word from the memory address stored inR0and places it intoR1.STR R1, [R0]: Writes the 32-bit value fromR1to the memory address stored inR0.
Given your C programming background, you can think of [R0] as dereferencing a pointer stored in R0.
To really understand LDR and STR, we need to see them in a complete context, which includes defining data in memory, getting its address, and then operating on it. The following resource walks through this entire process.
ARM Assembly: Lesson 3 (LDR, STR)
Let's return to 'ARM Assembly: Lesson 3 (LDR, STR)' to see a full demonstration of memory access.
Please watch from 01:21 to 14:23. This is a crucial section that covers: Defining data in memory using .data and .word directives. Loading an address: Using LDR R0, =var1 to get the memory address of var1 into R0. Note the = sign; this is a pseudo-instruction that the assembler uses to load an address. Loading a value: Using LDR R1, [R0] to dereference the address in R0 and load the actual value from memory into R1. Storing a value: Using STR R2, [R3] to store a new value back into a memory location.
Test your understanding!
Consider the following assembly snippet:
.data
my_var: .word 100
.text
.global _start
_start:
LDR R0, =my_var ; Instruction 1
LDR R1, [R0] ; Instruction 2
After these two instructions execute, what are the values in registers R0 and R1?
Show answer
- R0 will contain the memory address of
my_var. - R1 will contain the value
100.
Instruction 1 is a pseudo-instruction that loads the address of the label my_var into R0. Instruction 2 then uses that address to dereference memory and load the 32-bit word stored at that location (which is 100) into R1.
3. Arithmetic and Logical Instructions
These are the core data-processing instructions. They follow a very regular format, typically taking two source operands and placing the result in a destination register.
For a comprehensive reference on the syntax and semantics of these instructions, the following document is very useful.
The document 'ARMR Assembly Language' provides concise tables summarizing the basic data processing instructions.
Please read section 4.2.1, 'Simple Data Processing Instructions' (pages 140-142). Focus on the tables for 'Register Transfer Instructions', 'Arithmetic Instructions', and 'Logical Instructions'. This will formally introduce MOV/MVN, ADD/SUB, and AND/EOR/ORR/BIC.
Let's highlight the most common ones you'll use.
3.1 Arithmetic Operations: ADD and SUB
ADD Rd, Rn, Rm:Rd = Rn + RmADD Rd, Rn, #imm:Rd = Rn + immediate_valueSUB Rd, Rn, Rm:Rd = Rn - RmSUB Rd, Rn, #imm:Rd = Rn - immediate_value
Example:
To implement x = a + b where a is in R1 and b is in R2:ADD R0, R1, R2 ; Result x is placed in R0
3.2 Logical Operations: AND and ORR
These bitwise operations are fundamental for low-level driver development, as they are used to manipulate individual bits in hardware control registers.
AND Rd, Rn, Rm: Performs a bitwise AND betweenRnandRm, stores result inRd.ORR Rd, Rn, Rm: Performs a bitwise OR betweenRnandRm, stores result inRd.
As with arithmetic instructions, the second source operand can also be an immediate value.
Practical Use Case: Register Modification
Your work in driver development frequently involves read-modify-write cycles on peripheral registers. AND and ORR are the tools for this at the assembly level.
-
To clear a bit (masking): Use
AND. To clear bit 4 of a registerR1, you wouldANDit with a mask where bit 4 is 0 and all other bits are 1.AND R1, R1, #0xFFFFFFEF; Clears bit 4 of R1 -
To set a bit: Use
ORR. To set bit 4 ofR1, you wouldORRit with a mask where bit 4 is 1 and all other bits are 0.ORR R1, R1, #0x00000010; Sets bit 4 of R1
4. Putting It All Together: A Simple Function
Let's combine what we've learned with the AAPCS rules from the previous lesson to write a complete assembly function.
C Prototype: int calc(int a, int b);
Functionality: return (a + 5) - b;
Here's the ARM assembly implementation:
.global calc // Export the 'calc' symbol to be visible to the linker
calc:
// Function Prologue - not strictly needed here as we don't call
// other functions and only use caller-saved registers, but good practice.
// Function Body
ADD R0, R0, #5 // R0 = a + 5. Per AAPCS, 'a' is in R0.
SUB R0, R0, R1 // R0 = (a + 5) - b. Per AAPCS, 'b' is in R1.
// Function Epilogue
BX LR // Return to caller. The result is already in R0,
// as required by the AAPCS.
This simple function demonstrates:
- Following the AAPCS for input arguments (
R0,R1) and the return value (R0). - Using an
ADDinstruction with an immediate value. - Using a
SUBinstruction with a register operand. - Correctly returning using
BX LR.
Conclusion
In this lesson, we've covered the essential building blocks of ARM assembly programming. You now have the vocabulary to move data and perform fundamental computations.
Key Takeaways:
- ARM is a load-store architecture: operations happen on registers, and
LDR/STRare used for memory access. MOVis for loading immediate values or copying between registers.LDRloads from memory into a register;STRstores from a register into memory. The[Rx]syntax is key for dereferencing.ADDandSUBperform basic arithmetic.ANDandORRare crucial for bitwise manipulation, a cornerstone of driver development.
Next Lesson Preview:
A program isn't just a linear sequence of calculations. It needs to make decisions and repeat actions. In our next lesson, we will explore control flow instructions. We will learn how to use CMP (Compare) and TST (Test) to set processor flags, and then use conditional branch instructions (B<cond>, BL<cond>) to create loops and if-then-else structures.