CHAPTER 3. BOOT SECTOR PROGRAMMING (IN 16-BIT REAL
MODE) 24
but a day-to-day operating systems would never sit comfortably in such a tight box, so
it is important that we understand the solution, of segmentation, to this problem.
To get around this limitation, the CPU designers added a few more special registers,
cs, ds, ss, and es, called segment registers. We can imagine main memory as being
divided into segments that are indexed by the segment registers, such that, when we
specify a 16-bit address, the CPU automatically calculates the absolute address as the
appropriate segment’s start address offseted by our specified address [?]. By appropriate
segment, I mean that, unless explicitly told otherwise, the CPU will offset our address
from the segment register appropriate for the context of our instruction, for example:
the address used in the instruction mov ax, [0x45ef] would by default be offset from
the data segment, indexed by ds; similarly, the stack segment, ss, is used to modify the
actual location of the stack’s base pointer, bp.
The most confusing thing about segment addressing is that adjacent segments overlap
almost completely but for 16 bytes, so different segment and offset combinations can
actually point to the same physical address; but enough of the talk: we won’t truly
grasp this concept until we’ve seen some examples.
To calculate the absolute address the CPU multiplies the value in the segment register
by 16 and then adds your offset address; and because we are working with hexadecimal,
when we multiple a number by 16, we simply shift it a digit to the left (e.g. 0x42 * 16
= 0x420). So if we set ds to 0x4d and then issue the statement mov ax, [0x20], the
value stored in ax will actually be loaded from address 0x4d0 (16 * 0x4d + 0x20).
Figure 3.7 shows how we can set ds to achieve a similar correction of label addressing
as when we used the [org 0x7c00] directive in Section XXX. Because we do not use the
org directive, the assmebler does not offset our labels to the correct memory locations
when the code is loaded by BIOS to the address 0x7c00, so the first attempt to print an
’X’ will fail. However, if we set the data segment register to 0x7c0, the CPU will do this
offset for us (i.e. 0x7c0 * 16 + the secret), and so the second attempt will correctly
print the ’X’. In the third and fourth attempts we do the same, and get the same results,
but instead explicitly state to the CPU which segment register to use when computing
the physical address, using instead the general purpose segment register es.
Note that limitations of the CPU’s circuitry (at least in 16-bit real mode) reveal
themselves here, when seemingly correct instructions like mov ds, 0x1234 are not actu-
ally possibly: just because we can store a literal address directly into a general purpose
register (e.g. mov ax, 0x1234 or mov cx, 0xdf), it doesn’t mean we can do the same
with every type of register, such as segment registers; and so, as in Figure 3.7, we must
take an additional step to transfer the value via a general purpose register.
So, segment-based addressing allows us to reach further into memory, up to a little
over 1 MB (0xffff * 16 + 0xffff). Later, we will see how more memory can be accessed,
when we switch to 32-bit protected mode, but for now it suffices for us to understand
16-bit real mode segment-based addressing.
3.6.2 How Disk Drives Work
Mechanically, hard disk drives contain one or more stacked platters that spin under a
read/write head, much like an old record player, only potentially, to increase capacity,
with several records stacked one above the other, where a head moves in and out to get
coverage of the whole of a particular spinning platter’s surface; and since a particular
platter may be readible and writable on both of its surfaces, one read/write head may