Where is the operand?
An instruction like LOAD R0, … says what to do: copy something into register R0. But where is that something? It might be inside the instruction, in a register, or somewhere in memory. The addressing mode is the rule the CPU uses to find it.
A typical instruction has three parts:
| opcode | mode | address field |
|---|---|---|
| what to do (LOAD) | how to read the field | a number or a register |
When the operand is in memory, the address the CPU finally reads from is called the effective address (EA).
The machine in the model
Every mode in the model runs on the same small machine, so you can compare the answers:
| Item | Value |
|---|---|
| The instruction | at address 5, address field = 40 |
| PC after the fetch | 6 (it already points to the next instruction) |
| R1 | 45 |
| R2 (index register) | 2 |
| Memory 40 to 46 | 43, 17, 31, 8, 26, 52, 19 |
The nine modes
| Mode | Written as | Effective address | R0 in the model | Operand reads |
|---|---|---|---|---|
| Immediate | LOAD R0, #40 |
none, the operand is 40 | 40 | 0 |
| Register | LOAD R0, R1 |
none, the operand is in R1 | 45 | 0 |
| Direct (absolute) | LOAD R0, 40 |
40 | 43 | 1 |
| Indirect | LOAD R0, @40 |
M[40] = 43 | 8 | 2 |
| Register indirect | LOAD R0, (R1) |
R1 = 45 | 52 | 1 |
| Indexed | LOAD R0, 40(R2) |
40 + R2 = 42 | 31 | 1 |
| PC-relative | LOAD R0, 40(PC) |
PC + 40 = 46 | 19 | 1 |
| Auto-increment | LOAD R0, (R1)+ |
R1 = 45, then R1 becomes 46 | 52 | 1 |
| Auto-decrement | LOAD R0, −(R1) |
R1 becomes 44 first, EA = 44 | 26 | 1 |
Books write these differently. Indirect is often (40) instead of @40, and some machines mark modes with bits instead of symbols. The ideas are the same.
The operand is already in the CPU
- Immediate: the number is part of the instruction. It’s the fastest mode, but the value is fixed when the program is written.
- Register: the instruction names a register, and the register holds the value.
The address is in the instruction
- Direct: the address field is the memory address.
- Indirect: the address field points to a memory word that holds the real address, like a pointer stored in memory. It is flexible but needs two memory reads.
The address is in a register
- Register indirect: the register holds the address, like
*pin C. - Auto-increment
(R1)+: uses R1 as the address, then adds one item’s size to R1. Repeating the instruction walks forward through an array. - Auto-decrement
−(R1): subtracts first, then uses R1. Together with auto-increment this gives push and pop for a stack.
In the model memory holds one number per address, so the step is 1. On a real byte-addressed machine it is the size of the operand, for example 4 for a 32-bit word.
Address = register + constant
- Indexed: EA = address field + index register. The field is where an array starts, and the register picks the element.
- Base register: the same addition, with the roles swapped. A base register holds where a block of data (or the whole program) starts, and the field is the offset inside it.
- PC-relative: EA = PC + field. It is used for branches. The PC has already moved past the current instruction during the fetch, so the distance is measured from the next instruction.
Which mode for which job?
| In a program | Mode |
|---|---|
x = x + 1 (the constant 1) |
immediate |
| a local variable kept in a register | register |
| a global variable | direct |
*p |
register indirect |
| a pointer that is itself stored in memory | indirect |
a[i] |
indexed |
if / loop jumps |
PC-relative |
*p++ and pop |
auto-increment |
| push | auto-decrement |
Speed versus flexibility
Every memory read costs time, so modes that keep the operand in the CPU are fastest and indirect is slowest. More modes also make instructions harder to decode. That is one reason RISC processors keep only a few (register, immediate, base + offset and PC-relative), while older CISC machines like the VAX had many more.
When a question asks for the number of memory references of an instruction, check whether it counts the instruction fetch too. The model counts only the reads for the operand.
Code
mem = {40: 43, 41: 17, 42: 31, 43: 8, 44: 26, 45: 52, 46: 19}
A = 40 # address field of the instruction
def load(mode, regs):
"""Return (effective address, operand, memory reads) for LOAD R0 with this mode."""
if mode == 'immediate': # LOAD R0, #40
return None, A, 0
if mode == 'register': # LOAD R0, R1
return None, regs['R1'], 0
if mode == 'direct': # LOAD R0, 40
return A, mem[A], 1
if mode == 'indirect': # LOAD R0, @40
ea = mem[A] # first read: the pointer
return ea, mem[ea], 2
if mode == 'register indirect': # LOAD R0, (R1)
return regs['R1'], mem[regs['R1']], 1
if mode == 'indexed': # LOAD R0, 40(R2)
ea = A + regs['R2']
return ea, mem[ea], 1
if mode == 'pc-relative': # LOAD R0, 40(PC)
ea = regs['PC'] + A # PC already points to the next instruction
return ea, mem[ea], 1
if mode == 'auto-increment': # LOAD R0, (R1)+
ea = regs['R1']
regs['R1'] += 1 # step the pointer AFTER using it
return ea, mem[ea], 1
if mode == 'auto-decrement': # LOAD R0, -(R1)
regs['R1'] -= 1 # step the pointer BEFORE using it
ea = regs['R1']
return ea, mem[ea], 1
for mode in ['immediate', 'register', 'direct', 'indirect', 'register indirect',
'indexed', 'pc-relative', 'auto-increment', 'auto-decrement']:
regs = {'R1': 45, 'R2': 2, 'PC': 6}
ea, value, reads = load(mode, regs)
print(f'{mode:18} EA={ea} R0={value} reads={reads}')
Common mistakes
- PC-relative with the old PC. The PC is incremented during the fetch, so the base is the address of the next instruction.
- Register vs register indirect. In register mode R1 is the value. In register indirect R1 holds the address of the value.
- Indirect as if it were direct. With
@40the operand is not M[40] but M[M[40]]. - Getting the order wrong for auto-increment and auto-decrement. Increment happens after the access, decrement before it.
- Counting memory references. State whether the instruction fetch is included. Exam answers differ by one depending on that choice.
Complexity at a glance
| Case / operation | Time | Why |
|---|---|---|
| Immediate, register | 0 memory reads | The operand is already in the CPU. |
| Direct, register indirect, indexed, PC-relative, auto-increment/decrement | 1 memory read | |
| Indirect | 2 memory reads | One for the pointer, one for the operand. |
Quick check
Test yourself — pick an answer to see if you got it.
1. LOAD R0, @40 uses indirect addressing. M[40] = 43 and M[43] = 8. What ends up in R0?
Address 40 holds a pointer (43). The operand is at address 43, so R0 = 8. It takes two memory reads.
2. Which addressing mode suits a constant, as in x = x + 1?
Immediate mode keeps the constant inside the instruction, so no memory read is needed for it.
3. In indexed mode the address field is 1000 and the index register holds 12. What is the effective address?
EA = address field + index register = 1000 + 12. The operand is then read from M[1012].
4. Why does PC-relative addressing make code position-independent?
Moving the whole program moves the instruction and its target together, so the distance between them does not change.
5. R1 = 45. What does LOAD R0, (R1)+ do?
Auto-increment uses the address first and steps the register afterwards. Auto-decrement does the opposite, stepping first.