Skip to content

The A20 Line

The A20 line is the 21st physical address line (bit 20, counting from zero) on the CPU’s address bus. On real x86 hardware it starts out masked to zero, regardless of what the corresponding bit in a computed address actually holds: a default kept purely for backward compatibility, not any electrical necessity. Protected Mode and Emulating & Debugging both mention it in passing, the former as the reason memory above 1 MB stays unreliable after entering protected mode, without either explaining the mechanism itself.

The original 8086 has only twenty address lines, giving it a 1 MB addressable range, but its segmented addressing scheme (segment × 16 + offset) can compute a 21-bit result: an address like 0xFFFF:0xFFFF sums to 0x10FFEF, one megabyte and roughly 64 KB past the top of what the chip’s twenty pins can actually carry. On the 8086 itself this simply wrapped silently back around to the bottom of the address space, since the 21st bit had nowhere to go; when the 80286 shipped with a genuine 21st address line, software already written to depend on that wraparound (deliberately or not) would have started addressing real, different memory near the 1 MB mark instead of wrapping, breaking in a way IBM’s PC/AT design specifically chose to prevent by gating the 21st line through an extra piece of logic that, by default, keeps it forced low.

Enabling the A20 line means telling that gating logic to stop masking bit 20, and several largely redundant mechanisms exist to do so, a consequence of the gate historically living behind the same keyboard controller used for an entirely unrelated peripheral. The original IBM PC/AT method repurposes two spare output pins on the 8042 keyboard controller itself, sending a command byte that toggles the gate as a side effect of a chip whose actual job is scanning a keyboard matrix:

; classic 8042 keyboard-controller method
wait_8042:
in al, 0x64
test al, 2
jnz wait_8042
mov al, 0xD1
out 0x64, al
wait_8042_2:
in al, 0x64
test al, 2
jnz wait_8042_2
mov al, 0xDF ; enable A20
out 0x60, al

Fast A20, a later and considerably simpler alternative, exposes the same gate through a single dedicated I/O port, 0x92, where setting bit 1 enables the line directly without any command-byte handshake:

in al, 0x92
or al, 2
out 0x92, al

A third option, available only while still running under BIOS real-mode services (and therefore unusable once the kernel has already transitioned away from them), calls INT 0x15 with AX = 0x2401, delegating the actual enabling mechanism to whatever the firmware itself implements. None of the three is universally guaranteed present on every chipset, which is why real-world bootloaders commonly attempt more than one method in sequence rather than relying on a single one unconditionally succeeding.

Because some emulators and a subset of real hardware enable A20 by default, or because a bootloader stage running before the kernel’s own code may have already enabled it, testing the current state before attempting to change it avoids redundant or conflicting work. The standard test compares two physical addresses exactly 1 MB apart, one of which only wraps around to alias the other if A20 is still disabled:

; compare 0000:0500 and FFFF:0510 (1 MB apart, aliasing iff A20 is off)
mov ax, 0xFFFF
mov es, ax
mov word [0x0500], 0x1234
mov word [es:0x0510], 0x5678
cmp word [0x0500], 0x1234 ; if this now reads 0x5678, A20 is disabled

If A20 is disabled, the write through the es:0x0510 alias lands on the exact same physical byte as 0x0500: the two addresses, 0x100500 and 0x000500, differ by exactly 2^20 (one megabyte), so masking off the 21st bit leaves them indistinguishable, and the first value is silently overwritten; if A20 is enabled, the two addresses are genuinely distinct and the original value survives untouched. Writing to two different, unpredictable offsets rather than a single fixed pair, and restoring whatever was originally there afterward, avoids the test itself corrupting memory a bootloader might still need.

Forgetting to enable A20 at all produces one of the more disorienting classes of early boot bug precisely because it doesn’t fault: a page table, GDT, or piece of kernel code placed just past the 1 MB mark appears to load and execute normally in some code paths while silently reading or writing the wrong physical memory, one megabyte lower than intended, in others, depending on which specific addresses happen to be touched and whether they alias something else already in use. This is also why the boot process generally treats A20 as something to settle once, early, and definitively, before any code that assumes memory above 1 MB behaves normally runs: leaving it in an unknown state and hoping it happens to already be enabled is a common source of intermittent, hardware-dependent failures that don’t reproduce identically across every machine or emulator a kernel is tested on.

  1. ^ IBM, Personal Computer AT Technical Reference: the original PC/AT documentation describing the keyboard-controller A20 gate and the compatibility rationale behind it.
  • Protected Mode: why A20 matters at all, since protected mode exposes memory above 1 MB that stays unreliable without it enabled.
  • The Boot Process: the point in boot where A20 needs to be settled before later stages can trust memory above 1 MB.
  • Emulating & Debugging: Bochs’ unusually faithful emulation of the A20 gate and real-mode segmentation.
  • Resetting and Shutting Down: the other, unrelated purpose the 8042 keyboard controller pulsed here also serves.