-
Notifications
You must be signed in to change notification settings - Fork 0
real mode
The 16-bit world every x86 starts in — segments, offsets, and a 1 MiB ceiling that MyOS-Simple lives within until it switches to protected mode.
Every x86 CPU, from the original 8086 to the latest 64-bit chip, powers up in
real mode. It is a compatibility museum baked into silicon: a 16-bit
execution environment that behaves almost exactly like a 1978 Intel 8086. When
the BIOS jumps to our boot sector at 0x7C00, this is the
environment our code inherits.
Real mode is defined by a handful of properties:
-
16-bit registers. The general-purpose registers
AX,BX,CX,DX, the index registers, and the stack pointer are all 16 bits wide. The largest value a single register holds is0xFFFF(65535). - Segment:offset addressing. Memory is reached through a pair of 16-bit values, not a single flat pointer (see below).
- ~1 MiB of addressable memory. The addressing formula tops out just past one megabyte.
- No memory protection. Any code can read or write any address. There are no privilege rings, no page faults, no isolation. A stray pointer corrupts whatever it hits.
-
BIOS services are available. Because the IVT
is live, software interrupts like
int 0x10andint 0x13work. These vanish the instant we enter protected mode.
This is why all of MyOS-Simple's boot-time hardware work — clearing the screen, loading the kernel from disk — happens in real mode, before the protected-mode switch.
The defining feature of real mode is how it forms a physical address. The 8086 had 16-bit registers but a 20-bit address bus — it could point at 64 KiB but wire up to 1 MiB. Intel bridged the gap with segmentation: every memory access combines two 16-bit values.
physical address = (segment << 4) + offset
= segment * 16 + offset
The segment register supplies a base, shifted left by 4 bits (multiplied by 16),
and the offset is added on top. So the logical address 0x07C0:0x0000 and the
logical address 0x0000:0x7C00 both resolve to the same physical byte:
0x07C0 * 16 + 0x0000 = 0x7C00 + 0 = 0x7C00
0x0000 * 16 + 0x7C00 = 0 + 0x7C00 = 0x7C00
This is exactly why MyOS-Simple's bootloader zeroes its segment registers up front:
; Set up segments
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00helloworld-os-c/boot.asm:21-26
With DS = ES = SS = 0, every offset is its own physical address — mov bx, 0x1000 simply means physical 0x1000, with no segment arithmetic to track in
your head. The [ORG 0x7C00] directive at the top of each boot file tells the
assembler to compute label addresses as if the code lives at offset 0x7C00,
which (with a zero DS) matches the physical address the BIOS loaded us to.
💡 Tidbit: Because
segment * 16 + offsetcan be written many ways, the same physical byte has up to 4096 differentsegment:offsetspellings. Two addresses are aliases if they reduce to the same physical value. Real-mode programmers learned to normalise their pointers to avoid surprises.
The largest address real mode can name comes from maxing out both halves of the formula:
0xFFFF * 16 + 0xFFFF = 0xFFFF0 + 0xFFFF = 0x10FFEF (~1 MiB + ~64 KiB)
On the original 8086, the address bus was only 20 bits wide, so anything past
0xFFFFF (1 MiB) silently wrapped around to the bottom of memory. The
practical ceiling was therefore ~1 MiB.
💡 Tidbit: Real mode's 20-bit reach is a direct consequence of the
segment * 16 + offsetformula: shifting a 16-bit segment left by 4 and adding a 16-bit offset produces at most a 21-bit sum, but the original bus truncated it to 20 bits — capping classic real mode at ~1 MiB.
⚠️ Caveat: Later CPUs (80286+) had wider address buses, so the wrap no longer happened automatically. The small region just above 1 MiB (0x100000–0x10FFEF) became reachable from real mode and is called the High Memory Area (HMA) — but only if the A20 line (address line 20) is enabled. MyOS-Simple does not need the HMA: it loads everything into low memory and switches to protected mode before it would matter.
Here is what low memory looks like while MyOS-Simple's boot sector is running. The full table lives in the memory map reference; the highlights:
| Physical address | Contents |
|---|---|
0x00000 |
Interrupt Vector Table (IVT) + BIOS data area |
0x00500 |
First freely usable low memory |
0x01000 |
Where the kernel is loaded (stage 2+) |
0x07C00 |
The boot sector (512 bytes, ends in 0xAA55) |
0x07E00 |
Free memory above the boot sector |
0xB8000 |
VGA text-mode framebuffer |
The IVT at 0x00000 is what makes BIOS services work: each
int N instruction looks up entry N in that table and calls the firmware
routine it points to.
MyOS-Simple sets SP = 0x7C00 (boot.asm:26). The x86 stack grows downward —
each push decrements SP before writing. Starting the stack at 0x7C00 and
the code also at 0x7C00 is deliberate: the stack expands into the free memory
below the boot sector (0x7BFF, 0x7BFE, …), so it can never collide with
the 512 bytes of code sitting at 0x7C00 and above.
⚠️ Caveat: Real mode gives you no stack-overflow protection. If the stack grows far enough down it will eventually run into the IVT/BIOS data at0x00000and corrupt it silently. Boot code keeps its stack usage tiny for exactly this reason.
Real mode is friendly for booting — the BIOS does the hardware legwork for you — but it is a poor home for a real kernel:
- Only ~1 MiB of memory is reachable.
- 16-bit registers make 32-bit arithmetic clumsy.
- There is no memory protection, so a bug anywhere can corrupt anything.
- Segment juggling makes large, flat data structures painful.
So from stage 2 onward, the bootloader
installs a Global Descriptor Table, flips
CR0.PE, and far-jumps into protected mode: flat 32-bit
addressing, 4 GiB of reach, and privilege levels. The
boot process page walks that transition step by step.
Real mode never fully disappears, though — it is still the only environment in which the convenient BIOS services exist, which is why single-sector stage 1 stays in real mode for its entire life.
- The boot process — the full power-on-to-C walkthrough
- BIOS services — the interrupts real mode makes available
- The boot sector — the 512-byte program real mode first runs
- Protected mode — the 32-bit mode MyOS switches to
- The Global Descriptor Table — what enables the switch
- Memory map — every fixed address in low memory
- Glossary — segment, offset, A20, IVT, and more
- Home
Stages
- 1 · Assembly boot
- 2 · C protected mode
- 3 · Interactive shell
- 4 · Clock / processes / calc
- 5 · Stabilized release
Concepts — boot
Concepts — protected mode
Concepts — hardware
Concepts — OS services
Reference
- Memory map
- I/O ports
- GDT descriptor format
- Scancode tables
- Command reference
- Toolchain & build
- Glossary
Guides