When Intel was building the 8086, there was a valid case for having more than 64KB in a machine, but there was no way it'd ever use a 32-bit address space. Back then, even a megabyte was a whole lot of memory. (Remember the infamous quote "640K ought to be enough for anybody"? It's essentially a mistranslation of the fact that back then, 1MB was freaking huge.) The word "gigabyte" wouldn't be in common use for another 15-20 years, and it wouldn't be referring to RAM for like another 5-10 years after that.
So instead of implementing an address space so huge that it'd "never" be fully utilized, what they did was implement 20-bit addresses. They still used 16-bit words for addresses, because after all, this is a 16-bit processor. The upper word was the "segment" and the lower word was the "offset". The two parts overlapped considerably, though -- a "segment" is a 64KB chunk of memory that starts at (segment) * 16
, and the "offset" can point anywhere within that chunk. In order to calculate the actual address, you multiply the segment part of the address by 16 (or shift it left by 4 bits...same thing), and then add the offset. When you're done, you have a 20-bit address.
19 4 0
+--+--+--+--+
| segment |
+--+--+--+--+--+
| offset |
+--+--+--+--+
For example, if the segment were 0x8000, and the offset were 0x0100, the actual address comes out to ((0x8000 << 4) + 0x0100)
== 0x80100
.
8 0 0 0
0 1 0 0
---------------
8 0 1 0 0
The math is rarely that neat, though -- 0x80100
can be represented by literally thousands of different segment:offset combinations (4096, if my math is right).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…