Skip to content

boot sector

Mohiuddin Khan Inamdar edited this page Jun 21, 2026 · 3 revisions

← Home

The Boot Sector

Exactly 512 bytes, loaded to 0x7C00, ending in the magic word 0xAA55 — the smallest unit of code the firmware will ever run for you.

The boot sector is the first sector of a bootable disk: precisely 512 bytes, the last two of which must be the signature 0xAA55. It is the entry point for everything MyOS-Simple does — in stage 1 it is the entire operating system, and in stage 2+ it is a small bootloader that pulls in a larger kernel. This page explains the strict contract a boot sector must satisfy and the assembly idioms MyOS-Simple uses to meet it.

The contract

For the BIOS to load and run a sector, three conditions must hold:

  1. It is exactly 512 bytes. No more, no less. The BIOS reads exactly one sector (512 bytes on standard media) to 0x7C00.
  2. Its last two bytes are the boot signature 0x55 then 0xAA — read as a little-endian 16-bit word, 0xAA55.
  3. It is valid 16-bit code that begins executing at offset 0, because the BIOS far-jumps to the start of what it loaded, in real mode.

If any of these fail, the BIOS treats the device as non-bootable and moves to the next one in its boot order. The boot process page covers how the firmware finds and loads this sector; here we focus on building one correctly.

The 0xAA55 signature

The signature is the firmware's single, blunt test for "is this disk bootable?". It is stored in the last two bytes of the sector:

byte 510: 0x55
byte 511: 0xAA

Because x86 is little-endian, those two bytes read back as the 16-bit value 0xAA55 (low byte first). This is a constant source of confusion — the value people quote (0xAA55) has its bytes in the opposite order from how they sit on disk (55 AA).

💡 Tidbit: The 0xAA55 signature is the firmware's only sanity check that a disk is bootable. There is no checksum, no header, no filesystem inspection — just two magic bytes. Write 0xAA55 at offset 510 of any 512-byte sector and the BIOS will happily jump into it, valid code or not.

⚠️ Caveat: Get the byte order wrong — emitting 0x55AA instead of 0xAA55 — and the disk is silently treated as non-bootable. The NASM idiom dw 0xAA55 is correct because dw writes a 16-bit word in little-endian order, producing 55 AA on disk, which the firmware reads back as 0xAA55.

Building a 512-byte sector with NASM

MyOS-Simple's boot files share the same three structural directives. Here is the end of the monochrome stage 1 build:

[BITS 16]
[ORG 0x7C00]

start:
    ; ... code ...

times 510 - ($ - $$) db 0
dw 0xAA55

helloworld-os-asm/main.asm:10-11, 68-69

Three things are doing the heavy lifting:

[BITS 16] — assemble 16-bit code

The boot sector runs in real mode, so the assembler must emit 16-bit instruction encodings. [BITS 16] tells NASM exactly that. (The bootloader later switches to [BITS 32] after the protected-mode jump — see helloworld-os-c/boot.asm:47.)

[ORG 0x7C00] — origin at the load address

[ORG 0x7C00] tells NASM to compute every label's address as if the code were loaded at 0x7C00. This matters because the boot sector refers to its own data by absolute address — for example mov si, msg needs msg to resolve to the right physical location. Since the BIOS loads us to 0x7C00 and the bootloader zeroes its segments (DS = 0), the assembler's offsets line up exactly with the physical addresses at runtime. (Why 0x7C00? The history is on the boot process page.)

The padding idiom

times 510 - ($ - $$) db 0
dw 0xAA55

This is the canonical way to pad a boot sector to exactly 512 bytes with the signature on the end. Reading it piece by piece:

  • $ is the current address (the byte right after the code).
  • $$ is the address of the start of the section.
  • $ - $$ is therefore the number of bytes of code and data written so far.
  • 510 - ($ - $$) is how many bytes are left before offset 510.
  • times N db 0 emits N zero bytes — filling the gap so the code occupies exactly bytes 0–509.
  • dw 0xAA55 writes the 2-byte signature into bytes 510–511.

Total: exactly 512 bytes, signature at the end. Both stage 1 builds and the stage 2+ bootloader use this identical pattern (helloworld-os-asm/main_color.asm:192-193, helloworld-os-c/boot.asm:140-141).

💡 Tidbit: If your code plus data exceeds 510 bytes, 510 - ($ - $$) becomes negative and NASM throws a times value is negative error at assembly time. That error message is really telling you "your boot sector doesn't fit in 512 bytes" — a hard size ceiling that is the whole reason stage 2+ splits into a bootloader plus a separately loaded kernel.

One sector is rarely enough

512 bytes is tiny. After the signature you have only 510 bytes for code and data. Stage 1 squeezes a whole interactive program into that budget by writing hand-tuned assembly and leaning on BIOS services for all the hard work. But a C kernel does not fit, so the boot sector evolves into a two-part design:

Stage Boot sector role What it does
Stage 1 Is the OS Prints, reads a key, halts — all in 512 B
Stage 2+ Bootloader Loads a multi-sector kernel, enters protected mode, calls C

In the bootloader case, the boot sector lives at disk sector 1, and the kernel occupies the contiguous sectors starting at sector 2. The boot sector reads those extra sectors into memory with INT 13h disk loading before handing off. The number of sectors read grows with the kernel across the stages (16 / 15 / 39 / 39).

Where the boot sector sits in memory

Physical address Contents
0x07C00 The boot sector — 512 bytes, ending in 0xAA55
0x07DFE byte 510 = 0x55
0x07DFF byte 511 = 0xAA
0x07E00 First free byte above the boot sector

The boot sector also sets its stack just below itself (SP = 0x7C00, helloworld-os-c/boot.asm:26), so the downward-growing stack uses the free memory below 0x7C00 without ever touching the loaded code. The complete layout is in the memory map.

See also

Clone this wiki locally