Chapter 4
Instruction-Set Architecture and Addressing
Bachelors Program in Electrical Engineering
Microprocessors & Microcontrollers
•
Bachelor
Instruction-Set Architecture and Addressing
Introduction
An instruction-set architecture (ISA) is the programmer-visible specification of a processor. It defines the operations that software may request, the registers and memory model visible to programs, the binary encoding of instructions, the condition-code behavior, and the mechanisms used for control transfer and procedure calls. The ISA therefore forms the contract between compiler, assembly-language programmer, operating system, and hardware implementation.
This chapter develops the central ideas needed to read, design, and evaluate assembly instructions. Examples use generic register names such as R0, R1, and PC rather than a single commercial processor. Particular processors differ in opcode values, register widths, flags, and syntax, but the architectural principles are widely applicable to 8-, 16-, 32-, and 64-bit microprocessors and microcontrollers.
Learning Objectives
After studying this chapter, a student should be able to:
Interpret opcodes, operands, instruction formats, and condition flags.
Explain data-transfer, arithmetic, logical, branch, stack, and control instructions.
Compare immediate, register, direct, indirect, indexed, relative, and implied addressing.
Calculate approximate code size and execution time from instruction descriptions.
Select addressing modes and instruction sequences for compact, fast, and maintainable code.
Explain architectural trade-offs involving registers, memory, instruction length, and control flow.
1. The Programmer's Model of a Processor
A processor's programmer-visible state commonly includes:
General-purpose registers, used for operands, addresses, counters, or temporary values.
A program counter (PC), containing the address of the next instruction to be fetched.
A stack pointer (SP), identifying the top of a runtime stack.
Status or condition-code register, containing flags produced by selected operations.
Memory and input/output address space, accessed through defined instructions or memory operations.
The execution of an instruction can be described as a sequence of conceptual phases: fetch the instruction at the PC, decode its opcode and operands, obtain source data, execute the operation, write the result, update flags, and advance or replace the PC. Real processors overlap these activities through pipelining, but the conceptual model remains useful.
An instruction consists of an opcode field, which identifies the operation, and one or more operand fields. Some fields identify registers; others contain constants, addresses, displacement values, or addressing-mode codes. An instruction may be fixed-length or variable-length. Fixed-length instructions simplify fetching and decoding, whereas variable-length instructions can improve code density when common operations have compact encodings.
Generic instruction execution and encoding model
Figure: Generic instruction execution and encoding model
Figure: A generic instruction format and conceptual execution sequence.
2. Instruction Formats and Opcodes
An instruction format specifies the location and width of each encoded field. A three-register arithmetic instruction might be represented conceptually as
OP | Rd | Rs1 | Rs2
where OP identifies the operation, Rd is the destination register, and Rs1 and Rs2 are source registers. An immediate form may instead be
OP | Rd | Rs | immediate.
The number of bits allocated to a register field limits the number of directly selectable registers. If a field has n bits, it can encode up to 2n distinct values, assuming all encodings are available. An immediate field of k bits represents an unsigned range from 0 to 2k − 1. If interpreted as two's-complement signed data, its range is −2k−1 through 2k−1 − 1. These ranges apply only when the entire field is used for that purpose and no encoding is reserved.
An opcode table maps binary patterns to operations. For example, a processor might define a pattern as ADD, another as LOAD, and another as conditional branch. The numerical opcode is not universal: the same binary pattern can mean different operations in different ISAs. Correct interpretation therefore requires the processor's instruction-set reference manual.
Instruction formats also communicate architectural priorities. A register-register format supports fast arithmetic with regular decoding. A memory-operand format can reduce the number of explicit loads but requires more complex decoding and may incur memory latency. A compact instruction set may reduce program memory and instruction-fetch energy, while a wide instruction can encode more operands or a larger immediate value.
3. Data-Transfer Instructions
Data-transfer instructions move information without necessarily changing its numerical value. Typical forms include:
LOAD: memory to register, for example LOAD\ R1,[R2].
STORE: register to memory, for example STORE\ R1,[R2].
MOVE: register to register or immediate to register.
PUSH and POP: transfer data between registers and the stack.
Exchange: swap two register or memory values on architectures that support it.
Input/output transfer: communicate with peripheral registers in separate-I/O architectures.
The notation [R2] means that the contents of R2 are used as an address; it does not mean that the contents of R2 themselves are transferred. This distinction between a value and an address containing a value is fundamental.
A load/store architecture restricts ordinary arithmetic instructions to register operands. A memory-to-memory architecture permits arithmetic directly on memory locations. Load/store designs usually simplify pipelines and timing analysis, while memory-operand instructions can provide better code density for some programs.
Data movement may involve alignment and size. A word load may require an address aligned to the word boundary, or the hardware may perform multiple memory accesses for an unaligned address. Byte, half-word, word, and double-word operations must also define whether loaded data is sign-extended or zero-extended.
4. Arithmetic and Logical Instructions
Arithmetic instructions include addition, subtraction, multiplication, division, increment, decrement, and sometimes multiply-accumulate operations. For an unsigned addition of two w-bit operands, the mathematical result may require w+1 bits. The low w bits are stored, and the extra bit is commonly reported by the carry flag.
For signed two's-complement addition, overflow occurs when two operands of the same sign produce a result with the opposite sign. Carry and signed overflow are different conditions and must not be confused. A processor may set both independently.
Logical instructions operate bit by bit:
AND masks selected bits or tests whether selected bits are zero.
OR sets selected bits.
XOR toggles selected bits or compares bit patterns for inequality.
NOT complements every bit.
Shift and rotate instructions move or circulate bits.
For a left shift by one position, each bit moves toward the more significant position and a zero enters the least significant position; the bit leaving the most significant position may enter the carry flag. A logical right shift inserts zero at the most significant position. An arithmetic right shift replicates the sign bit and is useful for signed division by powers of two when rounding behavior is acceptable.
Condition Flags
Common flags include:
Z (zero): the result is zero.
N or S (negative/sign): the result's most significant bit is one, under two's-complement interpretation.
C (carry): an unsigned carry out or borrow-related condition, as defined by the ISA.
V or O (overflow): signed arithmetic overflow.
P (parity): parity of the result on some processors.
Not every instruction modifies every flag. A subsequent conditional branch may depend on flags produced by an earlier arithmetic or comparison instruction. Compiler writers and assembly programmers must therefore consult the instruction specification rather than assume that all flags are updated.
5. Branch, Call, and Control Instructions
Sequential execution normally increments the PC by the instruction length. A branch replaces the normal next PC with a target address. Branches may be:
Unconditional, always transferring control.
Conditional, transferring only when a flag or register condition is true.
Relative, using a signed displacement from the current PC or a defined reference point.
Indirect, obtaining the target from a register or memory location.
Computed, selecting among targets for a switch statement or jump table.
A relative branch with a signed displacement of k bits can reach a range determined by the displacement scaling and the architecture's definition of the reference PC. If instruction addresses are aligned to A bytes and the encoded displacement represents instruction units, the byte displacement is the encoded signed value multiplied by A. The exact range must be calculated from the ISA specification because some processors add the displacement to the address of the branch instruction, while others use the address of the following instruction.
A CALL saves a return address and transfers control to a procedure. RETURN restores that address. The return address may be placed on a hardware stack, in a link register, or in both. Conditional branches and calls are central to loops, decisions, interrupts, and subroutine-based software.
Control instructions include reset, halt, wait-for-interrupt, enable/disable interrupts, supervisor or user-mode transitions, and memory-barrier operations. These instructions have system-wide effects and normally require privileged execution or careful synchronization.
6. Stack Architecture
A stack is a last-in, first-out structure. The SP identifies the top, but the exact push and pop convention varies. In a descending stack, pushing commonly decreases SP before storing; in an ascending stack, pushing increases SP before storing. Some architectures define pre-decrement, post-decrement, pre-increment, or post-increment forms.
The stack stores return addresses, saved registers, function arguments, local variables, and interrupt context. A procedure should preserve any registers that the calling convention designates as callee-saved and may use caller-saved registers without restoring them. The calling convention is not merely an assembly detail: it enables separately compiled modules and operating-system interfaces to cooperate.
Stack errors include overflow, underflow, incorrect restoration order, and misalignment. Recursive functions require sufficient stack space for every active invocation. Interrupt handlers must preserve all state that can be modified and must follow the processor's interrupt-return protocol.
7. Addressing Modes
An addressing mode defines how an instruction obtains an operand or computes an effective address (EA). The following modes are common.
Addressing mode
Meaning
Typical use
Immediate
Operand is encoded in the instruction
Constants, masks
Register
Operand is in a named register
Fast arithmetic
Direct/absolute
Address field identifies a memory location
Global variables, fixed peripherals
Register indirect
Register contains the address
Pointers, buffers
Base plus displacement
EA = base register + signed offset
Structures, stack frames
Indexed
EA = base + index, sometimes scaled
Arrays, tables
PC-relative
EA or branch target uses PC plus displacement
Position-independent code
Auto-increment/decrement
Register indirect with automatic update
Sequential buffers, stacks
Implied/implicit
Operand is inherent in the opcode
Accumulator, flags, SP
The general effective-address expression for a scaled indexed mode is often:
EA = B + I × S + D
Here, EA is the effective byte address, B is the base-register value, I is an index value, S is a scale factor such as 1, 2, 4, or 8, and D is a displacement. All terms must be expressed in bytes. This form is useful for arrays of fixed-size elements and records containing fields.
Worked Example: Array Addressing
Suppose an array begins at address 0x2000, each element occupies 4 bytes, and element index i = 6. With a base-plus-scaled-index mode, the address is
EA = 0x2000 + 6 × 4 = 0x2018.
The scale factor is 4 because the element size is 4 bytes. If the index were already a byte offset, multiplying by 4 would be incorrect. This is a common source of pointer bugs.
Worked Example: Stack-Frame Addressing
Assume a procedure's frame pointer contains 0x8000 and a local 32-bit variable is located 12 bytes below it. A signed displacement of −12 gives
EA = 0x8000 − 12 = 0x7FF4.
The displacement is measured in bytes, and the resulting address must satisfy any alignment requirement of the 32-bit load or store.
8. Comparing Code Density, Execution Time, and Flexibility
Instruction-set choices involve competing objectives. Code density is the amount of program memory required. A simple measure is
D = Nbytes
where D is code size in bytes and N_bytes is the total number of encoded instruction bytes. A lower value generally improves instruction-cache use and reduces nonvolatile-memory cost, but it is not automatically faster.
For a sequence of instructions, execution time can be estimated as
T = ∑(i=1 to n) ti + Twait + Tpenalty
where t_i is the nominal time for instruction i, T_wait represents memory or peripheral wait states, and T_penalty includes effects such as pipeline stalls, cache misses, and branch mispredictions. The expression is an estimate: on pipelined or superscalar processors, instructions overlap, so summing isolated instruction latencies may overestimate actual time.
If a loop executes N times and its body has a steady-state cost of C cycles, with setup cost C₀ and exit cost Cₑ, then a simple estimate is
Ctotal ≈ C₀ + N × C + Cₑ
This approximation assumes that memory behavior, branch prediction, and instruction scheduling are stable across iterations. On a microcontroller without caches, it may be quite accurate; on a modern out-of-order processor, measurement or a detailed pipeline model is preferable.
A compact instruction may reduce code size but require extra decoding or memory access. A longer instruction may encode a larger constant and avoid a separate load. Register addressing is normally fast, while indirect memory addressing offers flexibility at the cost of memory latency. PC-relative addressing supports relocatable code but has a finite reach and may require a long-branch sequence when the target is distant.
Worked Comparison
Consider two hypothetical implementations of a three-operation calculation:
Sequence A: three 2-byte instructions, each taking 1 cycle, plus one 2-cycle load.
Sequence B: two 4-byte instructions, each taking 2 cycles.
Sequence A has 8 bytes and an estimated 5 cycles. Sequence B has 8 bytes and an estimated 4 cycles. Thus both have equal code size, but B is faster under the stated assumptions. If Sequence B causes an additional memory wait of 3 cycles, its total becomes 7 cycles and Sequence A is preferable. The result depends on the complete memory and pipeline context, not opcode count alone.
9. Worked Instruction Interpretation
Consider the following generic sequence, in which each register and memory word is 32 bits:
The first instruction places the immediate value 5 in R1. The second places 3 in R2. ADD computes 5 + 3 = 8 and writes the result to R1. STORE computes the address held in R0 plus displacement 8 and writes the 32-bit value 8 there.
If R0 = 0x1000, the store address is 0x1008. The final memory word at 0x1008 is 8, assuming the address is valid, aligned, and not affected by an intervening concurrent agent. Whether ADD changes flags depends on the ISA; whether MOV changes flags also depends on the ISA.
An equivalent memory-operand instruction might combine loading, arithmetic, and storing, but it could have a longer encoding and more complicated timing. The register-based version is usually easier for a simple pipeline to decode and schedule.
10. Applied Case: Real-Time Sensor Controller
Background
Consider an illustrative battery-powered sensor controller that samples an analogue sensor, filters the reading, checks a threshold, and transmits an alarm packet. The microcontroller has a 16-bit data path, a small flash memory, memory-mapped peripheral registers, and no instruction cache.
Problem
The original implementation uses absolute addresses for every peripheral access and a long sequence of register moves. It meets functional requirements but occupies too much flash and occasionally misses a sampling deadline.
Analysis
The sensor data register is located at a fixed peripheral address, so direct addressing is appropriate. The filter operates on a circular buffer, so register-indirect addressing with auto-increment is useful. The threshold is a constant that fits in an immediate field. The alarm routine is called frequently and should use a short relative call if the target is within range.
The designer compares two versions. Version 1 loads the buffer pointer from memory on every iteration. Version 2 retains the pointer in a register and uses post-increment addressing. Version 2 reduces both instruction count and memory traffic. However, it consumes a register that could otherwise hold a frequently used temporary. The final choice depends on register pressure and compiler allocation.
Approach and Solution
The optimized sequence uses direct addressing for the sensor register, immediate addressing for the threshold, register-indirect post-increment for the sample buffer, and a conditional branch for the alarm test. The interrupt handler saves the registers it modifies, clears the peripheral interrupt source, and returns using the architecture-defined interrupt-return instruction. The main program uses a bounded loop so that the worst-case execution time can be estimated.
Outcome
Under the stated processor timing model, the optimized loop removes one memory load per sample and reduces the steady-state body from 14 cycles to 10 cycles. For a 1 kHz sampling rate, this saves approximately 4,000 cycles per second. Flash usage also falls because short immediate and post-increment encodings replace longer address calculations. The improvement is valid only after verifying that the selected registers are preserved correctly and that the peripheral's address and access width requirements are respected.
Learning Points
The case illustrates that addressing-mode selection is an architectural design decision. Direct addressing is efficient for fixed hardware registers; indexed addressing suits data structures; immediate addressing is ideal for small constants; and register allocation affects both speed and code size. Functional correctness, timing, interrupt safety, and maintainability must be evaluated together.
11. Limitations, Security, and Best Practices
Instruction semantics can be affected by memory ordering, privilege level, interrupt timing, and exceptions. A peripheral register may have read-to-clear or write-one-to-clear behavior, so treating it like ordinary RAM can lose events. Memory-mapped I/O should be declared and accessed in a way that prevents inappropriate compiler reordering.
Indirect branches and returns require special attention in security-sensitive systems. Corrupted function pointers or return addresses can redirect execution. Stack guards, control-flow integrity, privilege separation, and non-executable memory are complementary protections, although their availability depends on the processor and operating system.
Best practices include:
Use the official ISA manual for opcode, flag, alignment, and timing details.
Distinguish signed and unsigned comparisons and use the correct condition code.
Track whether every instruction updates flags needed later.
Check displacement range and relocation requirements during linking.
Preserve registers according to the calling convention.
Measure critical code on the target hardware, including wait states and interrupts.
Prefer clear register allocation and comments that explain intent, not merely syntax.
Treat peripheral registers as devices with specified side effects, not ordinary memory.
Contemporary Developments
Modern embedded ISAs increasingly provide compressed instruction encodings, enabling common operations to use fewer bits while retaining a regular 32-bit base architecture. Bit-manipulation extensions accelerate masking, extraction, population counts, and cyclic redundancy checks. Vector and packed-SIMD instructions process several data elements per operation, improving signal-processing throughput but introducing alignment, lane-width, and portability considerations.
Security-oriented extensions add privileged execution levels, memory-protection units, cryptographic instructions, pointer authentication, or capabilities. These features extend the ISA contract beyond arithmetic performance to include isolation and trustworthy control flow. Nevertheless, no instruction extension replaces sound software design, correct privilege configuration, and verification.
Chapter Summary
An ISA defines the programmer-visible operations, registers, instruction formats, memory model, flags, and control mechanisms of a processor. An instruction contains an opcode and operand-related fields, while its addressing mode determines how operands or effective addresses are obtained. Data-transfer instructions move values; arithmetic and logical instructions transform them; branches, calls, and returns control execution; and stack instructions support procedures and interrupts.
Immediate, register, direct, indirect, indexed, PC-relative, auto-update, and implied addressing modes provide different balances of compactness, flexibility, speed, and hardware complexity. Effective-address calculations must respect byte units, alignment, displacement interpretation, and pointer semantics. Code density and execution time are related but distinct: a smaller program is not necessarily faster, and timing estimates must account for memory waits, pipelines, caches, and branch behavior. Professional assembly and compiler work therefore requires both exact interpretation of the ISA and careful evaluation of system-level constraints.
Key Terms
ISA: Programmer-visible specification of a processor.
Opcode: Encoded field identifying an operation.
Operand: Data item, register, or address used by an instruction.
Instruction format: Arrangement and width of fields in an instruction encoding.
Program counter (PC): Register containing the current or next instruction address.
Stack pointer (SP): Register identifying the stack top.
Condition flag: Status bit describing an arithmetic or logical result.
Effective address: Address calculated by an addressing mode.
Immediate addressing: Operand constant encoded in the instruction.
Register-indirect addressing: Register supplies a memory address.
Indexed addressing: Address formed from a base and index, often with scale.
PC-relative addressing: Address or branch target formed relative to the PC.
Load/store architecture: Design in which arithmetic operates primarily on registers.
Code density: Program size measured by encoded instruction storage.
Branch displacement: Encoded offset used to calculate a branch target.
Calling convention: Rules for arguments, return values, and register preservation.
Pipeline: Overlapped implementation stages used to improve instruction throughput.
Memory-mapped I/O: Peripheral registers assigned addresses in the memory space.
Signed overflow: Failure of a signed result to fit in its representation.
Instruction latency: Time or cycles before an instruction's result is available.
Review Questions
Short Answer Questions
What are the principal fields found in a typical machine instruction?
Distinguish carry from signed overflow.
What is the difference between a value in R2 and the address denoted by [R2]?
Why is PC-relative addressing useful for relocatable programs?
State two responsibilities of a stack in a procedure-based program.
Long Answer Questions
Explain the fetch, decode, operand-access, execute, and write-back phases of instruction processing, and relate them to instruction encoding.
Compare immediate, register, direct, indirect, and indexed addressing in terms of flexibility, code size, and likely execution cost.
Explain how condition flags support arithmetic, comparison, and conditional branching.
Discuss the advantages and disadvantages of load/store architectures compared with architectures allowing memory operands in arithmetic instructions.
Explain how calling conventions, stack frames, saved registers, and return addresses cooperate during a procedure call.
Analytical / Application Questions
A 32-bit array begins at 0x4000 and has 8-byte elements. Derive the effective address of element 9 using a scaled-index expression, and state the required scale factor.
Two loop implementations occupy 12 and 16 bytes. The first requires 6 cycles per iteration and the second 4 cycles, but the second incurs a 5-cycle instruction-fetch penalty once per loop execution. For 100 iterations, compare the nominal cycle totals and explain what additional hardware information would be needed for a final decision.
Design an addressing-mode strategy for a memory-mapped sensor controller that accesses one fixed peripheral register, a circular sample buffer, and a small constant threshold. Justify each selected mode and identify one possible hazard.
Multiple Choice Questions with Answers
Question 1
Which field identifies the operation to be performed by an instruction?
A. Opcode
B. Stack pointer
C. Displacement only
D. Parity flag
Correct Answer: A. Opcode
Question 2
In register-indirect addressing, what does the register normally contain?
A. The instruction opcode
B. The memory address of the operand
C. The number of condition flags
D. The instruction length
Correct Answer: B. The memory address of the operand
Question 3
What is the effective address for B = 0x2000, I = 6, S = 4, and D = 0 in EA = B + I × S + D?
A. 0x2006
B. 0x2010
C. 0x2018
D. 0x2060
Correct Answer: C. 0x2018
Question 4
Which flag most directly indicates that an unsigned addition produced a carry out of the most significant bit?
A. Zero flag
B. Carry flag
C. Negative flag
D. Parity flag
Correct Answer: B. Carry flag
Question 5
What is the principal purpose of a conditional branch?
A. To copy a constant into a register
B. To transfer control when a specified condition is satisfied
C. To permanently disable the stack
D. To decode an opcode
Correct Answer: B. To transfer control when a specified condition is satisfied
Question 6
Which addressing mode is especially suitable for a small constant encoded within the instruction?
A. Immediate
B. Register indirect
C. Indexed
D. Stack relative only
Correct Answer: A. Immediate
Question 7
A 16-bit signed immediate field using two's-complement representation has what range?
A. 0 to 65,535
B. −65,535 to 65,535
C. −32,768 to 32,767
D. −16,384 to 16,383
Correct Answer: C. −32,768 to 32,767
Question 8
Which statement about code size and execution time is correct?
A. The shortest code is always the fastest
B. Instruction count alone determines exact execution time
C. Smaller code may still be slower because of memory or pipeline effects
D. Code size has no relationship to instruction encoding
Correct Answer: C. Smaller code may still be slower because of memory or pipeline effects
Question 9
What is a common purpose of a calling convention?
A. To define how separately compiled procedures exchange arguments and preserve registers
B. To determine the electrical supply voltage
C. To replace all branch instructions
D. To make every instruction the same length
Correct Answer: A. To define how separately compiled procedures exchange arguments and preserve registers
Question 10
For a 4-byte array element, which scale factor is appropriate when the index is an element number rather than a byte offset?
A. 0
B. 1
C. 2
D. 4
Correct Answer: D. 4
Microprocessors & Microcontrollers
Chapter 4
of 14