Internals

This page explains how Subverge boots (grab a drink, this one gets technical), how an installed disk is laid out, how tasks are scheduled and how the omega runtime manages memory. For the compiler, check JIT, and for root units, check Root.

Boot

The kernel is entered through Multiboot1 in 32-bit mode. It builds a 4 GiB identity map from 2 MiB pages, switches to long mode and calls kmain. QEMU's -kernel option only accepts ELF32, so the build links a 64-bit ELF and converts it with objcopy -O elf32-i386.

The ISO

The ISO boots through GRUB, which is the only code in the system that was not written for Subverge.

Installed drives

Installed drives do not use GRUB. A custom MBR loads stage 2, which does the work that GRUB would otherwise do. It reads the kernel with int 13h extensions and collects the E820 memory map, and it builds a Multiboot info block, so the kernel cannot tell which loader started it. It then enters protected mode, copies the kernel to 1 MiB, clears bss and jumps to _start.

Disk layout

LBA 0        MBR
LBA 1-16     stage 2 (header at +8: "SUBVERGE", kernel lba, sectors, load, bss end, entry)
LBA 64       kernel
LBA 4096     state header "SVSTATE1"
LBA 4097+    1 MiB slots: slot 0 root, slots 1-4 tabs

There are no partitions and no filesystem. A saved world is omega source written into a fixed slot. The kernel keeps a copy of its .data section from boot, so install always writes a clean image.

The layout gives Subverge four tabs and a root of at most 1 MiB each.

Scheduling

Scheduling is preemptive round-robin and is driven by the PIT (nobody gets special treatment). The timer interrupt handler returns the next task's rsp, and that return performs the context switch. A voluntary yield is int 48, and the idle task is the hlt loop in kmain.

There are four tasks, one per tab, each with a 256 KiB stack. A CPU fault restarts that tab's task and keeps its world.

Screen

The screen is 80x25 VGA text (yes, it's tiny, get over it). Row 0 is the tab bar, and each tab has an 80x24 buffer. The DAC palette and the plane-2 font are reprogrammed so that omega, aleph and the subscript digits (0x80 to 0x8B) exist as characters.

Everything in root/ draws for that size.

omega runtime

Values are 24 bytes, {type, i64, object*}. Strings, quotes, ordinals and bindings are heap objects under a mark-sweep GC that runs at safe points between words (so you never take out the trash yourself).

Roots of the collector

The roots are the world, the history, the stack, the locals, the working dictionary, the line being run and a keep stack. call, times, for, while, each, map and filter push their arguments onto the keep stack.

make stress collects before every word and poisons freed memory, so a missed root shows up as 0xDD.

Root rebuilds

Root units are linked into the image and rebuilt incrementally when one is edited, as Root describes.

See also