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
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 write
mov 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
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
; 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
; 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 with
inc 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
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
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
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:
| Addressing Mode | Syntax Format | Effective Address/Value | Typical Use |
|---|---|---|---|
| Immediate Addressing | mov eax, 42 | 42 (constant value) | Initialization, constant operations |
| Register addressing | mov eax, ebx | The value of register ebx | Data transfer between registers |
| Direct addressing | mov eax, [var] | The value at memory address var | Accessing global variables |
| Indirect addressing | mov eax, [ebx] | Address = value of ebx | Pointer operations, traversing memory |
| Base addressing | mov eax, [ebx+8] | Address = ebx + 8 | Struct member access |
| Indexed addressing | mov eax, [arr+esi*4] | Address = arr + esi × 4 | One-dimensional array access |
| Base-indexed addressing | mov eax, [ebx+esi*4+8] | Address = ebx + esi × 4 + 8 | Two-dimensional arrays, complex structures |
Other extensionsIn 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.