I am reading about memory addressing. I read about segment offset and then about descriptor offset. I know how to calculate the exact addresses in real mode. All this is OK,
A 16-bit register can only address up to 0xFFFF (65,536 bytes, 64KB). When that wasn't enough, Intel added segment registers.
Any logical design would have simply combined two 16-bit registers to make a 32-bit address space, (e.g. 0xFFFF : 0xFFFF = 0xFFFFFFFF
), but nooooo... Intel had to get all weird on us.
Historically, the frontside bus (FSB) only had 20 address lines, and thus could only transmit 20-bit addresses. To "rectify" this, Intel devised a scheme in which segment registers only extend your address by 4-bits (16bits + 4 = 20, in theory).
To achieve this, the segment register is left-shifted from its original value by 4-bits, then added to the address in your general register (e.g. [es:ax] = ( es << 4 ) + ax
). Note: Left shifting 4 bits is equivalent to multiplying by 16.
That's it. Here's some illustrative examples:
;; everything's hexadecimal
[ 0:1 ] = 1
[ F:1 ] = F1
[ F:0 ] = F0
[ F:FF] = 1EF ; [F becomes F0, + FF = 1EF]
[ F000 : FFFF ] = FFFFF (max 20-bit number)
[ FFFF : FFFF ] = 10FFEF (oh shit, 21-bit number!)
So, you can still address more than 20-bits. What happens? The address "wraps around", like modulus arithmetic (as a natural consequence of the hardware). So, 0x10FFEF
becomes 0xFFEF
.
And there you have it! Intel hired some dumb engineers, and we have to live with it.