Assembly Language - Addressing Modes

Addressing Mode determines how the CPU locates the operands of an instruction—where data comes from and where results are stored.


What is an Addressing Mode?

The operands of each assembly instruction can be an immediate value, a value in a register, or data in memory.

Addressing ModeIt tells the CPU how to calculate the actual address of the operand or directly provides the operand value.

The x86 architecture provides a variety of flexible addressing modes. Understanding the usage scenarios of each mode is the foundation for writing efficient assembly code.


Immediate Addressing

The operand is directly included in the instruction as a constant value.

The source operand is an immediate value; the CPU reads it directly from the instruction without accessing memory or registers.

Example

; Immediate addressing example

mov eax, 42                 ; 42 is an immediate value, encoded directly in the instruction
add ebx, 100                ; 100 is an immediate value
mov ecx, 0x2A               ; Hexadecimal immediate value
mov edx, 'A'                ; Character 'A' = 0x41, also an immediate value

Immediate addressing is the fastest "addressing mode" because the data is in the instruction stream; the CPU gets the data while fetching the instruction. However, an immediate value can only be used as a source operand, not as a destination operand. You cannot writemov 42, eax。


Register Addressing

The operand is stored in a register, and the CPU directly operates on the register.

This is also one of the fastest operation modes because there is no memory access overhead.

Example

; Register addressing example

mov eax, ebx                ; Copy the value of ebx to eax (both operands use register addressing)
add ecx, edx                ; ecx = ecx + edx
push eax                    ; Push the value of eax onto the stack
inc ebx                     ; ebx = ebx + 1

Register addressing is the fastest; when writing assembly code, prioritize using registers to store frequently accessed data. However, the number of registers is limited, so you cannot put all data into registers.


Direct Addressing

The operand is a memory address, and the address is written directly in the instruction (in the form of a variable label).

The CPU needs to access memory once to read or write data.

Example

; File path: direct_addr.asm
; Direct addressing example

section .data
    value dd 12345678          ; Define a double-word variable in memory
    name db 'example', 0

section .text
    global _start

_start:
    ; Read memory using direct addressing
    mov eax, [value]           ; Read 4 bytes from memory address value to eax
    ; eax is now 12345678

    ; Write to memory using direct addressing
    mov dword [value], 98765   ; Write 98765 to the memory address of value

    ; Read a byte using direct addressing
    mov al, [name]             ; Read the first byte of name 'r' = 0x72
    mov bl, [name + 1]         ; Read the second byte of name 'u' = 0x75

    mov eax, 1
    mov ebx, 0
    int 0x80

Register Indirect Addressing

A register holds a memory address, and the CPU uses that address to access memory.

The register inside the brackets is used as a pointer.

Example

; File path: indirect_addr.asm
; Register indirect addressing example

section .data
    msg db 'Hello, EXAMPLE!', 0xA
    len equ $ - msg

section .text
    global _start

_start:
    mov eax, msg                
    mov al, [eax]               (row 2, column 3, subscripts start from 0)
    ; Now al = 'H' = 0x48

    ; Traverse the string and convert lowercase letters to uppercase
    mov esi, msg                ; esi points to the start of the string
    mov ecx, len                = 7)

convert_loop:
    mov al, [esi]               ; Indirect addressing: read the character pointed to by esi
    cmp al, 'a'                 ; Is it greater than or equal to 'a'
    jb next_char                ; No, skip
    cmp al, 'z'                 ; Is it less than or equal to 'z'
    ja next_char                ; No, skip
    sub al, 32                  ; Convert to uppercase (in ASCII table, lowercase - uppercase = 32)
    mov [esi], al               ; Indirect addressing: write back to the location pointed to by esi

next_char:
    inc esi                     ; Move the pointer to the next character
    loop convert_loop           ; Continue the loop until all are processed

    ; Output the converted string
    mov eax, 4
    mov ebx, 1
    mov ecx, msg
    mov edx, len
    int 0x80

    mov eax, 1
    mov ebx, 0
    int 0x80

Indirect addressing is the foundation of array traversal, string operations, and data structure access. ESI and EDI are registers specifically designed to work with indirect addressing; together withinc esithem, you can easily traverse contiguous memory.


Base Addressing

Effective address = value of base register + offset (displacement).

The base register can be EBX, EBP, ESI, EDI, etc.

Example

; Base addressing example: accessing structure members

section .data
    ; Simulate a simple structure: {id, age, score}
    ; id = 2 bytes
    ; age = 2 bytes
    ; score = 4 bytes
    student db 0x01, 0x00      ; id = 1
            db 0x14, 0x00      ; age = 20
            dd 95              ; score = 95

section .text
    global _start

_start:
    mov ebx, student            ; ebx holds the base address of the structure

    ; Access each member via base + offset
    mov ax, [ebx]               ; Read id (offset 0)
    mov ax, [ebx + 2]           ; Read age (offset 2)
    mov eax, [ebx + 4]          ; Read score (offset 4)

    ; Modify age
    mov word [ebx + 2], 21      ; age = 21

    ; Modify score
    mov dword [ebx + 4], 98     ; score = 98

    mov eax, 1
    mov ebx, 0
    int 0x80

Indexed Addressing

Use an index register (ESI or EDI) plus an offset to access array elements.

Example

; Indexed addressing example: traversing an array

section .data
    array dd 10, 20, 30, 40, 50     ; An array of 5 doubleword elements
    array_len equ ($ - array) / 4    ; Number of elements = total bytes / 4

section .text
    global _start

_start:
    mov ecx, array_len              ; Loop counter
    mov esi, 0                      ; Index (subscript starts from 0)
    mov ebx, 0                      ; Accumulated sum

sum_loop:
    mov eax, [array + esi * 4]      ; Indexed addressing: array + index * element size
    ; esi * 4 because each element is 4 bytes
    add ebx, eax                    ; Accumulate into ebx
    inc esi                         ; Add 1 to the index
    loop sum_loop
    ; ebx = 10+20+30+40+50 = 150

    ; Index + offset: access the second element
    ; array + 2*4 = array + 8, i.e., 30
    mov eax, [array + 2*4]          ; eax = 30

    mov eax, 1
    mov ebx, 0
    int 0x80

Base-Indexed Addressing

Effective address = base register + index register × scale factor + offset.

This is the most powerful addressing mode in x86, capable of completing address calculation in a single instruction.

Note: x86 supports only one index register × scale factor; it does not support multiple registers with scale factors.

Example

; Base-indexed addressing example: accessing a 2D array

section .data
    ; A 2D array with 3 rows and 4 columns
    matrix dd 1, 2, 3, 4
           dd 5, 6, 7, 8
           dd 9, 10, 11, 12

section .text
    global _start

_start:
    ; Access matrix
    ; Address = matrix + row*bytes per row + column*bytes per element
    ;       = matrix + 1*16 + 2*4
    ;       = matrix + 24

    mov ebx, matrix             ; Base register
    mov esi, 24                 ; Precomputed total offset

    ; Correct format: base + offset
    mov eax, [ebx + esi]         ; eax = 7

    ; Standard base-indexed addressing format: [base + index*scale factor + offset]
    ; Directly access the 6th element (matrix
    mov edi, 6
    mov eax, [matrix + edi*4]    ; eax = 7

    ; Program exit
    mov eax, 1
    mov ebx, 0
    int 0x80

Addressing Modes Overview

The following diagram fully shows the 7 addressing modes of x86 and how they work:

x86 寻址方式一览
Addressing ModeSyntax FormatEffective Address/ValueTypical Use
Immediate Addressingmov eax, 4242 (constant value)Initialization, constant operations
Register addressingmov eax, ebxThe value of register ebxData transfer between registers
Direct addressingmov eax, [var]The value at memory address varAccessing global variables
Indirect addressingmov eax, [ebx]Address = value of ebxPointer operations, traversing memory
Base addressingmov eax, [ebx+8]Address = ebx + 8Struct member access
Indexed addressingmov eax, [arr+esi*4]Address = arr + esi × 4One-dimensional array access
Base-indexed addressingmov eax, [ebx+esi*4+8]Address = ebx + esi × 4 + 8Two-dimensional arrays, complex structures

In 32-bit protected mode,all general-purpose registers can be used as base or index registers. This is very different from 16-bit real mode (in 16-bit mode, only BX, BP, SI, DI can be used for addressing). The flexibility of 32-bit makes addressing more convenient.

Other extensions