Lesson 12 of 48 intermediate

Segmented Memory and Physical Address

How the 8086 combines segment and offset to address 1 MB of memory with 16-bit registers

Open interactive version (quiz + challenge)

Real-world analogy

Imagine a huge library with 16 floors, each having 65,536 book slots. Your library card (a 16-bit register) can only hold a slot number — not enough to describe every book in the whole building. So the library uses a two-part system: a floor number (segment) and a slot on that floor (offset). The librarian multiplies the floor number by 16, then adds the slot number to get the absolute position. That is exactly how the 8086 forms a 20-bit physical address from two 16-bit values: Segment x 16 + Offset.

What is it?

The 8086 uses segmented memory to bridge the gap between its 16-bit registers and its 20-bit address bus. Every memory access combines a 16-bit segment register value and a 16-bit offset using the formula: Physical Address = Segment x 16 + Offset. Four segment registers (CS, DS, SS, ES) define 64 KB windows into the 1 MB address space. The CPU automatically selects the appropriate segment register based on the type of access (code, data, stack, or string). Multiple segment:offset pairs can map to the same physical address, and segments overlap on 16-byte (paragraph) boundaries.

Real-world relevance

Segmented memory defined the IBM PC era. Every DOS program lived within the 640 KB conventional memory limit imposed by the IBM PC's memory map. DOS memory managers (HIMEM.SYS, EMM386) existed specifically to work around segmentation limits. The A20 gate — a hardware hack tied to the address wraparound — persisted in PCs until UEFI replaced BIOS. Even modern x86 CPUs boot in Real Mode with segmentation active before switching to Protected Mode with paging.

Key points

Code example

; Demonstrate segmented memory addressing
.MODEL SMALL
.STACK 100h
.DATA
  msg1 DB 'Segment Demo', 0Dh, 0Ah, '$'
  val1 DW 0ABCDh
  val2 DW 0

.CODE
MAIN PROC
  ; Setup data segment
  MOV AX, @DATA
  MOV DS, AX          ; DS points to data segment
  MOV ES, AX          ; ES also points to data segment

  ; --- Default segment usage ---
  MOV BX, OFFSET val1 ; BX = offset of val1
  MOV AX, [BX]        ; DS:BX -> reads val1 = ABCDh

  ; --- Segment override ---
  MOV AX, ES:[BX]     ; ES:BX -> same physical address here
                        ; because DS = ES in this example

  ; --- Stack segment (SS) usage ---
  PUSH 1234h           ; SS:SP -> push to stack
  MOV BP, SP
  MOV CX, [BP]         ; SS:BP -> reads 1234h from stack
  POP AX               ; restore stack

  ; --- Demonstrate address calculation ---
  ; If DS = 1234h and BX = 0010h
  ; Physical = 1234h x 10h + 0010h
  ;          = 12340h + 0010h = 12350h

  ; --- Different segment:offset, same address ---
  ; DS=1235h, BX=0000h -> 12350h + 0000h = 12350h
  ; DS=1200h, BX=0350h -> 12000h + 0350h = 12350h
  ; All point to physical address 12350h

  ; Store result to verify
  MOV [val2], AX

  MOV AH, 4Ch
  INT 21h
MAIN ENDP
END MAIN

Line-by-line walkthrough

  1. 1. MOV AX, @DATA / MOV DS, AX — loads the data segment address into DS. The assembler replaces @DATA with the paragraph address where .DATA begins
  2. 2. MOV ES, AX — sets ES equal to DS. Now both segment registers point to the same segment. In real programs, ES often points to a different segment (e.g., video memory at B800h)
  3. 3. MOV BX, OFFSET val1 — loads the offset (not the value) of val1 into BX. OFFSET is an assembler operator that returns the address within the data segment
  4. 4. MOV AX, [BX] — reads a word from DS:BX. The BIU computes physical address = DS x 16 + BX, fetches the 16-bit value (ABCDh), and loads it into AX
  5. 5. MOV AX, ES:[BX] — same offset BX, but now using ES instead of DS. The ES: prefix overrides the default segment. Since DS = ES, this reads the same physical address
  6. 6. PUSH 1234h — pushes 1234h onto the stack. The BIU writes to SS:SP (stack segment). SP is decremented by 2 first, then the value is stored
  7. 7. MOV BP, SP / MOV CX, [BP] — BP accesses the stack. [BP] defaults to SS segment (unlike [BX] which defaults to DS). Reads back 1234h from the top of the stack
  8. 8. POP AX — pops the value from SS:SP into AX and increments SP by 2, restoring the stack to its previous state

Spot the bug

MOV AX, 1234h
MOV DS, AX         ; DS = 1234h
MOV SI, 5678h      ; offset = 5678h
; Programmer expects physical address: 12345678h
MOV AL, [SI]       ; read from 'address 12345678h'
Need a hint?
How does the 8086 actually compute the physical address? Is 12345678h even possible with a 20-bit address bus?
Show answer
The programmer mistakenly concatenated the segment and offset (1234h + 5678h = 12345678h). The 8086 does NOT concatenate — it computes Segment x 16 + Offset = 1234h x 10h + 5678h = 12340h + 5678h = 179B8h. Also, the 8086 only has 20 address bits, so the maximum address is FFFFFh (about 1 MB). The value 12345678h would be over 300 MB — far beyond what the 8086 can address.

Explain like I'm 5

Imagine you live in a city with streets that are really long — 65,536 houses on each street. But the city has 65,536 streets, and streets overlap a lot (each new street starts only 16 houses apart from the last one). To find a house, you say 'Street 2000, House 1234.' The postal service multiplies the street number by 16 and adds the house number to get the actual address. That is how the 8086 finds memory: it takes a street number (segment) and a house number (offset) and combines them into one big address.

Fun fact

The A20 gate is one of the strangest hardware hacks in computing history. IBM needed to maintain address wraparound compatibility with the 8086, but the 80286 had 24 address lines. Their solution? Route address line A20 through the keyboard controller, which could enable or disable it. So for years, enabling memory above 1 MB required talking to the keyboard controller — a hack that persisted for over 30 years in PC design.

Hands-on challenge

Calculate the physical addresses for the following segment:offset pairs and determine which ones point to the same physical location: (a) 1000h:0100h, (b) 1010h:0000h, (c) 0FFFh:0110h, (d) 0000h:10100h (invalid — explain why), (e) 1008h:0080h. Then write an 8086 program that uses segment overrides (ES: prefix) to copy a word from one segment to another.

More resources

Open interactive version (quiz + challenge) ← Back to course: Microprocessor A–Z