Building NexisK: A Technical Deep Dive into My i386 Kernel
NexisK is an experimental 32‑bit x86 kernel written in C and NASM that demonstrates how to take control of a PC from firmware, enter protected mode, discover memory, and set up basic interrupt and process infrastructure. The article walks through the bootloader design, CPU initialization, memory ma…
NexisK is an experimental 32‑bit x86 kernel that the author has built from the ground up. Rather than rely on an existing operating‑system framework, the project focuses on the low‑level mechanisms that allow a computer to run code that can manage memory, handle interrupts, and launch user processes. The current release, v0.8.9, runs on an i386 CPU in protected mode and includes a custom two‑stage BIOS bootloader, GDT and IDT initialization, BIOS E820 memory discovery, a basic physical memory manager, and early process support.
Why Build a Kernel From Scratch?
Most application developers start with an operating system that already provides memory management, device drivers, and a file system. Kernel developers, however, must create those services themselves. By writing the bootloader, setting up protected mode, and discovering usable RAM, the author gains a direct view of the boundary between software and hardware. Each layer depends on the previous one: a faulty GDT stops protected mode, an incorrect IDT can crash the system, and a bad memory manager can overwrite kernel data. This bottom‑up approach forces a clear understanding of how the CPU transitions from firmware to kernel code.
Bootloader Architecture
NexisK’s bootloader is split into two stages. Stage 1 is the 512‑byte boot sector that loads Stage 2 into memory. Stage 2 performs the heavy lifting: it builds a boot menu, queries the BIOS for the E820 memory map, selects the kernel image, loads it, and then hands control to the kernel’s entry point, kmain. Keeping the bootloader separate from the kernel clarifies responsibilities and makes the loader reusable for future kernels.
The boot image is packaged as a 1.44 MB floppy‑style disk image, then turned into an El Torito bootable ISO. The repository contains the source for both stages and a small build script that produces the final ISO.
Entering Protected Mode
Protected mode changes the CPU’s segmentation, privilege levels, and interrupt handling. NexisK initializes a Global Descriptor Table (GDT) with code, data, and stack segments, then enables protected mode by setting the PE bit in the CR0 register and performing a far jump to the kernel’s 32‑bit code segment. Without a valid GDT the kernel would run in real mode and could not use the C compiler’s generated code.
Debugging this transition is critical: verify that the bootloader runs, Stage 2 loads, the kernel is at the expected address, protected mode is active, and kmain is reached. Once these steps are confirmed, higher‑level initialization can be tackled.
Interrupt and Exception Handling
Interrupts are the CPU’s way of reacting to events outside the normal instruction stream. NexisK sets up an Interrupt Descriptor Table (IDT) that maps exception vectors (0–31) and hardware IRQs (32–47) to handler routines. The Programmable Interrupt Controller (PIC) is remapped so that IRQs start at 0x20, keeping them separate from processor exceptions.
Hardware interrupts include the Programmable Interval Timer (PIT) for periodic ticks, keyboard and PS/2 mouse interrupts, and a system‑call interrupt vector (int 0x80). The kernel’s exception handlers log errors to the serial port, while the PIT handler can be used for a simple scheduler.
Memory Discovery and Physical Memory Manager
Before allocating memory, the kernel must know which physical addresses are usable. NexisK queries the BIOS using INT 15h, E820h, and passes the resulting memory map to the kernel. The map lists regions with base addresses, sizes, and types (usable, reserved, ACPI, etc.).
The Physical Memory Manager (PMM) parses the E820 map, filters usable regions, and builds a bitmap that tracks 4 KiB pages. Each bitmap entry is a byte: 0 for free, 1 for reserved or allocated. Although a bit‑per‑page bitmap would be more space‑efficient, the byte‑per‑page representation simplifies debugging and inspection.
Current PMM functionality includes:
- Parsing usable regions from the E820 map
- Calculating the number of 4 KiB pages
- Marking pages as free or reserved
Future work involves initializing the bitmap, reserving kernel and bootloader memory, handling page allocation and freeing, and integrating with a virtual memory manager.
Process and System‑Call Foundations
Although virtual memory and a full process model are still under development, NexisK already contains a minimal process context structure and a basic system‑call interface via int 0x80. This allows user programs to request services such as printing to the VGA text buffer or writing to the serial port.
The kernel’s early userspace directory structure is a placeholder for future file‑system support. The goal is to demonstrate how a process can be created, switched, and terminated before a full virtual‑memory subsystem is available.
What’s Next?
Key unfinished areas include a complete virtual memory manager, a robust scheduler, and a production‑ready userspace environment. The author plans to refine the PMM, add page‑fault handling, and eventually support multi‑core scheduling.
For developers interested in low‑level OS design, NexisK offers a practical example of how to bootstrap a kernel, manage hardware, and lay the groundwork for more advanced features.
Why it matters
Understanding the fundamentals of kernel development—bootloading, protected‑mode entry, memory discovery, and interrupt handling—provides insight into how modern operating systems manage hardware and resources. NexisK serves as a hands‑on learning platform for aspiring OS engineers.
Key points
- Custom two‑stage BIOS bootloader that loads the kernel
- GDT and IDT setup for protected‑mode operation
- BIOS E820 memory discovery and a bitmap‑based physical memory manager
- Early process and system‑call infrastructure via int 0x80
- Separate bootloader and kernel for clear responsibility and reusability
Frequently asked questions
What is the purpose of the two‑stage bootloader?
Stage 1 loads Stage 2, which performs the heavy boot tasks such as memory map discovery, kernel selection, and transferring control to the kernel. Separating them keeps responsibilities clear and makes the loader reusable.
Why does NexisK use a byte‑per‑page bitmap in the PMM?
A byte‑per‑page bitmap is easier to debug and inspect, even though it uses more memory. The author prioritizes correctness and simplicity for the initial implementation.
What hardware interrupts are handled by NexisK?
The kernel handles the PIT for periodic ticks, keyboard and PS/2 mouse interrupts, and a system‑call interrupt vector (int 0x80).





