IN DEVELOPMENTBIOS / x86 / NASM

THE noOS FIELD MANUAL

Know every instruction.

Read the boot path, inspect the memory layout, and run the actual kernel. The source is the specification; the verified gates describe its progress.

What noOS is

noOS is an experimental x86 operating system. A BIOS loads its boot sector, which loads a second stage and then a protected mode Assembly kernel. It runs directly on the virtual hardware QEMU provides.

All code executed by noOS is NASM Assembly. Python and Make build the disk image and run tests on the host; neither is part of the operating system runtime. There is no libc or host operating system inside the guest.

Supported target: QEMU’s legacy BIOS pc machine with VGA text mode, the legacy PIC, PIT, COM1 serial output, and a PS/2 keyboard. Physical hardware and UEFI boot are unverified.

Boot & architecture

BIOS → first stage

Loads the first sector at 0x7C00; the loader preserves the BIOS boot drive.

boot/stage1.asm
16-BIT REAL MODE
First stage → second stage

Reads the second stage from the raw disk image and transfers control to 0x8000.

boot/stage2.asm
BIOS DISK SERVICES
Second stage → protected mode

Preserves the E820 map, loads the kernel, checks A20, installs a GDT, sets CR0.PE, and makes a far jump.

boot/gdt.asm
FLAT SEGMENTATION
Protected mode → kernel

Initializes segment registers and a stack, then enters the kernel at 0x10000.

kernel/entry.asm
32-BIT KERNEL

Kernel initialization

kernel/main.asm initializes and checks the VGA terminal, IDT, PIC, physical allocator, heap, paging, read-only initrd, and ATA-backed noFS mount. It then installs the PS/2 keyboard, detects the CPU, configures the PIT, enables interrupts, checks tick-based delay, and starts the shell.

Hardware ownership

The kernel writes its VGA terminal through 0xB8000. The PS/2 driver receives keyboard events through IRQ1 after the PIC is remapped and the IDT installed. The PIT drives IRQ0. COM1 serial output exposes boot and diagnostic events to QEMU’s host terminal.

The source modules live in kernel/. The roadmap below records each implementation’s verification status.

Memory & paging

The second stage preserves BIOS E820 records. The kernel validates the map, distinguishes usable from reserved ranges, reserves the first MiB, and allocates complete 4 KiB physical frames from usable RAM below 128 MiB.

AddressPurposeOwner
0x4FF0 / 0x4FF4E820 record count / map signatureLoader handoff metadata
0x5000E820 memory map bufferSecond stage / memory discovery
0x7C00512-byte BIOS boot sectorFirst stage
0x8000Second stage load addressLoader
0x10000Kernel load and entry address32-bit kernel
0x70000–0x7FFFF64 KiB kernel heap arenaHeap allocator
0x88000–0x8FFFFKernel stack region; initial top 0x90000Kernel stack
0xB800080 × 25 VGA text bufferVGA hardware
0x00000–0xFFFFFReserved first MiBBoot environment and hardware

Physical frames and heap

The physical allocator tracks eligible, busy, and pinned frames. It handles exhaustion and rejects unaligned, out-of-range, invalid, double, and pinned-frame frees. The 64 KiB heap uses 16-byte aligned allocations, first fit, block splitting, and adjacent free block coalescing.

mem reads the actual E820 usable total, managed usable frames, unavailable frames inside the managed window, allocated/free frames, and heap usage. Detected usable memory above 128 MiB contributes to the E820 total but is outside the allocator’s current management window.

Supervisor paging

The kernel identity maps the first 128 MiB with supervisor-only page tables. The null page is absent, and the VGA page is uncached. CR0 paging and supervisor write protection are enabled. The bootstrap page directory and 32 page tables occupy 33 pinned frames that cannot be released through the allocator.

Kernel workers share the supervisor address space. Each ring 3 execution receives a private page directory/table and separate user mappings. The mapped window also includes reserved and unavailable addresses; only E820-eligible frames enter the physical free pool. Boot checks validate the kernel, stack, E820, heap, and VGA mappings before paging is enabled.

Shell commands

The shell runs inside the kernel and implements twenty-seven commands. Type help in the guest to see the command menu for your build.

CommandPurpose
helpList implemented commands.
clearClear the VGA terminal.
version / aboutPrint the kernel identity and project description.
echo [text]Write text to the terminal.
historyShow previously entered commands.
color FBSet foreground then background using two hex digits; color e1 is yellow on blue.
memPrint detected and managed memory, frame counts, and heap usage.
cpuPrint CPUID vendor, family, model, stepping, and leaf 1 EDX features; report when CPUID is unavailable.
uptimeRead live 64-bit PIT tick and elapsed second counters.
sysinfoCombine version, CPU, memory, uptime, keyboard IRQ, and dropped-event diagnostics.
lsList the current noFS directory or the read-only initrd mount.
cat FILERead a named noFS or initrd file; reject noFS data with a bad checksum.
pwdPrint the current directory or /initrd.
cd DIRAccept /, /initrd, .., or a child directory name.
mkdir NAMECreate a persistent directory.
touch NAMECreate an empty persistent file.
write FILE [TEXT]Replace an existing file’s contents and flush the change to disk.
rm NAMERemove a file or an empty directory; does not securely erase data.
tasks on|offEnable PIT worker preemption or stop the workers.
psPrint actual ring 0 task state, work, switch, and IRQ tick counters.
user demo|fault|write|stack|io|spinRun the embedded ring 3 demo or privilege checks; return to the shell.
run NAME.elfExecute a validated static ELF32 program from the initrd in ring 3.
benchMeasure 100,000 real frame allocation/free pairs in elapsed PIT ticks.
reboot / haltRestart or halt the guest machine.
panic [page|gp|divide]Trigger a real exception: invalid opcode by default, or page fault, general protection, or divide error.

Line editing

Left and Right move the cursor. Home and End jump within the line. Delete and Backspace remove characters; Escape clears the draft. Tab inserts one space. Up and Down navigate eight history entries while preserving the current draft. Input is bounded to 127 ASCII characters.

Filesystem commands operate on the current writable noFS directory or the read-only /initrd mount. Kernel task and user mode commands expose the verified scheduler and synchronous ring 3 runtime.

Files & storage

The writable root, /, is backed by noFS on a separate primary IDE slave disk. The kernel uses ATA PIO for actual sector reads, writes, and cache flushes. It never writes the primary master boot disk.

Persistent noFS

mkdir creates directories; touch creates empty files. write replaces the contents of an existing file. rm removes a file or an empty directory. cd accepts /, /initrd, .., or a child directory name; pwd prints the current path.

INSIDE THE noOS SHELL
mkdir notes
cd notes
touch note.txt
write note.txt Hello from noOS
cat note.txt
pwd
cd /

The volume has 32 file/directory slots and supports at most three directory components. Names are 1–31 printable ASCII characters with no spaces or slash; . and .. are reserved path components. File storage is bounded to 4 KiB. The shell has a 127-character line limit, so text entered through write is also constrained by the command and file name.

The read-only mount

/initrd is a virtual, flat, read-only mount embedded in the boot image. It bundles hello.txt, manual.txt, and the three sample ELF programs; cat prints their actual bytes from validated initrd data. Write commands are refused in this mount.

READ THE EMBEDDED FILES
cd /initrd
ls
cat hello.txt
pwd
cd /

Initrd startup validates magic, version, entry count, total length, bounded printable names, duplicates, and data bounds. Overflow, overlap, and invalid metadata are rejected before file access.

Disk integrity and recovery

noFS keeps two five-sector metadata banks with generation counters and FNV-1a checksums. It writes and flushes directory sectors before writing the header commit record last. Each file alternates between two eight-sector data banks, with an integrity checksum checked before contents are displayed.

Mounting uses a structurally valid metadata bank and can fall back to the other bank. If neither is valid, the disk is left untouched and writes are refused; /initrd remains readable. An uncertain write failure takes the volume offline until reboot remounts it.

The host volume is data/noos-data.img, a 512 KiB image created only when absent. make clean preserves it. Tests demonstrate persistence across complete QEMU process closure and reopening, but do not establish a real power loss guarantee. rm does not perform secure erasure.

Inspect kernel/fs/nofs.asm, shell filesystem handlers, the ATA driver, and the initrd validator.

Timer & CPU

The PIT uses a 1,193,182 Hz input clock and divisor 11,932, producing roughly 100 IRQ0 ticks per second. Both tick and elapsed second counters are 64-bit. Seconds are derived from accumulated PIT input cycles rather than assuming an exactly 100 Hz rate.

The kernel’s tick-based delay uses interrupts and halt while waiting. It rejects disabled interrupts and invalid tick counts. uptime reads the counters; sysinfo also exposes actual keyboard IRQ and dropped-event counts.

CPU initialization first tests CPUID availability, then obtains vendor, family, model, stepping, and feature bits from the virtual CPU. Values displayed by cpu come from that hardware response.

Measure the current build

bench performs 100,000 actual physical frame allocation/free pairs and reports elapsed PIT ticks. The timer quantizes the measurement and includes interrupt and worker preemption time. A zero result means the work finished below tick resolution. Results describe the current emulated run, with no fixed throughput or physical hardware claim. The recorded build measurements include binary sizes and host/QMP timings; these timings include host and emulator overhead.

INSIDE THE noOS SHELL
cpu
uptime
sysinfo
bench

Tasks & privilege

PIT preemption

The shell and two workers have real saved interrupt contexts. Each worker has its own reserved kernel stack. tasks on starts PIT-driven round robin with a two-tick quantum; tasks off stops the workers while the shell continues. ps reads actual state, work, switch, and IRQ tick counters.

All three tasks run in ring 0 and share the kernel address space. Boot checks verify cooperative switching and preemption of non-yielding workers, including register preservation and return to the shell.

INSIDE THE noOS SHELL
tasks on
ps
tasks off
user demo
user spin

A real ring 3 transition

user demo enters CPL3 through IRETD with user code/data selectors and a TSS. Each execution gets a private page directory/table, code and stack frames. User addresses begin at 0x40000000, outside the kernel identity window. The embedded demo code is read-only; its stack has an absent guard page below it. Kernel pages stay supervisor-only.

INT 80h enters a dedicated TSS kernel trap stack. User IOPL is zero and the absent I/O bitmap denies port access. Syscall writes validate overflow, bounds, and every covered page before reading bytes.

EAX syscallArgumentsResult
1 — writeEBX = user pointer; ECX = length, at most 1024 bytesBytes written, or -14 for an invalid buffer.
2 — exitEBX = statusReturn to the suspended kernel caller.
3 — ticksNo argumentsLow 32 bits of the actual PIT tick counter.
Unknown—-38.

Fault demonstrations

user fault attempts a kernel-memory read; user write attempts to modify read-only code; user stack accesses the guard page; user io attempts forbidden port I/O. These real exceptions terminate that user execution, restore the shell, and release its owned frames. user spin does visible user work until thirty PIT ticks have elapsed, then exits.

One user program runs synchronously at a time. Kernel workers pause while it runs; PIT and keyboard interrupts remain live. On return, the kernel restores CR3, segments, stack, flags, and worker scheduling state before releasing user frames. Concurrent user scheduling and FPU context switching are outside the current implementation.

Inspect the scheduler, the ring 3 runtime, and user mode checks.

Static ELF32 programs

run NAME.elf loads a bundled program from the validated /initrd data, regardless of the current directory. The applications and their genuine ELF headers are generated with NASM from user/.

INSIDE THE noOS SHELL
run hello.elf
run checks.elf
run fault.elf

hello.elf writes a message and exits. checks.elf checks writable data, zero-filled BSS, and validated cross-page syscall buffers. fault.elf attempts a supervisor-memory read and returns through the user fault handler.

The accepted subset

The loader accepts little-endian ELF32, System V ABI version 0, ET_EXEC, EM_386, and format version 1. Its bounded program table has one to four ordered PT_LOAD entries. File offsets and virtual addresses are 4 KiB aligned, alignment is exactly 4096, and segments cannot share pages.

All loaded memory fits in the 64 KiB user window beginning at 0x40000000. File bytes cannot exceed segment memory size, every file range is checked for bounds and overflow, and the entry point must be inside file-backed RX bytes. Only R, RX, and RW flags are accepted; writable mappings follow PF_W. Frames are zeroed before copying, leaving BSS zero-filled.

The complete metadata is checked before allocation or copying. Exit, faults, and load failure release owned user frames. The loader ignores section tables and physical-address fields; dynamic linking, relocations, PIE, TLS, argv, and the Linux syscall ABI are unsupported.

Programs use the noOS syscall ABI and run one at a time. There is no execution timeout; a program that never exits requires a VM reset. This non-PAE mode has no NX bit, so data and stack pages can execute even though W+X ELF flags are refused.

kernel/tasks/elf.asm implements the loader. test_elf.py checks the three real programs, fault recovery, frame cleanup, and thirty malformed metadata fixtures.

Build & test

Install NASM, QEMU, GNU Make, and Python 3. Build tools run on the host; the result is a raw BIOS disk image that QEMU boots.

BUILD AND BOOT
git clone https://github.com/OthmaneBlial/noOS.git
cd noOS
make
make run
TargetResult
makeAssemble the stages and kernel; build build/noos.img.
make runBoot the image with QEMU, 128 MiB of RAM, and serial output in the host terminal.
make testBuild and run the automated QEMU checks defined in the current checkout.
make debugStart paused with a GDB server at 127.0.0.1:1234.
make sizeReport the current binary sizes.
make cleanRemove generated build output; preserve data/noos-data.img.

The boot image, NASM listing, and binaries are generated locally. A separate persistent volume at data/noos-data.img is created only when absent; make run attaches it as IDE slave alongside the boot image. Inspect Makefile and tests/ for the exact build and verification commands. A QEMU pass establishes virtual platform behavior; it is not a physical machine test.

What the tests inspect

make test runs QEMU checks for boot, VGA, keyboard, shell, memory, paging, initrd, persistent noFS, scheduler contexts, ring 3, the ELF subset, frame benchmark accounting, and hardware diagnostics. The memory matrix uses 16, 64, 128, and 256 MiB guests and compares the preserved E820 records with allocator accounting. The 256 MiB case checks that detected memory above the management ceiling stays outside the free pool.

The suite also inspects actual page directory and table entries, supervisor permissions, the absent null page, VGA cache flags, and pinned paging structures. Fault tests deliberately trigger invalid opcode, page fault, general protection, and divide error exceptions. Hardware tests check live PIT counter advancement, CPU data, and keyboard IRQ statistics.

Initrd tests read the actual two files, check missing files and directory errors, and reject six corrupt image fixtures: bad magic, excessive count, invalid data offset, invalid length, unterminated name, and duplicate name. See test_initrd.py.

The noFS suite closes and reopens the QEMU process to check persistence, inspects raw disk sectors, checks directory and slot limits, validates corrupt-bank fallback, and confirms that invalid volumes are left untouched. It rejects six structurally malformed metadata fixtures even when their checksums are valid, and refuses to display file data with a bad integrity checksum. See test_filesystem.py. These checks do not simulate every real power loss failure.

User tests inspect actual CPL3, the TSS kernel trap stack, private CR3, code and stack permissions, the guard page, denied I/O, real fault recovery, and restoration of allocated frame counts. Scheduling tests inspect advancing worker counters under PIT preemption.

ELF tests execute the real hello, fault, and data/BSS/check applications, verify cross-page syscall behavior and frame cleanup, and reject thirty malformed metadata fixtures. Frame benchmark checks preserve free and allocated frame counts while reporting actual elapsed PIT ticks.

See test_memory.py, test_paging.py, and test_hardware.py for assertions and scope.

GDB debugging

Use x86_64-elf-gdb, or another GDB build with i386 target support. Run make debug in one host terminal; QEMU’s -S option pauses the guest before execution. Connect from a second terminal.

CONNECT TO THE PAUSED GUEST
x86_64-elf-gdb
set architecture i386
target remote 127.0.0.1:1234
hbreak *0x10000
continue
info registers
x/12i $pc

The hardware breakpoint stops at protected mode kernel entry. Flat NASM binaries do not carry ELF debug symbols; build/kernel.lst helps correlate instructions with source. stepi advances one instruction; x/16wx 0xB8000 inspects VGA memory.

Keep the GDB server bound to 127.0.0.1. The debug target already uses this loopback address.

Verified roadmap

Each milestone contains five equally weighted gates. Only a verified gate contributes to the total. The forty-gate checklist covers this defined implementation scope; even 100% does not establish production maturity or unrestricted platform support. This manual and the landing page read the same roadmap data.

—Loading verified gates…

Loading the roadmap…

Limits & contributing

noOS is a developing kernel. UEFI, x86-64, physical hardware compatibility and a future x86-64 path require their own implementation and evidence. ELF support is the explicitly bounded static subset above. Kernel tasks share one address space; user execution is synchronous, with no concurrent user scheduler or FPU context switching. The roadmap distinguishes completed work from planned work.

Keep runtime code in NASM Assembly, validate untrusted input, and include a focused test for nontrivial behavior. Report the exact checkout, QEMU command, observed output, and reproduction steps when filing a bug.

Completed milestones are tested, committed, and pushed to main. Roadmap gates increase only when the implementation is verified. Review the engineering roadmap, inspect the source repository, or open an issue.

noOS is available under the MIT license.