nasm-know-hows

nasm assembly related stuff

View on GitHub

πŸ“˜ 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:


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:

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:


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


πŸŽ‰ Excellent! You’ve mastered memory addressing!

Next: Topic 12: Arrays & Strings


← Previous Topic Back to Main Next Topic β†’