Computer Organisation & Architecture (COA) · Lesson 4 of 8 · Updated ·
Instruction Sets and Addressing Modes — Formats, Immediate to Indexed Addressing, RISC vs CISC
An instruction set architecture (ISA) is the vocabulary a CPU understands: which operations exist, how they're encoded in bits, and how they find their operands. Addressing modes and instruction-format numericals are among the most asked COA questions in university and GATE exams.
1. Instruction format
An instruction is split into fields:
| opcode | mode | operand / address fields ... |
- Opcode — which operation (ADD, LOAD, JUMP…). With
kopcode bits you can have 2ᵏ distinct operations. - Operand fields — registers, memory addresses or constants.
Number of addresses per instruction (for X = A + B):
| Format | Example | Notes |
|---|---|---|
| 3-address | ADD X, A, B | short programs, long instructions |
| 2-address | MOV X, A / ADD X, B | one operand is also the destination |
| 1-address (accumulator) | LOAD A / ADD B / STORE X | ACC is implied (like our toy CPU) |
| 0-address (stack) | PUSH A / PUSH B / ADD / POP X | operands on a stack |
2. Instruction format numericals
Example 1. A CPU has 64 registers and 32-bit instructions. An instruction has an opcode, two register operands and a 12-bit immediate. How many opcodes are possible?
Register field = log₂64 = 6 bits each → 12 bits; immediate 12 bits; opcode = 32 − 12 − 12 = 8 bits → 256 opcodes.
Example 2 (expanding opcodes). Instructions are 16 bits. Two-address instructions use a 4-bit opcode and two 6-bit address fields (4 + 6 + 6 = 16). There are 15 two-address instructions. What is the maximum number of one-address instructions?
A 4-bit opcode has 2⁴ = 16 patterns; 15 are used, so 1 pattern is free. A one-address instruction doesn't need the second 6-bit address field, so that free pattern can be extended with those 6 bits: 1 × 2⁶ = 64 one-address instructions.
import math
# Example 1
reg_bits = int(math.log2(64))
opcode_bits = 32 - 2 * reg_bits - 12
print("Ex1: opcode bits =", opcode_bits, "-> opcodes =", 2 ** opcode_bits)
# Example 2
two_addr_patterns = 2 ** 4
free_patterns = two_addr_patterns - 15
one_addr = free_patterns * 2 ** 6
print("Ex2: free 4-bit patterns:", free_patterns, "-> max one-address instructions:", one_addr)
Ex1: opcode bits = 8 -> opcodes = 256
Ex2: free 4-bit patterns: 1 -> max one-address instructions: 64
Rule: leftover opcode patterns × 2^(bits freed by dropping an address field).
3. Addressing modes
The effective address (EA) is the memory address where the operand actually is. Each mode is a different way to compute it.
| Mode | Instruction contains | Operand is | Memory accesses for operand | Use |
|---|---|---|---|---|
| Immediate | the value itself | #5 | 0 | constants |
| Direct (absolute) | address A | M[A] | 1 | global variables |
| Indirect | address A of a pointer | M[M[A]] | 2 | pointers |
| Register | register R | R | 0 | fastest; temporaries |
| Register indirect | register R holding an address | M[R] | 1 | pointers in registers |
| Indexed / base + offset | address A + index register | M[A + R] | 1 | arrays, struct fields |
| PC-relative | offset d | M[PC + d] (target PC+d for branches) | 1 | branches, position-independent code |
| Auto-increment | register R | M[R], then R ← R + size | 1 | stepping through arrays |
4. Simulate the modes
memory = {100: 500, 200: 300, 300: 42, 500: 7, 510: 99}
R = {"R1": 300, "R2": 10}
PC = 150
def operand(mode, field):
if mode == "immediate": return field
if mode == "direct": return memory[field]
if mode == "indirect": return memory[memory[field]]
if mode == "register": return R[field]
if mode == "register_indirect": return memory[R[field]]
if mode == "indexed": base, reg = field; return memory[base + R[reg]]
if mode == "relative": return memory[PC + field]
tests = [("immediate", 100), ("direct", 100), ("indirect", 100), ("register", "R1"),
("register_indirect", "R1"), ("indexed", (500, "R2")), ("relative", 50)]
for mode, field in tests:
print(f"{mode:18} field={str(field):12} operand={operand(mode, field)}")
immediate field=100 operand=100
direct field=100 operand=500
indirect field=100 operand=7
register field=R1 operand=300
register_indirect field=R1 operand=42
indexed field=(500, 'R2') operand=99
relative field=50 operand=300
Trace two of them: indirect 100 → M[100] = 500 → M[500] = 7. Indexed (500, R2) → EA = 500 + 10 = 510 → M[510] = 99 (this is exactly arr[10] when the array starts at 500).
5. RISC vs CISC
| RISC (Reduced Instruction Set) | CISC (Complex Instruction Set) | |
|---|---|---|
| Instructions | few, simple, fixed length | many, complex, variable length |
| Memory access | only LOAD/STORE touch memory | many instructions operate on memory directly |
| CPI | ~1 (easy to pipeline) | varies, many cycles |
| Registers | many | fewer |
| Control unit | hardwired | often microprogrammed |
| Examples | ARM (phones, Apple M-series), RISC-V | x86 (Intel/AMD PCs) |
Modern x86 chips internally translate complex instructions into RISC-like micro-operations, so the line has blurred — but the exam distinctions above are still standard. RISC-V is an open ISA growing fast, including in Indian chip projects (e.g. the Shakti processors from IIT Madras).
6. Hardwired vs microprogrammed control
- Hardwired control unit: control signals generated by fixed logic circuits — fast, hard to change (RISC).
- Microprogrammed control unit: each instruction is a small program of micro-instructions stored in a control memory — flexible, easier to add complex instructions, slower (classic CISC).
Common mistakes
- Immediate vs direct:
#100is the value 100;100(direct) is the value at address 100. - Counting memory accesses for indirect as 1 (it's 2 for the operand, plus the instruction fetch).
- Forgetting that PC already points to the next instruction when computing PC-relative targets in exam questions (read the question's convention).
Quick recap
- Instruction = opcode + operand fields; k opcode bits → 2ᵏ operations; expanding opcodes reuse leftover patterns.
- Modes: immediate (value), direct
M[A], indirectM[M[A]], register, register indirectM[R], indexedM[A+R], relativeM[PC+d], auto-increment. - RISC: simple, fixed-length, load/store, pipelines well (ARM, RISC-V). CISC: complex, variable length (x86).
- Hardwired control = fast; microprogrammed = flexible.