Opcode Tables - The Bit Dictionary Inside a CPU
An assembler mnemonic is written for humans. The CPU executes bits. An opcode table is the dictionary between those two worlds.
When you write ADD R5, R3, the assembler does not send the letters A-D-D to the CPU. It looks up the instruction format, places register numbers and immediate values into fixed bit fields, and emits a binary instruction word. The decoder inside the CPU is built to recognize those bit patterns and assert the matching control signals.
Learning Objectives
By the end of this lesson, you should be able to:
- read an opcode table as a fixed bit-field layout;
- distinguish opcode bits from operand fields;
- encode simple AVR and ARM Thumb instructions by hand;
- explain why register fields may be split or restricted;
- connect instruction encoding to decoder hardware.
Why Instruction Tables Exist
Every instruction word has limited space. For a 16-bit instruction, the architecture must divide those 16 bits among:
- fixed opcode bits that identify the instruction family;
- register fields;
- immediate fields;
- branch offsets;
- condition or mode bits.
The exact bit positions are architecture-specific. AVR, ARM Thumb, RISC-V compressed instructions, and 8051 all make different trade-offs.
Reading Bit-Field Notation
Opcode tables usually number bits from most significant to least significant.
bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
word: 1 1 1 0 K K K K d d d d K K K K
Common symbols:
| Symbol | Meaning |
|---|---|
0, 1 |
fixed opcode bit |
d |
destination register field |
r, m, n |
source register fields |
K, i |
immediate constant bits |
k |
branch or address offset bits |
If a field is split, the assembler still treats it as one value. It simply places different bits of that value into different locations.
AVR Example: ADD Rd, Rr
Classic AVR ADD has this format:
0000 11rd dddd rrrr
| Bits | Field | Meaning |
|---|---|---|
| 15..10 | 000011 |
fixed opcode for ADD |
| 9 | r |
bit 4 of source register Rr |
| 8..4 | ddddd |
destination register Rd |
| 3..0 | rrrr |
bits 3..0 of source register Rr |
Worked encoding: ADD R5, R3
Rd = 5 = 00101
Rr = 3 = 00011
format: 0000 11 r ddddd rrrr
0000 11 0 00101 0011
binary: 0000 1100 0101 0011
hex: 0x0C53
The source register field is split because AVR's instruction map was packed tightly. Split fields are common in real instruction sets.
AVR Example: LDI Rd, K
LDI loads an 8-bit immediate into registers R16 through R31.
1110 KKKK dddd KKKK
| Bits | Field | Meaning |
|---|---|---|
| 15..12 | 1110 |
fixed opcode for LDI |
| 11..8 | upper K nibble |
immediate bits 7..4 |
| 7..4 | dddd |
destination register minus 16 |
| 3..0 | lower K nibble |
immediate bits 3..0 |
Worked encoding: LDI R16, 0x42
K = 0x42 = 0100 0010
R16 maps to dddd = 0000
format: 1110 KKKK dddd KKKK
binary: 1110 0100 0000 0010
hex: 0xE402
Important restriction: LDI R5, 0x42 is invalid on classic AVR because the dddd field cannot name R0 through R15.
AVR Example: RJMP k
RJMP uses a signed 12-bit word offset:
1100 kkkk kkkk kkkk
The target is computed relative to the next instruction:
target word address = PC + 1 + sign_extend(k)
The 12-bit signed range is:
-2048 to +2047 words
Because AVR program memory is word-addressed for instruction fetch, this is about -4096 to +4094 bytes of reach for 16-bit instruction words. Always check the exact wording in the device manual and assembler listing.
ARM Thumb Example: ADDS Rd, Rn, Rm
A common 16-bit Thumb register add form is:
0001 100m mmnn nddd
| Bits | Field | Meaning |
|---|---|---|
| 15..9 | 0001100 |
fixed opcode for register ADDS |
| 8..6 | mmm |
second source register Rm, low registers only |
| 5..3 | nnn |
first source register Rn, low registers only |
| 2..0 | ddd |
destination register Rd, low registers only |
Worked encoding: ADDS R0, R1, R2
Rm = R2 = 010
Rn = R1 = 001
Rd = R0 = 000
format: 0001 100m mmnn nddd
binary: 0001 1000 1000 1000
hex: 0x1888
The 3-bit register fields can name only R0 through R7. Thumb has other encodings for high registers, but they use different bit layouts.
ARM Thumb Example: MOVS Rd, #imm8
Thumb move-immediate format:
0010 0ddd iiii iiii
Worked encoding: MOVS R3, #25
Rd = R3 = 011
imm8 = 25 = 0x19 = 0001 1001
format: 0010 0ddd iiii iiii
binary: 0010 0011 0001 1001
hex: 0x2319
The opcode table tells the assembler exactly where the register number and immediate byte go. The decoder later checks the fixed top bits to select the move-immediate datapath.
Side-by-Side Trade-Offs
| Instruction | Width | Fixed opcode bits | Operand fields | Main trade-off |
|---|---|---|---|---|
AVR ADD Rd,Rr |
16 | 6 | two 5-bit registers, one split | full 32-register access |
AVR LDI Rd,K |
16 | 4 | 4-bit high-register field, 8-bit immediate | immediate fits, register range limited |
AVR RJMP k |
16 | 4 | signed 12-bit offset | good relative reach |
Thumb ADDS Rd,Rn,Rm |
16 | 7 | three 3-bit low-register fields | compact but low registers only |
Thumb MOVS Rd,#imm8 |
16 | 5 | 3-bit register, 8-bit immediate | compact literal load |
There is no universal "best" layout. More bits for opcodes mean more instruction variants. More bits for operands mean larger register numbers, wider immediates, or farther branches.
How the Decoder Uses the Table
The decoder does not search a software table at runtime. The opcode table describes hardware comparisons and routing.
Modern decoders can be pipelined, table-driven in microcode, or split into multiple decode stages, but the basic idea remains: fixed bit patterns choose control signals.
Practical Checks
- Verify bit numbering before hand-encoding.
- Check whether immediates are signed, unsigned, shifted, scaled, or split.
- Check whether register fields name all registers or only a subset.
- Confirm whether branch offsets are measured in bytes, halfwords, or words.
- Use the assembler listing file to compare your hand encoding with tool output.
- Remember endianness affects how bytes are stored in memory, not the logical bit layout shown in the opcode table.
Common Mistakes
- Treating a split field as two separate values.
- Forgetting an offset is relative to
PC + 1or another architecture-specific base. - Encoding an immediate that is too large for the field.
- Using a high register in a low-register-only Thumb instruction.
- Confusing instruction-word bit order with byte order in memory.
- Assuming two architectures encode similar mnemonics similarly.
Summary
Opcode tables are precise maps from assembly syntax to instruction bits. Fixed opcode bits identify the instruction; operand fields carry register numbers, immediates, and offsets. Reading these tables explains assembler errors, register restrictions, branch ranges, instruction size, and the decoder hardware that turns bits into control signals.
Further Reading
- Microchip, AVR Instruction Set Manual, instruction encodings and status effects.
- Arm, Thumb Instruction Set Quick Reference Card and Cortex-M architecture documentation.
- Patterson and Hennessy, Computer Organization and Design, instruction formats and datapath control.
- GNU Binutils documentation, assembler listing files and disassembly tools.