8086 Register Set
The 8086's fourteen registers — your processor's fast scratchpad for data, addresses, and status
Open interactive version (quiz + challenge)Real-world analogy
What is it?
The 8086 has fourteen 16-bit registers organized into four groups. Four general-purpose registers (AX, BX, CX, DX) handle data and arithmetic, with each splittable into two 8-bit halves. Two pointer registers (SP, BP) manage the stack. Two index registers (SI, DI) support array and string operations. Four segment registers (CS, DS, SS, ES) define 64 KB windows into the 1 MB address space. The Flags register holds status and control bits. The Instruction Pointer (IP) tracks the next instruction to execute. Together, these 14 registers form the complete programmer-visible state of the 8086.
Real-world relevance
Every x86-64 register you use today is a direct extension of the original 8086 registers. EAX (32-bit) and RAX (64-bit) are extensions of AX. When you debug C code and see variables stored in EAX, ESP, or EDI, you are looking at the grandchildren of the 8086 registers. Compilers still respect the implicit roles: ECX for loop counters, EAX for return values, ESP for the stack pointer — conventions that trace directly back to the 8086.
Key points
- General-Purpose Registers: AX (Accumulator) — AX is the primary accumulator for arithmetic and I/O. Many instructions implicitly use AX: MUL/DIV use AX for operands and results, IN/OUT transfer data through AL/AX, and string operations like LODSW load into AX. It is split into AH (high byte) and AL (low byte), each independently accessible.
- General-Purpose Registers: BX (Base) — BX serves as a base address register for memory access. It is the only general-purpose register that can be used in base-addressing modes like [BX], [BX+SI], and [BX+DI]. It also works as a general data register for arithmetic. Splits into BH and BL.
- General-Purpose Registers: CX (Counter) — CX is the designated counter register. LOOP, LOOPZ, and LOOPNZ decrement CX and branch based on its value. REP prefixes for string operations use CX as the repeat count. Shift and rotate instructions use CL for variable shift counts. Splits into CH and CL.
- General-Purpose Registers: DX (Data) — DX extends AX for 32-bit operations. MUL produces a 32-bit result in DX:AX. DIV takes a 32-bit dividend from DX:AX. DX also holds the port number for IN/OUT instructions when the port address exceeds 8 bits. Splits into DH and DL.
- Pointer Registers: SP and BP — SP (Stack Pointer) always points to the top of the stack in the SS segment. PUSH decrements SP, POP increments it. You should rarely modify SP directly. BP (Base Pointer) is used to access parameters and local variables in stack frames. BP defaults to the SS segment, making it ideal for procedure stack access.
- Index Registers: SI and DI — SI (Source Index) and DI (Destination Index) are used for indexed memory access and string operations. LODSB reads from DS:SI, STOSB writes to ES:DI, MOVSB copies from DS:SI to ES:DI. Both auto-increment or auto-decrement based on the Direction Flag.
- Segment Registers: CS, DS, SS, ES — CS (Code Segment) points to the current code — IP is an offset within CS. DS (Data Segment) is the default for most data access. SS (Stack Segment) is used with SP and BP. ES (Extra Segment) is used by string destination operations and as an auxiliary data segment. Each defines a 64 KB window into the 1 MB address space.
- The Flags Register (Status and Control) — The 16-bit Flags register contains 9 active flags. Six status flags are set by ALU operations: CF (Carry), ZF (Zero), SF (Sign), OF (Overflow), PF (Parity), AF (Auxiliary Carry). Three control flags are set by the programmer: DF (Direction for strings), IF (Interrupt enable), TF (Trap for single-step debugging).
- Register Encoding in Machine Code — Each register has a 3-bit code used in instruction encoding: AX=000, CX=001, DX=010, BX=011, SP=100, BP=101, SI=110, DI=111. The REG field and R/M field in the ModR/M byte use these codes. Understanding this helps when debugging raw machine code.
- IP — The Instruction Pointer — IP (Instruction Pointer) holds the offset of the next instruction to execute within the CS segment. You cannot read or write IP directly with MOV. It is modified indirectly by JMP, CALL, RET, INT, and IRET. The BIU uses CS:IP to fetch the next instruction bytes.
Code example
; Demonstrate all four register groups
.MODEL SMALL
.STACK 100h
.DATA
nums DW 10, 20, 30, 40, 50
count DW 5
sum DW 0
.CODE
MAIN PROC
MOV AX, @DATA ; setup segment register (DS)
MOV DS, AX
; General-purpose registers
XOR AX, AX ; AX = accumulator = 0
MOV CX, [count] ; CX = counter = 5
LEA BX, nums ; BX = base address of array
; Index register
XOR SI, SI ; SI = index = 0
; Loop using CX counter and SI index
add_loop:
ADD AX, [BX+SI] ; AX += nums[SI] (base+index)
ADD SI, 2 ; next word (2 bytes)
LOOP add_loop ; CX-- and loop if CX != 0
MOV [sum], AX ; store result to memory
; Pointer registers demonstrated
PUSH AX ; SP decremented, AX on stack
MOV BP, SP ; BP = stack frame pointer
MOV DX, [BP] ; DX = value at top of stack
POP AX ; restore AX, SP incremented
MOV AH, 4Ch
INT 21h
MAIN ENDP
END MAINLine-by-line walkthrough
- 1. MOV AX, @DATA / MOV DS, AX — loads the data segment address into DS via AX. This is a segment register setup using a general-purpose register as intermediary
- 2. XOR AX, AX — zeroes AX by XOR-ing it with itself. Faster and shorter than MOV AX, 0. AX will be our sum accumulator
- 3. MOV CX, [count] — loads 5 into CX from memory. CX will serve its role as the loop counter for the LOOP instruction
- 4. LEA BX, nums — loads the offset address of the nums array into BX. BX serves its role as the base register for array addressing
- 5. XOR SI, SI — zeroes SI. It will be our index offset into the array, incrementing by 2 each iteration (word-sized elements)
- 6. ADD AX, [BX+SI] — base+index addressing. The physical address is DS*16 + BX + SI. Adds the word at that address to AX
- 7. ADD SI, 2 — advances SI by 2 bytes (one word) to point to the next array element
- 8. LOOP add_loop — decrements CX, jumps back if CX != 0. After 5 iterations, AX = 10+20+30+40+50 = 150
- 9. PUSH AX / MOV BP, SP / MOV DX, [BP] / POP AX — demonstrates pointer registers: PUSH modifies SP, BP accesses the stack frame, POP restores SP
Spot the bug
MOV CX, 5
MOV SI, 0
MOV AX, 0
add_loop:
ADD AX, [SI] ; read array element
ADD SI, 2
LOOP add_loopNeed a hint?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- 8086 Register Organization (GeeksforGeeks)
- x86 Registers Explained (Wikibooks)
- 8086 Register Set and Functions (YouTube)
- emu8086 — Practice with Registers (Softonic)