Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pastaboy12345/NKLegacy/llms.txt

Use this file to discover all available pages before exploring further.

kmain() is the C entry point called by the Multiboot assembly stub in ntoskrnl/arch/i386/boot.asm. By the time it runs, the CPU is in 32-bit protected mode, a 16 KB kernel stack has been set up at stack_top, and the flat memory model is active. kmain then executes five sequential initialization phases before handing off to the shell loop — none of these phases allocates heap memory or calls any standard library function; every subsystem uses static storage.
The function signature is void kmain(uint32_t magic, void *mboot_info). Both parameters are provided by the Multiboot ABI — magic should equal 0x2BADB002 and mboot_info points to the Multiboot information structure populated by GRUB. NKLegacy currently casts both to (void) and does not use them, but they must appear in the signature to match what the boot.asm stub pushes onto the stack.

Full Source — kmain.c

/*
 * NKLegacy Kernel
 * Windows NT-inspired kernel operating system
 *
 * kmain.c - Kernel main entry point
 */

#include "kprintf.h"
#include "panic.h"
#include "bsod.h"
#include "../arch/i386/gdt.h"
#include "../arch/i386/idt.h"
#include "../driver/vga/vga.h"
#include "../driver/serial/serial.h"
#include "../driver/pic/pic.h"
#include "../driver/pit/pit.h"
#include "../driver/keyboard/keyboard.h"
#include "../exe/cmd.h"
#include "../fs/nkfs/nkfs.h"
#include <nk/string.h>

#define NK_VERSION "0.1.0"
#define NK_CODENAME "Legacy"

void kmain(uint32_t magic, void *mboot_info) {
    (void)magic;
    (void)mboot_info;

    /* ---- Phase 1: Early Hardware Init ---- */

    /* Initialize serial port for early debug output */
    serial_init(COM1);
    serial_print(COM1, "NKLegacy: Serial debug initialized\n");

    /* Initialize VGA text mode */
    vga_set_color(0x0F);  /* White on black */
    vga_clear();

    /* ---- Phase 2: CPU Tables ---- */

    serial_print(COM1, "NKLegacy: Loading GDT...\n");
    gdt_init();

    serial_print(COM1, "NKLegacy: Loading IDT...\n");
    idt_init();

    /* ---- Phase 3: Interrupt Hardware ---- */

    serial_print(COM1, "NKLegacy: Initializing PIC...\n");
    pic_init();

    serial_print(COM1, "NKLegacy: Initializing PIT...\n");
    pit_init();

    serial_print(COM1, "NKLegacy: Initializing keyboard...\n");
    keyboard_init();

    /* Enable interrupts */
    __asm__ volatile("sti");
    serial_print(COM1, "NKLegacy: Interrupts enabled\n");

    /* ---- Phase 4: Display Boot Banner ---- */

    /* NT-style boot header */
    vga_set_color(0x0B);  /* Light cyan */
    vga_print("NKLegacy Kernel ");
    vga_set_color(0x0F);  /* White */
    vga_print("v" NK_VERSION);
    vga_print(" [Build: " __DATE__ " " __TIME__ "]\n");

    vga_set_color(0x08);  /* Dark gray */
    vga_print("Copyright (c) 2026 William Beauregard. MIT License.\n");
    vga_print("Windows NT-inspired kernel operating system.\n\n");

    /* Initialization status */
    vga_set_color(0x0A);  /* Light green */
    vga_print("  [OK] ");
    vga_set_color(0x07);
    vga_print("Global Descriptor Table\n");

    vga_set_color(0x0A);
    vga_print("  [OK] ");
    vga_set_color(0x07);
    vga_print("Interrupt Descriptor Table\n");

    vga_set_color(0x0A);
    vga_print("  [OK] ");
    vga_set_color(0x07);
    vga_print("Programmable Interrupt Controller (8259)\n");

    vga_set_color(0x0A);
    vga_print("  [OK] ");
    vga_set_color(0x07);
    vga_print("Programmable Interval Timer (1000 Hz)\n");

    vga_set_color(0x0A);
    vga_print("  [OK] ");
    vga_set_color(0x07);
    vga_print("PS/2 Keyboard Driver\n");

    vga_set_color(0x0A);
    vga_print("  [OK] ");
    vga_set_color(0x07);
    vga_print("Serial Debug (COM1 @ 115200)\n");

    vga_print("\n");
    vga_set_color(0x0F);
    kprintf("NKLegacy kernel initialized. %u ticks elapsed.\n\n", pit_get_ticks());

    vga_set_color(0x0E);  /* Yellow */
    vga_print("nklegacy> ");
    vga_set_color(0x0F);

    serial_print(COM1, "NKLegacy: Kernel initialization complete\n");

    nkfs_init();

    /* ---- Phase 5: Shell Loop ---- */
    cmd_run();
}

Phase-by-Phase Walkthrough

1

Phase 1 — Early Hardware Init

The very first call is serial_init(COM1), which programs the UART for COM1 at 115200 baud, 8-N-1. Because this happens before the GDT or IDT have been loaded, it gives a reliable out-of-band debug channel: even a fault during GDT loading would still produce readable output on the serial port. The immediately following serial_print confirms the port is live.vga_set_color(0x0F) writes the attribute byte (foreground = white, background = black) that subsequent vga_print calls will use. vga_clear() then fills all 2000 character cells of the 80×25 VGA text buffer at 0xB8000 with spaces using that attribute, producing a clean black screen.
2

Phase 2 — CPU Tables

gdt_init() builds the Global Descriptor Table in a static array, fills the null descriptor, a flat ring-0 code segment (base 0, limit 4 GB, 32-bit), and a flat ring-0 data segment, then executes lgdt and reloads CS via a far jump and all data segment registers with the data selector.idt_init() fills all 256 IDT entries with ISR stubs (assembled in ntoskrnl/arch/i386/isr.asm), wires up the exception handlers, and executes lidt. Both tables are in static storage — no dynamic allocation is involved.These two steps must be completed before any interrupt can be safely fielded, which is why Phase 3 (which ultimately calls sti) comes strictly after.
3

Phase 3 — Interrupt Hardware

pic_init() sends Initialization Command Words to both the master (port 0x20) and slave (port 0xA0) 8259A PICs to remap IRQ0–IRQ7 to interrupt vectors 0x200x27 and IRQ8–IRQ15 to 0x280x2F. This moves hardware interrupts above the 32 CPU-reserved exception vectors.pit_init() programs channel 0 of the 8253/8254 Programmable Interval Timer in mode 3 (square-wave) with a divisor that produces a 1000 Hz IRQ0 rate, giving a 1 ms tick resolution. The tick counter is later read by pit_get_ticks() for the boot banner.keyboard_init() unmasks the keyboard IRQ line in the PIC and installs the PS/2 keyboard ISR so that keypresses begin generating scancode interrupts.Finally, __asm__ volatile("sti") sets EFLAGS.IF, enabling CPU interrupt delivery. From this point on the PIT fires every millisecond and keystrokes are captured.
4

Phase 4 — Boot Banner and NKFS Init

The banner sequence uses vga_set_color() extensively to reproduce the NT-style colored status output. The header line combines the compile-time string macro NK_VERSION "0.1.0" with the C preprocessor’s __DATE__ and __TIME__ tokens to embed the exact build timestamp.Six [OK] status lines (light green 0x0A for the tag, gray 0x07 for the description) confirm each subsystem that was initialized in Phases 1–3.kprintf("NKLegacy kernel initialized. %u ticks elapsed.\n\n", pit_get_ticks()) prints the number of PIT ticks that have accumulated since pit_init() ran, demonstrating that the timer ISR has been firing live.nkfs_init() zeroes and initializes the NKFS in-RAM filesystem: it clears the 256-node node_pool and the 512 KB data_pool (both static arrays in .bss), sets the allocation counters to zero, and creates the root directory node named "C:".
5

Phase 5 — Shell Loop

cmd_run() is the terminal call in kmain. It implements an infinite read-dispatch loop: it reads characters from the PS/2 keyboard driver, echoes them to the VGA console, and on each newline parses and dispatches the command. It never returns. If it did, the boot.asm entry stub would execute cli / hlt to halt the CPU indefinitely.

Build docs developers (and LLMs) love