CampusFlow
ArchitectureAddressing Modes

đŸŽ¯ Addressing Modes

How CPUs determine the location of operands: from immediate constants to complex memory calculations.

What are Addressing Modes?

â„šī¸

The How of Operand Access

Addressing modes define how the CPU calculates the effective address of an operand. The mode is encoded in the instruction opcode or a separate field. Different modes trade off speed, flexibility, and code size.

The effective address (EA) is the actual memory address used to access the operand. The addressing mode tells the CPU how to compute EA from the instruction's address fields and CPU register values.

Addressing Modes Overview

Address Calculation Examples

Example: Array Access

mips

; C code: arr[i] where arr is at 0x1000, i=5, each element 4 bytes
; Base + Indexed addressing

    li   $t0, 0x1000       ; base address of array
    li   $t1, 5            ; index i
    sll  $t1, $t1, 2       ; t1 = i * 4 (scale by element size)
    add  $t2, $t0, $t1     ; t2 = base + offset = &arr[5]
    lw   $t3, 0($t2)       ; t3 = arr[5] (register indirect with offset)

Example: Struct Field Access

mips

; C code: person.age where person is at address in $a0
; Base + Displacement addressing

    lw   $t0, 8($a0)       ; load person.age (offset 8 bytes into struct)
    lw   $t1, 0($a0)       ; load person.name pointer (offset 0)
    lw   $t2, 4($a0)       ; load person.id (offset 4)

Addressing Modes Comparison

ModeSpeedFlexibilityCode SizeBest For
ImmediateFastestLowCompactConstants, masks
RegisterFastestMediumCompactALU operations
DirectFastLowMediumGlobal variables
Register IndirectFastHighCompactArrays, pointers
IndexedMediumVery HighMediumArrays of structs
IndirectSlowestHighMediumPointers to pointers
PC-RelativeFastMediumCompactBranches, PIC

Effective Address Calculation

💡

EA = Base + (Index × Scale) + Displacement

Many architectures support a generalized formula: Effective Address = Base Register + (Index Register × Scale Factor) + Displacement. x86's MODR/M byte encoding is the classic example, where scale can be 1, 2, 4, or 8.

c

// x86 addressing mode calculation
// MODR/M byte: mod(2) | reg(3) | r/m(3)
// SIB byte:    scale(2) | index(3) | base(3)

uint32_t calc_ea(uint8_t modrm, uint8_t sib, uint32_t base, uint32_t index, int32_t disp) {
    uint8_t mod = (modrm >> 6) & 0x3;
    uint8_t rm  = modrm & 0x7;
    
    if (mod == 0 && rm == 5) return disp;           // [disp32]
    if (mod == 1)             return base + (int8_t)disp;  // [base + disp8]
    if (mod == 2)             return base + disp;         // [base + disp32]
    if (mod == 3)             return base;                // register direct
    
    // With SIB byte
    uint8_t scale_val = 1 << ((sib >> 6) & 0x3);
    return base + (index * scale_val) + disp;
}

Interview Questions

What is the difference between register mode and register indirect mode?

Register mode uses the register's value directly as the operand (e.g., ADD R1, R2). Register indirect mode uses the register's value as a memory address to fetch the operand (e.g., LW R1, (R2)). Register mode needs no memory access; register indirect needs one memory access to get the operand.

Explain PC-relative addressing and why it's important.

PC-relative addressing computes the effective address as PC + offset. It's used in branch instructions to allow position-independent code (PIC) that works regardless of where the code is loaded in memory. This is critical for shared libraries and dynamically linked code.

How do auto-increment and auto-decrement modes support stack operations?

Auto-increment uses the address then increments, ideal for popping from a stack where the stack pointer increases after read. Auto-decrement decrements then uses the address, ideal for pushing where the stack pointer decreases before write. These are used in architectures like Motorola 68000 for efficient stack-based operations.

Why do modern architectures typically provide fewer addressing modes than older CISC designs?

RISC philosophy argues that complex addressing modes increase hardware complexity, slow down the clock cycle, and complicate pipelining. Modern architectures prefer simpler, regular modes (register, immediate, base+displacement) and rely on compiler optimization to combine simple instructions when needed.