Appearance
Operating System from Scratch
| Difficulty | Advanced |
| Team Size | 2-3 people |
| Time | ~50-60 hours |
| Demo-ready by | Step 5 |
| Prerequisites | C, x86 assembly, computer architecture fundamentals |
| Built by | Linux, FreeBSD, MINIX, SerenityOS |
Skills you'll earn: Bootloader development, kernel mode programming, memory management, interrupt handling, filesystem implementation
Start with a bootloader. End with a minimal OS that runs programs.
Step 1: Boot and print to screen (~3-4 hours)
The CPU starts in real mode. You need to get a message on screen with nothing but raw assembly.
- Write a bootloader in NASM assembly (x86)
- Fit it in 512 bytes (the boot sector)
- Use BIOS interrupt
int 0x10to print "Hello, OS" to the screen - End with the boot signature
0x55AAat byte 510 - Test with QEMU:
qemu-system-x86_64 -fda boot.bin
You now have: A bootable program.
Step 2: Enter protected mode (~4-5 hours)
Real mode is 16-bit with 1MB of addressable memory. You need 32-bit protected mode.
- Set up a Global Descriptor Table (GDT) with code and data segments
- Disable interrupts, load the GDT, set the PE bit in CR0
- Far jump to a 32-bit code segment
- Print to the screen by writing to VGA text buffer at
0xB8000(no more BIOS interrupts)
You now have: 32-bit protected mode with direct hardware access.
Step 3: Load a kernel (~5-6 hours)
Your bootloader is 512 bytes. That's not enough for an OS. Load more code from disk.
- Use BIOS disk services (
int 0x13) to read sectors from disk into memory - Write your kernel in C, compile it as a flat binary
- The bootloader loads the kernel into a known memory address and jumps to it
- Your C
kernel_main()function now runs
You now have: A kernel loaded from disk.
Step 4: Interrupts and keyboard input (~5-6 hours)
The OS can't respond to hardware events. You need interrupts.
- Set up an Interrupt Descriptor Table (IDT)
- Program the PIC (Programmable Interrupt Controller) to map hardware interrupts
- Write an interrupt handler for the keyboard (IRQ 1)
- Read scancodes from port
0x60, translate to characters, print to screen
You now have: Keyboard input.
Step 5: Memory management (~5-6 hours)
You're writing to hardcoded addresses. You need to track what memory is free.
- Parse the memory map (from BIOS
int 0x15, eax=0xE820or GRUB multiboot info) - Implement a physical page allocator (bitmap or free list, 4KB pages)
- Set up paging: map virtual addresses to physical addresses
- Identity-map the kernel, map user space separately
You now have: Virtual memory.
Step 6: A simple shell (~5-6 hours)
You can type characters but there's no command processor.
- Implement a text buffer that accumulates keystrokes
- On Enter, parse the line as a command
- Start with built-in commands:
help,clear,echo - Add a prompt string
You now have: A command-line shell.
Step 7: Processes (~5-6 hours)
- Implement a process structure: register state, stack, page directory
- Create a simple round-robin scheduler with timer interrupts
- Context switch between two processes
- Each process runs in its own address space
Step 8: File system (~5-6 hours)
- Implement a simple file system (FAT12 or a custom one) on a RAM disk
- Support
read,write,create, andlistoperations - Load the file system image as part of the boot disk
Step 9: System calls (~4-5 hours)
- Implement a syscall interface (interrupt
0x80orsyscallinstruction) - Add syscalls:
write,read,exit,fork - User programs call these to interact with the kernel
Useful Resources
- OSDev Wiki — the definitive resource
- Writing a Simple Operating System — from Scratch (Nick Blundell, PDF)
- NASM documentation
- QEMU documentation
- xv6 — MIT teaching OS — reference implementation
Where to go from here
- Network stack (Ethernet driver, TCP/IP)
- Graphics mode (VESA framebuffer, basic GUI)
- ELF binary loader (run compiled programs)
- Multi-core support (SMP)
- Port to RISC-V or ARM