π Topic 11: Memory Addressing Modes
Master all x86-64 addressing modes for flexible and efficient memory access.
Overview
Memory addressing modes determine how the CPU calculates the address of data in memory. Understanding these is crucial for:
- π― Efficient array access
- π Data structure manipulation
- β‘ Performance optimization
- π Reading compiler output
Part 1: The Square Brackets [ ]
Square brackets mean: βGo to this address and get/put the value thereβ
Without brackets = the address itself
With brackets = the value at that address
mov rax, array ; RAX = address of array
mov rax, [array] ; RAX = value at array
Part 2: All Addressing Modes
1. Immediate (No Memory Access)
C Equivalent:
int x = 42; // Constant value
Assembly:
mov rax, 42 ; RAX = 42 (no memory access)
mov eax, 0x1234 ; EAX = 0x1234
2. Register Direct
C Equivalent:
int a = b; // Register to register
Assembly:
mov rax, rbx ; RAX = RBX (no memory access)
mov eax, edx ; EAX = EDX
3. Memory Direct (Absolute Address)
C Equivalent:
int x = *(int*)0x1000; // Read from absolute address
Assembly:
mov rax, [0x1000] ; RAX = value at address 0x1000
mov [0x2000], rbx ; Store RBX at address 0x2000
Usage: Rarely used (except for memory-mapped I/O).
4. Register Indirect
C Equivalent:
int *ptr = &value;
int x = *ptr; // Dereference pointer
Assembly:
mov rbx, array ; RBX = address
mov rax, [rbx] ; RAX = value at address in RBX
mov [rbx], rcx ; Store RCX at address in RBX
5. Register + Displacement
C Equivalent:
struct Point {
int x; // offset 0
int y; // offset 4
};
struct Point *p;
int y_value = p->y; // Access field at offset
Assembly:
; RBX points to struct
mov eax, [rbx + 0] ; Access x (offset 0)
mov eax, [rbx + 4] ; Access y (offset 4)
mov eax, [rbx + 8] ; Access next field
; Array access
mov rax, [array + 16] ; Access element 16 bytes into array
6. Base + Index
C Equivalent:
int array[10];
int index = 5;
int value = array[index]; // Dynamic index
Assembly:
mov rbx, array ; base = array address
mov rcx, 5 ; index = 5
mov eax, [rbx + rcx] ; value = array[index]
7. Base + Index*Scale (SIB - Scale-Index-Base)
Most powerful addressing mode!
Format: [base + index*scale + displacement]
Where:
base= any register (RBX, RSI, etc.)index= any register except RSPscale= 1, 2, 4, or 8displacement= constant offset (-2Β³ΒΉ to 2Β³ΒΉ-1)
C Equivalent:
int array[100];
int index = 10;
int value = array[index]; // array + index * sizeof(int)
Assembly:
; Access int array (4 bytes per element)
mov rbx, array ; base
mov rcx, 10 ; index
mov eax, [rbx + rcx*4] ; value = array[index]
; Address = base + index*4
; Access long array (8 bytes per element)
mov rax, [rbx + rcx*8] ; Address = base + index*8
; With displacement
mov eax, [rbx + rcx*4 + 16] ; Skip first 4 elements (16 bytes)
Part 3: Scale Factor Examples
; Scale = 1 (byte arrays, char)
mov al, [rsi + rdi*1] ; char array[index]
; Scale = 2 (word arrays, short)
mov ax, [rsi + rdi*2] ; short array[index]
; Scale = 4 (dword arrays, int, float)
mov eax, [rsi + rdi*4] ; int array[index]
; Scale = 8 (qword arrays, long, double, pointers)
mov rax, [rsi + rdi*8] ; long array[index]
Part 4: RIP-Relative Addressing (x86-64)
Position-independent code - address relative to current instruction.
C Equivalent:
static int global_var = 42;
int x = global_var; // Compiler uses RIP-relative
Assembly:
section .data
value dq 100
section .text
; RIP-relative (x86-64 only)
mov rax, [rel value] ; Address = RIP + offset to value
; Explicit form (NASM)
mov rax, [value wrt ..gotpc]
; Default in 64-bit
mov rax, [value] ; NASM uses RIP-relative by default
Why use it:
- β Code can be loaded at any address (PIE/PIC)
- β More compact encoding
- β Required for shared libraries
Part 5: Practical Examples
Example 1: Array Iteration
C Equivalent:
int sum_array(int *array, int count) {
int sum = 0;
for (int i = 0; i < count; i++) {
sum += array[i];
}
return sum;
}
Assembly (Method 1: Index):
; int sum_array(int *array, int count)
; Args: array=RDI, count=ESI
sum_array:
xor eax, eax ; sum = 0
xor ecx, ecx ; i = 0
loop:
cmp ecx, esi
jge done
add eax, [rdi + rcx*4] ; sum += array[i] (SIB addressing!)
inc ecx
jmp loop
done:
ret
Assembly (Method 2: Pointer):
sum_array:
xor eax, eax ; sum = 0
lea rdx, [rdi + rsi*4] ; end = array + count*4
loop:
cmp rdi, rdx
jge done
add eax, [rdi] ; sum += *ptr (register indirect!)
add rdi, 4 ; ptr++
jmp loop
done:
ret
Example 2: Struct Access
C Equivalent:
struct Person {
int age; // offset 0
int height; // offset 4
long id; // offset 8
};
int get_height(struct Person *p) {
return p->height;
}
Assembly:
; int get_height(struct Person *p)
; Arg: p in RDI
get_height:
mov eax, [rdi + 4] ; return p->height (displacement!)
ret
Example 3: 2D Array Access
C Equivalent:
int matrix[ROWS][COLS];
int get_element(int row, int col) {
return matrix[row][col];
}
Assembly:
; Assume COLS = 10, each element is 4 bytes
; Address = base + (row * COLS + col) * 4
section .data
matrix times 100 dd 0 ; 10x10 matrix
section .text
; int get_element(int row, int col)
; Args: row=EDI, col=ESI
get_element:
; Calculate: row * COLS (10)
imul edi, 10 ; row * COLS
; Calculate: (row * COLS + col) * 4
add edi, esi ; row * COLS + col
mov rbx, matrix
mov eax, [rbx + rdi*4] ; matrix[row][col] (SIB!)
ret
Example 4: String Operations
C Equivalent:
char *strcpy(char *dest, const char *src) {
char *orig_dest = dest;
while (*src != '\0') {
*dest++ = *src++;
}
*dest = '\0';
return orig_dest;
}
Assembly:
; char *strcpy(char *dest, const char *src)
; Args: dest=RDI, src=RSI
strcpy:
mov rax, rdi ; Save orig_dest
loop:
mov cl, [rsi] ; Load byte from src (register indirect!)
mov [rdi], cl ; Store to dest
test cl, cl ; Check if null
jz done
inc rsi ; src++
inc rdi ; dest++
jmp loop
done:
ret
Part 6: Address Calculation Examples
Calculate Effective Address (LEA)
LEA doesnβt access memory - it just calculates the address!
C Equivalent:
int *ptr = &array[index]; // Get address, not value
int offset = index * 4 + 10; // Calculate without memory access
Assembly:
; Get address (doesn't dereference)
lea rax, [rbx + rcx*4] ; RAX = rbx + rcx*4 (no memory access!)
; Use for arithmetic
lea rax, [rdi + rdi*4] ; RAX = rdi * 5 (1 + 4)
lea rax, [rdi + rdi*8] ; RAX = rdi * 9 (1 + 8)
lea rax, [rdi + rsi + 10] ; RAX = rdi + rsi + 10
Part 7: Size Specifiers
When size is ambiguous, use size specifiers:
; Ambiguous
mov [rbx], 42 ; β ERROR: What size?
; Clear
mov byte [rbx], 42 ; β 1 byte
mov word [rbx], 42 ; β 2 bytes
mov dword [rbx], 42 ; β 4 bytes
mov qword [rbx], 42 ; β 8 bytes
; Not ambiguous (size inferred from register)
mov [rbx], al ; β 1 byte (AL is 8-bit)
mov [rbx], ax ; β 2 bytes (AX is 16-bit)
mov [rbx], eax ; β 4 bytes (EAX is 32-bit)
mov [rbx], rax ; β 8 bytes (RAX is 64-bit)
Part 8: Common Patterns
Pattern 1: Array Element Access
; array[index]
mov eax, [array + rcx*4] ; For int array (4 bytes)
mov rax, [array + rcx*8] ; For long/pointer array (8 bytes)
Pattern 2: Struct Field Access
; struct->field
mov eax, [rbx + OFFSET] ; Access field at offset
Pattern 3: Pointer Arithmetic
; ptr++
add rdi, 4 ; Advance by 4 bytes (int*)
add rdi, 8 ; Advance by 8 bytes (long*)
Pattern 4: Array of Structs
; array[index].field
; Address = base + index*sizeof(struct) + field_offset
mov eax, [rbx + rcx*16 + 4] ; Assuming 16-byte structs, field at offset 4
Part 9: Performance Considerations
Cache-Friendly Access
; β GOOD: Sequential access (cache-friendly)
mov rcx, 0
loop:
mov eax, [array + rcx*4]
; Process eax
inc rcx
cmp rcx, 100
jl loop
; β BAD: Random access (cache-unfriendly)
mov rcx, 0
loop:
; Random index calculation
mov eax, [array + random_index*4]
Alignment
; Aligned access (faster)
section .data
align 16
aligned_array dq 1, 2, 3, 4
; Misaligned access (slower, may cause crashes on some CPUs)
mov rax, [rbx + 1] ; Misaligned 64-bit read
β Practice Exercises
Exercise 1: Array Sum
Calculate the sum of a double array (8-byte elements).
Solution
```nasm ; double sum_doubles(double *array, int count) ; Args: array=RDI, count=ESI sum_doubles: xorpd xmm0, xmm0 ; sum = 0.0 xor ecx, ecx ; i = 0 loop: cmp ecx, esi jge done addsd xmm0, [rdi + rcx*8] ; sum += array[i] (scale=8!) inc ecx jmp loop done: ; Result in XMM0 ret ```Exercise 2: Reverse Array
Reverse an array in place.
Solution
```nasm ; void reverse(int *array, int count) ; Args: array=RDI, count=ESI reverse: xor ecx, ecx ; left = 0 lea edx, [esi - 1] ; right = count - 1 loop: cmp ecx, edx jge done ; Swap array[left] and array[right] mov eax, [rdi + rcx*4] mov ebx, [rdi + rdx*4] mov [rdi + rcx*4], ebx mov [rdi + rdx*4], eax inc ecx ; left++ dec edx ; right-- jmp loop done: ret ```π Quick Reference
Addressing Modes
[register] ; Register indirect
[register + disp] ; Register + displacement
[base + index] ; Base + index
[base + index*scale] ; SIB
[base + index*scale + disp] ; Full SIB
[rel label] ; RIP-relative
Scale Values
1 β byte/char
2 β word/short
4 β dword/int/float
8 β qword/long/double/pointer
π― Knowledge Check
- β All 7 addressing modes and when to use each
- β SIB addressing (Scale-Index-Base)
- β Scale factors for different data types
- β RIP-relative addressing and why it matters
- β LEA for address calculation
- β Size specifiers (byte/word/dword/qword)
- β Performance implications of different modes
π Excellent! Youβve mastered memory addressing!
Next: Topic 12: Arrays & Strings
| β Previous Topic | Back to Main | Next Topic β |