Post

5. Memory Access & Addressing Modes

5. Memory Access & Addressing Modes

Immediate, Register, and Offset Addressing

ARM provides several ways to access memory. These addressing modes are used to specify how the memory address for a load/store instruction is computed.

Addressing ModeDescriptionExample
Immediate AddressingThe memory address is determined by an immediate value. This is often used for loading constants or small values directly.LDR R0, =0x1000 (Load the address 0x1000 into R0)
Register AddressingThe memory address is computed using the value stored in a register. This is useful when accessing data that is already in a register.LDR R0, [R1] (Load the value from the memory address in R1 into R0)
Offset AddressingThe memory address is calculated by adding an offset (either an immediate value or a register value) to the base address stored in a register.LDR R0, [R1, #4] (Load the value from the address R1 + 4 into R0)

Immediate Addressing: In this mode, the address is directly encoded as an immediate value. This is useful for small constants or for loading data into a register.

Example:

1
LDR R0, =0x2000    // Load the address 0x2000 into R0

Register Addressing: The address is stored in a register, and the data is loaded from that address. This is the most straightforward form of memory access.

Example:

1
LDR R0, [R1]      // Load value from address in R1 into R0

Offset Addressing: The offset (a constant or register) is added to the base address in the register to calculate the memory address.

Example:

1
LDR R0, [R1, #4]  ; Load value from address R1 + 4 into R0

Pre-indexing vs. Post-indexing

In ARM, pre-indexing and post-indexing refer to two distinct methods of calculating the memory address and updating the base register. These modes are used primarily in load/store operations, offering flexibility in how memory is accessed and how the base register is updated.

Pre-indexing
  • The pre-indexing mode adds the offset to the base register before accessing memory and updates the base register with the new address after the memory access. The ! symbol at the end of the address expression indicates that the base register is updated after the access.

Example:

1
LDR R0, [R1, #4]!  ; Load value from memory at address R1 + 4 into R0, then update R1 to R1 + 4

In this case, the address used for the load is R1 + 4, and once the memory is accessed, the base register (R1) is incremented by 4.

Post-indexing
  • In post-indexing, the memory address is calculated using the original base register value (before any offset is added). The base register is updated after the memory access, meaning the memory access happens first, and the register is updated afterward.

Example:

1
LDR R0, [R1], #4  ; Load value from address in R1 into R0, then update R1 to R1 + 4

In this case, the load instruction uses the address in R1 to load data into R0. After the load operation, R1 is updated by adding the offset #4.

Load/Store Multiple (LDMDB, STMIA)

ARM provides special instructions for transferring multiple registers to and from memory at once. These instructions are highly useful in situations where several registers need to be saved or restored, such as during function calls, interrupt handling, or context switching.

LDMDB (Load Multiple Decrement Before)
  • LDMDB is used to load multiple registers from memory. The decrement before mode means that the base address is decremented before each load operation, which is particularly useful when loading multiple values from a stack or restoring saved register states in reverse order.

Example:

1
LDMDB R0!, {R1, R2, R3}  // Load values into R1, R2, R3 from memory, decrement R0 before each load

In this case, the values of R1, R2, and R3 are stored in memory starting at the address in R0. After each store, R0 is incremented by the size of the data stored (4 bytes).

STMIA (Store Multiple Increment After)
  • STMIA is used to store multiple registers to memory. The increment after mode means that after storing each register’s value, the base address (in the register) is incremented by the size of the data type being stored (usually 4 bytes for ARM).

Example:

1
STMIA R0!, {R1, R2, R3}  // Store values in R1, R2, R3 to memory starting at R0, increment R0 after each store

In this case, the values of R1, R2, and R3 are stored in memory starting at the address in R0. After each store, R0 is incremented by the size of the data stored (4 bytes).

Example:

1
2
3
4
LDR r0, =0xABCD        ; Load the address 0xABCD into register r0
LDR r1, [r0]          ; Load the value at the address in r0 into r1

STR r1, [r0]          ; Store the value in r1 to the address in r0
1
2
3
LDMIA r2!, {r3-r6}    ; Load multiple registers from memory starting at address in r2

STMIA r2!, {r3-r6}    ; Store multiple registers to memory starting at address in r2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    .section .data
source_array: 
    .word 0x00000001, 0x00000002, 0x00000003, 0x00000004   // Source data (4 words)
destination_array:
    .space 16                                              // Space for 4 words (4 * 4 bytes)

    .section .text
    .global _start

_start:
    LDR r0, =source_array      // Load address of source_array into r0
    LDR r1, =destination_array  // Load address of destination_array into r1

    // Load multiple registers from source_array into r2-r5
    LDMIA r0!, {r2-r5}         // Load values from source_array into r2, r3, r4, r5; increment r0

    // Modify the loaded values (for example, increment each by 1)
    ADD r2, r2, #1             // Increment value in r2
    ADD r3, r3, #1             // Increment value in r3
    ADD r4, r4, #1             // Increment value in r4
    ADD r5, r5, #1             // Increment value in r5

    // Store the modified values back to destination_array
    STMIA r1!, {r2-r5}         // Store values from r2-r5 into destination_array; increment r1

    // Exit the program using sys_exit
    MOV r0, #0                  // Set exit code to 0 (success)
    MOV r7, #1                  // Load syscall number for sys_exit into r7
    SWI 0                       // Make the syscall to exit
This post is licensed under CC BY 4.0 by the author.