Microprocessors and Microcontrollers

Chapter 3
Instruction Sets and Addressing Modes

Diploma in Computer Architecture and Networking
Microprocessors and Microcontrollers   •   Diploma

Instruction Sets and Addressing Modes

Introduction

A microprocessor becomes useful only when it can interpret and execute instructions. An instruction is a binary-coded command that specifies an operation, its operands, and sometimes the way in which those operands are located. The complete collection of instructions supported by a processor is its instruction set. Addressing modes define how an instruction identifies data in registers, memory, or within the instruction itself.

This chapter uses the Intel 8086 and ARM architectures as representative examples. The 8086 illustrates a traditional complex instruction set with variable-length instructions, segmented memory, and a visible machine-cycle structure. ARM illustrates a reduced instruction set approach, regular instruction formats, large register files, and efficient load/store operation.

Learning Objectives

After studying this chapter, the learner should be able to:

  • identify the principal fields of a machine instruction;
  • distinguish opcodes, operands, data types, and instruction formats;
  • explain common addressing modes using 8086 and ARM examples;
  • interpret stack operations, subroutines, and return mechanisms;
  • explain condition flags and their use in decision-making;
  • describe machine cycles, instruction timing, and the effect of memory access;
  • select suitable addressing modes for practical assembly-language tasks;
  • compare important instruction-set features of 8086 and ARM.

1. Instruction-Set Fundamentals

An instruction normally contains an opcode and one or more operand specifications. The opcode identifies the operation, such as addition, comparison, loading, storing, or branching. Operand fields identify registers, constants, or memory locations.

A generic instruction can be represented as:

Instruction = Opcode + Operand
i

Here, “Operand” may be a register number, immediate value, displacement, or addressing-mode information. This is a conceptual representation rather than a fixed encoding rule; actual formats differ among processors.

1.1 Instruction categories

CategoryPurpose8086 examplesARM examples
Data transferMove dataMOV, PUSH, POPLDR, STR, MOV
ArithmeticNumerical operationsADD, SUB, MULADD, SUB, MUL
LogicalBit manipulationAND, OR, XORAND, ORR, EOR
Shift and rotateMove bitsSHL, SHR, ROLLSL, LSR, ROR
Comparison/testSet flags without retaining a resultCMP, TESTCMP, TST
Control transferChange execution sequenceJMP, CALL, RETB, BL, BX
Processor controlControl machine stateHLT, CLI, STICPSID, CPSIE, system instructions

1.2 Data types

The instruction set determines which data representations can be processed directly. Common types include:

  • bit: a single binary value;
  • byte: 8 bits;
  • word: a processor-dependent unit; in the 8086, a word is 16 bits;
  • doubleword: 32 bits, commonly used in 32-bit ARM systems;
  • signed integer: represented using two’s complement;
  • unsigned integer: interpreted as a non-negative binary value;
  • character: usually represented by an 8-bit code such as ASCII;
  • address: a value identifying a storage location;
  • bit field: a selected group of bits within a register or word.

The same bit pattern can have different meanings depending on the instruction. For example, 11111111₂ is 255 when unsigned but −1 when interpreted as an 8-bit two’s-complement integer.

For an n-bit unsigned value, the range is:

0 ≤ x ≤ 2n − 1

For an n-bit two’s-complement signed value, the range is:

−2^(n−1) ≤ x ≤ 2^(n−1) − 1

These ranges assume that all n bits are used for the value and that no special encoding is reserved.

2. Instruction Formats and Encoding

An instruction format specifies the location and size of fields within an encoded instruction. Typical fields include:

  1. opcode;
  2. source and destination register identifiers;
  3. addressing-mode bits;
  4. immediate data;
  5. displacement or offset;
  6. condition or control bits.

2.1 8086 instruction format

8086 instructions are variable in length, from 1 to 6 bytes. A general instruction may contain a prefix, opcode, ModR/M byte, displacement, and immediate data. Not every instruction contains every field.

The ModR/M byte helps identify register and memory operands. Its fields are:

FieldSizeFunction
MOD2 bitsSpecifies register or memory form and displacement size
REG3 bitsSelects a register or an opcode extension
R/M3 bitsSelects a register or effective-address combination

The 8086’s variable format permits compact instructions but makes decoding more complex than in a fixed-length architecture.

2.2 ARM instruction format

Classic 32-bit ARM instructions are commonly 32 bits long and contain fields such as a condition code, opcode, source and destination registers, and an immediate or shift specification. Thumb and Thumb-2 provide 16-bit and mixed-width encodings to improve code density.

A traditional ARM data-processing instruction often includes:

  • a condition field;
  • an operation field;
  • an S bit indicating whether flags are updated;
  • an Rn source register;
  • an Rd destination register;
  • a second operand, either a register or an immediate value.

Fixed or regular formats simplify instruction decoding and pipeline implementation. However, modern ARM instruction sets include several encoding formats, so “fixed length” should not be assumed for every ARM execution state.

3. Addressing Modes

An addressing mode specifies how the processor obtains an operand. If an instruction refers to memory, the processor calculates an effective address (EA). The exact formula depends on the architecture.

3.1 Immediate addressing

The operand is contained in the instruction itself.

MOV AX, 25       ; 8086
MOV R0, #25      ; ARM syntax

Immediate addressing is fast because no separate data-memory read is normally required. Its limitation is that the constant is restricted by the instruction encoding.

3.2 Register addressing

The operand is held in a register.

MOV AX, BX
ADD R0, R1, R2

Register access is usually faster than memory access and requires fewer instruction bytes. The number of available registers is therefore an important design characteristic.

3.3 Direct or absolute memory addressing

The instruction contains a memory address or an address field from which the memory location is determined.

MOV AL, [2000H]       ; conceptual 8086 notation

This mode is convenient for fixed memory locations, but the address field increases instruction size and may be unsuitable for position-independent code.

3.4 Register-indirect addressing

A register contains the address of the operand.

MOV AL, [BX]          ; 8086
LDR R0, [R1]          ; ARM

Register-indirect addressing is fundamental for pointers, arrays, buffers, and dynamically allocated data.

3.5 Based and indexed addressing

The effective address is formed by combining a base register and, where supported, an index register. In the 8086, common effective-address forms include:

EA = Base + Index + Displacement

For example:

MOV AL, [BX + SI + 10H]

The 8086 permits combinations involving BX or BP with SI or DI. BP-based addresses conventionally use the stack segment, while BX-based addresses conventionally use the data segment unless a segment override is supplied.

In ARM, a base register can be combined with an immediate or another register. Scaled register offsets are particularly useful for arrays whose elements have power-of-two sizes.

3.6 Indexed addressing for arrays

Suppose an array begins at address A, each element occupies s bytes, and element i is required. The byte address is:

Address(i) = A + i × s

This formula assumes zero-based indexing, contiguous elements, and a constant element size. For an array beginning at 1000H, with 2-byte elements, element 3 is at:

1000H + 3 × 2 = 1006H

3.7 Relative addressing

A branch target is calculated relative to the current instruction location or a related program counter value:

Target = BasePC + SignExtendedOffset

Relative addressing supports relocatable code. The exact base PC and scaling factor are architecture-specific. ARM branch instructions commonly use a signed, scaled offset; 8086 conditional jumps use signed displacements of defined sizes.

3.8 Implied and stack addressing

Some instructions imply their operands. For example, 8086 CLC clears the carry flag without naming it. Stack instructions implicitly use the stack pointer and stack segment. ARM PUSH and POP, when available in the selected instruction set, implicitly use the stack pointer and a register list.

3.9 ARM pre-indexed and post-indexed addressing

ARM load/store instructions can optionally update the base register:

LDR R0, [R1, #4]       ; access R1 + 4; R1 unchanged
LDR R0, [R1, #4]!      ; pre-index; update R1 before/with access
LDR R0, [R1], #4       ; post-index; update R1 after access

These forms are useful for traversing arrays, stacks, and buffers. The exact availability and syntax depend on the ARM instruction set version.

3.10 Comparison of addressing modes

ModeOperand locationMain useMain limitation
ImmediateInside instructionConstantsLimited constant size
RegisterRegister fileFast temporary dataLimited registers
DirectEncoded memory addressFixed variablesLarger code; less relocatable
Register indirectAddress in registerPointers and buffersRequires address preparation
Indexed/base-plus-offsetCalculated memory addressArrays and recordsEncoding restrictions
RelativePC-relative targetBranches and position-independent codeLimited range
ImpliedDefined by opcodeFlags, accumulator, stackLess explicit

4. Instruction Execution and Machine-Cycle Timing

Instruction execution is commonly described as a sequence of fetch, decode, operand-access, execute, and write-back activities. The precise sequence differs between processors and may overlap in a pipeline.

Instruction execution stages Fetchinstruction memory Decodeopcode and operands Accessregister or memory ExecuteALU or branch Writeresult Conceptual instruction cyclePipeline implementations may overlap stages for different instructions.
Instruction execution stages from fetch to write back
Figure: Instruction execution stages from fetch to write back

Figure: Conceptual stages of instruction execution; actual timing depends on the processor and instruction.

A machine cycle is a processor-defined bus or internal activity, such as an opcode fetch, memory read, memory write, or interrupt acknowledge. An instruction may require one or several machine cycles. A clock cycle is one period of the processor clock. If the clock frequency is f, the clock period is:

T = 1 ÷ f

where T is in seconds when f is in hertz. If an operation requires N clock cycles, its idealized execution time is:

t = N × T = N ÷ f

This estimate assumes no wait states, cache misses, bus contention, pipeline stalls, or interrupt delays. On the 8086, external bus cycles are commonly described using T-states. Memory wait states may be inserted when a device cannot respond within the normal bus timing. In pipelined ARM processors, instruction throughput and individual instruction latency must be distinguished; several instructions may be in different stages simultaneously.

5. Flags and Condition Codes

Flags are status bits updated by arithmetic, logical, or comparison operations. Important 8086 flags include:

  • CF (carry flag): carry out of the most significant bit in unsigned arithmetic, or borrow-related status in subtraction;
  • ZF (zero flag): result equals zero;
  • SF (sign flag): copy of the result’s most significant bit;
  • OF (overflow flag): signed result cannot be represented in the destination size;
  • PF (parity flag): even parity of the low-order result byte;
  • AF (auxiliary carry): carry between bit 3 and bit 4, useful in decimal adjustment.

ARM processors use condition flags commonly named N, Z, C, and V:

  • N indicates a negative sign bit;
  • Z indicates zero;
  • C indicates carry or no borrow according to the instruction’s defined subtraction convention;
  • V indicates signed overflow.

A crucial distinction is that carry and overflow are not equivalent. Carry concerns unsigned arithmetic; overflow concerns signed arithmetic.

For example, in 8-bit arithmetic:

01111111₂ = +127
00000001₂ = +1
10000000₂ = −128 (signed interpretation)

The mathematical result +128 cannot be represented as an 8-bit signed value, so signed overflow occurs. The interpretation of the same result as unsigned is 128, and the carry behavior must be determined from the binary addition.

Comparison instructions usually perform subtraction internally, update flags, and discard the arithmetic result. Conditional branches then test the flags. This allows programs to implement decisions, loops, and relational operations.

6. Stack Operations and Subroutines

A stack is a last-in, first-out (LIFO) storage structure. Its principal control register is the stack pointer (SP). A stack may grow toward higher or lower addresses; the convention is architecture-specific.

On the 8086, PUSH decrements SP by two and stores a word; POP reads a word and increments SP by two. Thus, for a 16-bit stack operation:

SPnew = SPold − 2 (PUSH)
SPnew = SPold + 2 (POP)

These relations assume the ordinary 8086 word stack operation and no interruption between the conceptual steps. The 8086 uses SS:SP to identify the stack location.

A subroutine is a reusable sequence invoked by a call instruction. A typical call mechanism saves a return address, transfers control to the subroutine, and later restores control using a return instruction.

CALL SUM        ; 8086: saves return address
...
SUM PROC
    ADD AX, BX
    RET
SUM ENDP

ARM commonly uses BL (branch with link), which places a return address in the link register, conventionally R14 or LR. A subroutine may preserve LR by saving it on the stack when it makes nested calls. A return can use an instruction such as BX LR, depending on the execution state and instruction-set version.

Stacks also support:

  • saving registers across calls;
  • local variables;
  • interrupt-context preservation;
  • parameter passing;
  • nested and recursive procedures.

A stack frame is a structured region associated with one procedure invocation. Good procedure design follows the platform’s calling convention, preserves required registers, maintains stack alignment, and restores the stack before returning.

7. Worked Example: Array Traversal

Consider five unsigned 16-bit values stored consecutively in memory beginning at address 1200H. The task is to add them and place the result in a register.

Each element occupies two bytes. The address of element i is:

Address(i) = 1200H + 2i

The final element, i = 4, is at 1208H. A conceptual 8086 solution could use SI as an index and CX as a loop counter:

MOV SI, 1200H
MOV CX, 5
XOR AX, AX
NEXT: ADD AX, [SI]
      ADD SI, 2
      LOOP NEXT

The addressing mode in ADD AX, [SI] is register indirect. The instruction ADD SI, 2 uses immediate addressing. LOOP uses an implicit counter register on the 8086. The example also demonstrates why element size must be included when advancing an array pointer.

If the values are 10, 20, 30, 40, and 50, the mathematical sum is:

10 + 20 + 30 + 40 + 50 = 150

The result fits in a 16-bit unsigned register. In production code, the programmer must still consider overflow when the array values or length are larger.

8. Applied Case: Embedded Sensor Controller

Background

This illustrative case concerns a small ARM-based environmental sensor controller. The controller samples temperature and humidity, stores readings in a circular buffer, and transmits a packet when a threshold is exceeded.

Problem

The first implementation used direct memory addresses and repeatedly recalculated buffer locations. It was difficult to relocate the buffer, and occasional stack corruption occurred during interrupt handling.

Analysis

A circular buffer is naturally represented by a base address and a moving index. Register-indirect addressing with post-indexing reduces address-management instructions. A subroutine handles packet construction, while an interrupt service routine saves the registers required by the calling convention. The program compares the sensor value with a threshold and branches according to the Z, N, and V/C-related condition rules appropriate to the selected data interpretation.

Approach and solution

The buffer pointer is held in a register and advanced by the record size after each store. When the pointer reaches the buffer limit, it is reset to the base address. The interrupt routine uses a documented stack frame, preserves nonvolatile registers, and returns through the architecture’s interrupt-return mechanism rather than an ordinary subroutine return. Loads and stores are used explicitly because ARM follows a load/store model: arithmetic instructions operate primarily on registers.

Outcome

The revised design is relocatable, easier to test, and less vulnerable to register or stack misuse. Timing analysis identifies sensor-access delays and interrupt latency rather than assuming that every instruction has identical execution time.

Learning points

  1. Addressing modes should match data structure access patterns.
  2. Calling conventions are part of reliable system design.
  3. Interrupt handlers require stricter context preservation than ordinary code.
  4. Timing must include memory and peripheral wait states.
  5. Clear separation between unsigned thresholds and signed measurements prevents flag errors.

9. Best Practices, Limitations, and Contemporary Development

Effective assembly programming begins with selecting the smallest clear instruction sequence, not merely the shortest sequence. Programmers should document operand sizes, signedness, flag effects, and assumptions about registers. They should avoid relying on flags that an intervening instruction may modify.

Common errors include confusing an address with the data stored at that address, using the wrong scale for an array index, failing to preserve a return address, and treating carry as signed overflow. Debuggers and instruction-set simulators are valuable for observing registers, memory, flags, and stack frames step by step.

Instruction timing is also architecture-dependent. Modern processors may use caches, branch prediction, speculative execution, pipelines, and out-of-order execution. Consequently, an instruction’s nominal cycle count may not equal the time observed in a complete program. Embedded systems with real-time requirements must consult the target processor’s technical reference manual and account for worst-case behavior.

Security is relevant to instruction-set use. Incorrect bounds calculations can cause buffer overflows; unsafe return-address handling can enable control-flow attacks; and speculative execution has exposed microarchitectural side channels. Secure coding therefore requires bounds checking, memory protection, controlled privilege transitions, and adherence to platform calling conventions.

Chapter Summary

An instruction set defines the operations a processor can perform and the encodings used to request them. Instruction formats contain opcodes and operand-related fields, while data types determine how bit patterns are interpreted. Addressing modes—including immediate, register, direct, register-indirect, indexed, relative, and stack addressing—determine how operands and targets are located.

The 8086 demonstrates variable-length instructions, segmented addressing, ModR/M encoding, word-oriented stack operations, and explicit machine-cycle concepts. ARM demonstrates regular register-based operations, load/store organization, condition flags, link-register subroutine calls, and flexible base-plus-offset addressing. Flags support comparisons and conditional execution, but carry must be distinguished from signed overflow. Stacks and calling conventions provide the foundation for subroutines, interrupts, recursion, and register preservation. Reliable processor programming requires attention to data size, signedness, effective-address calculation, timing assumptions, and secure control-flow management.

Key Terms

Instruction set: Complete collection of operations supported by a processor.

Opcode: Encoded field identifying the operation to be performed.

Operand: Data item, register, constant, or address used by an instruction.

Instruction format: Arrangement of opcode, register, addressing, and data fields.

Addressing mode: Rule used to locate an instruction operand.

Effective address: Calculated memory address of an operand.

Immediate addressing: Mode in which the operand is included in the instruction.

Register-indirect addressing: Mode in which a register contains the operand address.

Displacement: Constant offset added to a base or program-counter value.

Relative addressing: Addressing based on a program-counter-related value.

Flag: Status bit recording an arithmetic, logical, or processor condition.

Carry flag: Status associated primarily with unsigned carry or borrow behavior.

Overflow flag: Status indicating signed range overflow.

Stack pointer: Register identifying the active stack location.

Subroutine: Reusable sequence of instructions invoked by a call.

Calling convention: Rules for arguments, return values, registers, and stack use.

Machine cycle: Processor-defined bus or internal operation used during execution.

Clock period: Time for one processor clock cycle, equal to 1 ÷ frequency.

Load/store architecture: Design in which memory is accessed explicitly by load and store instructions.

Pipeline: Overlapped execution stages used to improve instruction throughput.

Review Questions

Short Answer Questions

  1. What is the difference between an opcode and an operand?
  2. Define effective address and give one example of how it is calculated.
  3. Why is immediate addressing generally faster than direct memory addressing?
  4. Distinguish the carry flag from the overflow flag.
  5. What information is normally preserved when a subroutine is called?

Long Answer Questions

  1. Explain the principal 8086 addressing modes and illustrate each with an assembly-language example.
  2. Compare the instruction formats and decoding characteristics of the 8086 and ARM architectures.
  3. Describe stack operation, stack frames, and subroutine return mechanisms in the 8086 and ARM.
  4. Explain the conceptual stages of instruction execution and discuss why actual timing may differ from a simple cycle count.
  5. Discuss how flags support comparison, branching, loops, and signed or unsigned arithmetic.

Analytical / Application Questions

  1. An array begins at 2400H and contains 12-bit values stored in 2-byte units. Derive the address of element 7 and explain a suitable addressing mode for accessing it.
  2. Design a subroutine interface for an ARM sensor-processing routine. Specify argument registers, return-value handling, preserved registers, and stack requirements.
  3. A real-time controller uses a 20 MHz clock and an operation requiring 16 clock cycles under zero-wait-state conditions. Calculate the ideal execution time and identify two practical factors that could increase it.

Multiple Choice Questions with Answers

Question 1

Which field identifies the operation to be performed by a machine instruction?

A. Opcode

B. Displacement

C. Stack pointer

D. Parity bit

Correct Answer: A. Opcode

Question 2

In register-indirect addressing, where is the operand address held?

A. In the opcode

B. In a register

C. In the flag register only

D. In the instruction decoder

Correct Answer: B. In a register

Question 3

For a zero-based array beginning at address A, with element size s bytes, what is the address of element i?

A. A + i + s

B. A × i × s

C. A + i × s

D. A − i × s

Correct Answer: C. A + i × s

Question 4

Which 8086 instruction conventionally decrements SP by two?

A. POP

B. PUSH

C. RET

D. CLC

Correct Answer: B. PUSH

Question 5

Which flag primarily indicates signed arithmetic overflow?

A. ZF

B. CF

C. PF

D. OF

Correct Answer: D. OF

Question 6

Which ARM instruction is commonly associated with loading data from memory into a register?

A. LDR

B. STR

C. CMP

D. BIC

Correct Answer: A. LDR

Question 7

What is the principal purpose of a subroutine return instruction?

A. Clear all flags

B. Restore execution to the saved return address

C. Change every memory address

D. Disable the stack

Correct Answer: B. Restore execution to the saved return address

Question 8

A processor has a 20 MHz clock. What is its clock period under the ideal definition T = 1 ÷ f?

A. 5 ns

B. 20 ns

C. 50 ns

D. 200 ns

Correct Answer: C. 50 ns

Question 9

Which addressing mode is especially suitable for a branch whose target should move with the program?

A. Relative addressing

B. Implied addressing

C. Accumulator addressing

D. Immediate addressing

Correct Answer: A. Relative addressing

Question 10

Why must carry and overflow be interpreted differently?

A. Carry applies to instructions only, while overflow applies to memory only

B. Carry concerns unsigned arithmetic, while overflow concerns signed representation

C. Carry is always zero after subtraction

D. Overflow is used only by stack instructions

Correct Answer: B. Carry concerns unsigned arithmetic, while overflow concerns signed representation

Microprocessors and Microcontrollers
Chapter 3 of 14