You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
A processor can only do what its instruction set permits. Every program you ever run — a browser, a game, an operating system — is, after compilation, nothing more than a long sequence of these instructions stored as binary patterns in memory. At A-Level you need to understand precisely what an instruction set is, how a single machine-code instruction is formatted into an opcode and an operand, and — most importantly for the exam — the four addressing modes that determine how the operand is interpreted to reach the actual data.
Addressing modes are a perennial favourite of examiners because they test genuine understanding rather than rote learning: you are typically given a short program and a snapshot of memory and asked to trace the contents of registers step by step. This lesson builds that skill deliberately, with fully worked traces for immediate, direct, indirect and indexed addressing, and a specimen question modelled on the real exam style.
This lesson maps to the AQA A-Level Computer Science (7517) specification, §4.7.3 Structure and role of the processor and its components (and supports the assembly-language content of §4.6.3):
An instruction set — formally the instruction set architecture (ISA) — is the complete collection of all the machine-code instructions a particular processor can recognise and carry out. Each instruction is a fixed binary pattern; the processor's control unit is hardwired (or microcoded) to respond to each pattern in a defined way.
The instruction set defines:
Two processors with different instruction sets cannot run each other's machine code directly: a program compiled for an ARM chip is meaningless binary to an x86 chip, which is why software is compiled separately for each architecture.
The instruction set is the boundary between hardware and software — often called the hardware/software interface. Everything above it is software; everything below it is the physical implementation. It is helpful to see the chain by which your high-level code becomes instructions the processor obeys:
| Level | Example | Becomes… |
|---|---|---|
| High-level language | total = total + scores[i]; | compiled by a compiler into… |
| Assembly language | LDA scores,X / ADD total / STA total | assembled by an assembler into… |
| Machine code | 0001 0110 … | the binary the processor's instruction set defines |
The instruction set defines exactly which machine-code patterns are valid and what each does, so the compiler and assembler must target that specific set. This is why the same C program must be recompiled to run on a different architecture, and why a virtual machine or emulator is needed to run one architecture's machine code on another. It also explains the role of intermediate representations such as Java bytecode, which target a defined virtual instruction set so that one compiled program can run anywhere a matching virtual machine exists.
A typical machine-code instruction divides into two fields:
| Field | Width example | Purpose |
|---|---|---|
| Opcode | bits 4–7 (the high nibble) | Specifies which operation to perform — ADD, SUB, LDA (load), STA (store), BRA (branch)… |
| Operand | bits 0–3 (the low nibble) | Specifies what to operate on — a memory address, a literal value, or a register identifier |
flowchart LR
subgraph INSTR["One machine-code instruction (8 bits)"]
OP["Opcode<br/>(4 bits)"]
OPER["Operand<br/>(4 bits)"]
end
The split has direct consequences:
Exam Tip: If a question gives an instruction width and an opcode width, you can be asked to calculate the number of possible operations (2opcode bits) or the number of directly addressable locations (2operand bits). Make sure you know which power-of-two applies to which field.
| Category | Examples | Description |
|---|---|---|
| Data transfer | LDA, STA, MOV | Move data between registers and memory |
| Arithmetic | ADD, SUB, MUL, DIV | Perform calculations |
| Logical | AND, OR, NOT, XOR | Bitwise logic operations |
| Comparison | CMP | Compare two values and set status flags |
| Branch / Jump | BRA, BEQ, BNE, BGT, BLT | Change the flow of execution (conditional or unconditional) |
| Shift | LSL, LSR, ASR | Shift bits left or right |
| I/O | IN, OUT | Read from or write to I/O ports |
| Halt | HLT / STOP | Stop execution |
An addressing mode specifies how the operand field is to be interpreted in order to locate the actual data — the effective address (or, for immediate mode, the value itself). A-Level requires you to know four modes. In the examples below, # marks an immediate value, plain numbers are addresses, parentheses indicate indirection, and ,X indicates indexing.
The operand is the data. No memory access is needed to obtain it — the value travels inside the instruction.
LDA #5 # ACC ← 5 (the literal 5 is loaded directly)
ADD #3 # ACC ← ACC + 3 (add the literal 3)
The operand is the address of the memory location holding the data. Effective address = operand.
LDA 200 # ACC ← contents of address 200
STA 300 # contents of address 300 ← ACC
The operand is the address of a location that itself contains the address of the actual data — a pointer. Effective address = contents of (operand).
LDA (200) # Step 1: read address 200 -> it holds 500 (a pointer)
# Step 2: read address 500 -> ACC ← contents of address 500
The operand is a base address to which the contents of the Index Register (IX) are added. Effective address = operand + [IX].
LDA 100,X # Effective address = 100 + [IX]
# If IX = 3, then ACC ← contents of address 103
| Mode | Operand means | Effective address | Example | Operand memory accesses |
|---|---|---|---|---|
| Immediate | The actual value | n/a (value is in the instruction) | LDA #5 | 0 |
| Direct | Address of the data | operand | LDA 200 | 1 |
| Indirect | Address of a pointer to the data | contents of (operand) | LDA (200) | 2 |
| Indexed | Base address; add IX | operand + [IX] | LDA 100,X | 1 (after the add) |
Exam Tip: Watch the symbol carefully.
#= immediate (value), no symbol = direct (address), parentheses = indirect (pointer),,X= indexed (add IX). Misreading the symbol is the commonest way to lose addressing-mode marks.
You will frequently be given a memory snapshot and a short program and asked to state the accumulator after each instruction. Always work in three columns: mode → effective address → resulting value.
Memory and registers before execution:
| Address | Contents |
|---|---|
| 100 | 42 |
| 101 | 7 |
| 102 | 100 |
Registers: ACC = 0, IX = 1.
LDA #10 # immediate: ACC ← 10
ADD 100 # direct: add [100]=42 -> ACC = 10 + 42 = 52
LDA 100,X # indexed: eff. addr = 100 + IX(1) = 101 -> ACC ← [101] = 7
LDA (102) # indirect: [102]=100 (pointer) -> ACC ← [100] = 42
STA 101 # direct: [101] ← ACC = 42 (memory[101] now holds 42)
Step-by-step justification (the "show your working" the examiner rewards):
| Step | Instruction | Mode | Effective address | Working | ACC after |
|---|---|---|---|---|---|
| 1 | LDA #10 | Immediate | — | value 10 is in the instruction | 10 |
| 2 | ADD 100 | Direct | 100 | 10 + [100] = 10 + 42 | 52 |
| 3 | LDA 100,X | Indexed | 100 + [IX] = 101 | load [101] = 7 | 7 |
| 4 | LDA (102) | Indirect | [102] = 100 | load [100] = 42 | 42 |
| 5 | STA 101 | Direct | 101 | store ACC into [101] | 42 (unchanged) |
Exam Tip: Always show the method, not just the answer. Writing the addressing mode, the effective address and the value at each step earns the working marks even if a single arithmetic slip changes the final number.
Indexed addressing earns its place in the instruction set because it makes array processing efficient. Consider summing the four elements of an array stored at addresses 200–203:
| Address | Contents |
|---|---|
| 200 | 5 |
| 201 | 8 |
| 202 | 2 |
| 203 | 4 |
The following loop uses the index register IX to step through the array. The instruction ADD 200,X never changes — only IX changes — yet it reads a different element each pass:
LDA #0 # ACC ← 0 (running total)
LDX #0 # IX ← 0 (array offset)
loop: ADD 200,X # ACC ← ACC + [200 + IX]
INX # IX ← IX + 1 (advance to next element)
CMX #4 # compare IX with 4
BNE loop # if IX != 4, repeat
STA 300 # store the total at address 300
Tracing the accumulator and index register through the loop:
| Pass | IX before | Effective address (200 + IX) | Element read | ACC after ADD | IX after INX |
|---|---|---|---|---|---|
| 1 | 0 | 200 | 5 | 0 + 5 = 5 | 1 |
| 2 | 1 | 201 | 8 | 5 + 8 = 13 | 2 |
| 3 | 2 | 202 | 2 | 13 + 2 = 15 | 3 |
| 4 | 3 | 203 | 4 | 15 + 4 = 19 | 4 |
After the fourth pass IX = 4, so BNE loop falls through and the total 19 is stored at address 300. This is the single most important reason indexed addressing exists: one instruction can process an entire array simply by varying the index register, which is far more compact and flexible than writing a separate ADD for every element. High-level code such as for (i = 0; i < 4; i++) total += a[i]; compiles directly to a loop of this shape.
Examiners may give you a binary instruction and an opcode table and ask you to decode it. Suppose instructions are 8 bits: the top 4 bits are the opcode and the bottom 4 are the operand. Given this (illustrative) opcode table:
| Opcode (binary) | Mnemonic | Meaning |
|---|---|---|
| 0001 | LDA | Load (direct) |
| 0010 | STA | Store (direct) |
| 0011 | ADD | Add (direct) |
| 0100 | SUB | Subtract (direct) |
Decode the instruction 00110101:
0011, operand = 0101.0011 → ADD.0101 from binary to denary → 5.ADD 5 → add the contents of memory address 5 to the accumulator (direct addressing).Notice the limits this format imposes, exactly as discussed earlier: a 4-bit opcode allows only 24=16 different operations, and a 4-bit operand can address only locations 0–15 (24=16 addresses) in direct mode. If the program needs to reach address 200, direct addressing with a 4-bit operand cannot — which is precisely the problem indirect addressing solves by storing a full-width address in memory and pointing at it.
Each mode exists to solve a real problem:
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.