The 8086 has 20 address lines, enough for 1 MB of memory, but its registers are only 16 bits. It builds each address from two 16-bit parts: a segment and an offset, written segment:offset. The CPU shifts the segment left by four bits (multiplies it by 16) and adds the offset. A segment therefore starts on a 16-byte boundary (a paragraph) and reaches 64 KB from there.
| segment:offset | segment × 16 + offset | physical |
|---|---|---|
| B800:0000 | B8000 + 0000 | B8000h |
| B000:8000 | B0000 + 8000 | B8000h |
| 0040:0017 | 00400 + 0017 | 00417h |
| FFFF:0010 | FFFF0 + 0010 | 00000h on the 8086 (wraps) |
Segments overlap, so one byte has many names: B800:0000 and B000:8000 are the same place. Compare addresses only after converting them to physical form.
The four segment registers
CS holds the code segment (instructions are fetched at CS:IP), SS the stack (SS:SP), DS the default data segment, and ES an extra one, the destination of string instructions. Each memory operand uses one of them by default, and a segment prefix can choose another.
How DOS sets them
A .COM program is loaded right after its 256-byte program segment prefix (PSP). CS, DS, ES and SS all hold the segment of the PSP, IP is 100h, and SP sits near the top of the 64 KB (FFFEh when that much memory is free), with a zero word there so that a plain RET reaches the INT 20h at offset 0 of the PSP. A .EXE has a header that gives the start CS:IP and SS:SP, relocated to where it was loaded; DS and ES point at the PSP, so the program loads DS itself, typically with MOV AX, @data / MOV DS, AX.
MOV BX, CSCALL HEX4MOV BX, DSCALL HEX4MOV BX, SSCALL HEX4HEX4 PROC NEAR ; print BX as four hex digits and a spaceMOV CX, 4NEXT1:PUSH CXMOV CL, 4ROL BX, CL ; bring the top nibble downPOP CXMOV AL, BLAND AL, 0FhADD AL, '0'CMP AL, '9'JBE SHOW1ADD AL, 7 ; 'A'..'F'SHOW1:MOV DL, ALMOV AH, 02hINT 21hLOOP NEXT1MOV DL, ' 'INT 21hRETHEX4 ENDP
Pitfalls
An offset wraps at 64 KB: incrementing DI past FFFFh goes back to 0 in the same segment, it does not move on to the next. To walk through more than 64 KB, advance the segment register instead. On the 8086, addresses above FFFFFh wrap to the start of memory.
Later CPUs
The 80286 has 24 address lines. In real mode FFFF:0010 to FFFF:FFFF then reach just above 1 MB (the high memory area), once the A20 line is enabled. In protected mode a segment register holds a selector, an index into a descriptor table, instead of a paragraph number.