File structure
Host-side build tooling
add_file.py— Host-side script to add files to the drive image filesystemcc.py— Host-side C subset compiler (translatesuser/programs/*.cto NASM-compatible assembly)make_os.sh— Build script (assembles kernel, compiles C programs viacc.py, creates floppy image)
Shared includes
kernel/include/constants.asm— Shared constants (BUFFER,DIRECTORY_SECTOR,SECTOR_BUFFER,NE2K_BASE,PROGRAM_BASE,MAX_ARGV_ENTRIES,SYS_*syscall numbers, etc.)kernel/include/dns_query.asm,encode_domain.asm,parse_ip.asm— Shared DNS/IP helpers; see source headers for calling conventions.
Boot and kernel core
kernel/arch/x86/boot/boot.asm— Pre-paging boot binary (org 0x7C00): MBR + post-MBR real-mode bootstrap (secondINT 13hread ofkernel.bindirectly into phys0x20000, E820 probe, PIC remap, A20, GDT load,CR0.PEflip) + 32-bitearly_pe_entry(build boot PD + first kernel PT, enable paging, far-jump tohigh_entryat virt0xFF820000). No real-mode-to-PE relocation copy —KERNEL_LOAD_PHYS = 0x20000is the kernel’s final home. Post-MBR region pads to the next 512-byte boundary;BOOT_SECTORSis derived ((boot_end - post_mbr_continue) / 512) so the count auto-grows when the boot code crosses a sector.make_os.shonly has to measurekernel.bin’s sector count for the-DKERNEL_SECTORS=Nsecond-pass nasm invocation.kernel/arch/x86/kernel.asm— Post-paging high-half kernel (section .text progbits vstart=0FF820000h=DIRECT_MAP_BASE + KERNEL_LOAD_PHYS, plus asection .bss nobits follows=.textfor zero-init reservations):high_entry(segment / GDT / IDT / stack setup, identity-drop, bitmap init, kernel-PT allocation, kernel_idle_pd build, boot-PD freeback) followed by%includes of every kernel subsystem (drivers, fs, helpers, net stack, syscall dispatcher, IDT, post-flip entry, frame allocator, address-space helpers) and the kernel GDT. The libbboeos blob is no longerincbin‘d here — it ships aslib/libbboeoson the disk image andlibbboeos_installloads it at boot.kernel/arch/x86/idt.asm— 32-bit IDT with CPU exception stubs andINT 30hgate;idt_init(called fromhigh_entry) patches the high-half handler offsets at boot since the IDT_ENTRY macro can only emit the low 16 bits innasm -f binmode (section-relative labels reject& 0FFFFh/>> 16arithmetic). Any post-flip exception lands inexc_commonand printsEXCnn EIP=h CR2=h ERR=hon COM1.kernel/arch/x86/entry.asm—protected_mode_entry(TSS patch, PIT + IRQ handler install, driver / VFS / NIC inits, banner) flowing intoshell_reload(loadsbin/shelland jumps),program_enter(fd reset, BSS zero via the trailer-magic protocol, ESP snapshot,iretdto ring 3 atPROGRAM_BASE), and the IRQ 0 / IRQ 6 handlers. Segment / GDT / IDT / ESP setup happens inkernel.asm’shigh_entrybefore falling intoprotected_mode_entry; the ring-0 stacks (three 1 KB slots:kernel_stack,kernel_stack_b,kernel_stack_c) live at virtKERNEL_VIRT_BASE + KERNEL_RESERVED_BASEupward (≈0xFF828000for the current ~40 KB kernel) — see Kernel stack note indocs/architecture.md, not in entry.asm.
Memory management
kernel/memory_management/frame.asm— Bitmap physical-frame allocator:frame_alloc/frame_free/frame_init(two-pass E820 walker — first pass finds the highest type=1 frame base, between passes sizes the bitmap, second pass marks free regions) /frame_reserve_range. Bitmap is sized at boot from E820, clamped to FRAME_PHYSICAL_LIMIT (~4 GB, the 32-bit phys ceiling), and lives in the post-kernel cluster at FRAME_BITMAP_PHYS. Frames above FRAME_DIRECT_MAP_LIMIT (the kernel direct-map ceiling) reach the kernel via the kmap window — seekmap.asm.kernel/memory_management/access.asm—access_okuser-buffer pointer-validation helper (rejects ranges that span the user/kernel boundary or wrap), invoked by syscall handlers before touching userspace memory.kernel/memory_management/address_space.asm— Per-program PD lifecycle:address_space_create(allocate PD, copy-image kernel half fromkernel_idle_pd, build user PTs for handoff frame + libbboeos + program text/BSS + stack),address_space_destroy(free user frames, skipping shared-AVL PTEs, then free PTs and PD), and the page-mapping primitives (address_space_map_page, etc.) those two drive. All PD/PT reads and writes go throughkmap_map/kmap_unmapso a high-physical PD or PT frame stays addressable.kernel/memory_management/kmap.asm— kernel temporary-mapping window at PDE 1023 (virt0xFFC00000..0xFFFFFFFF).kmap_init(called fromhigh_entryafter the idle PD takes over) allocates one frame as the window PT and installs it atkernel_idle_pd[1023]; per-program PDs inherit it via the kernel-half copy-image inaddress_space_create.kmap_map(eax = phys) → eax = kernel_virtfast-paths to the direct-map alias when phys is below FRAME_DIRECT_MAP_LIMIT and falls back to a slot in the window for higher frames.kmap_unmapreleases the slot.KMAP_SLOT_COUNT = 4covers the deepest concurrent nesting in the tree (PD + PT walks inaddress_space_destroy); slot exhaustion panics.
Syscalls and system
kernel/arch/x86/syscall.asm—INT 30hdispatcher: per-handler bodies inlined directly (fs_, io_, rtc_, sys_) and tail-jump shims intokernel/syscall/syscalls.cfor the four net_* handlers. The.iret_cfpath sign-extends AX into EAX before iret;.iret_cf_eaxis the explicit-32-bit variant used byio_read/io_write, whose byte counts can exceed 32 767.kernel/syscall/syscalls.c— C bodies for the four non-trivial network handlers:sys_net_mac,sys_net_open,sys_net_recvfrom,sys_net_sendto. Reached viacall sys_net_X; jmp .iret_cfshims insyscall.asm.kernel/arch/x86/system.c—reboot(8042 reset),shutdown(APM / QEMU / Bochs shutdown ports).
Drivers
kernel/drivers/console.c— Unified output:put_character(ANSI parser + screen + serial mirror with auto\n→\r\n) andput_string. Delegates raw bytes toserial_character(drivers/serial.c) and ANSI cursor / palette commands to the VGA helpers indrivers/vga.c.kernel/drivers/serial.c— COM1 driver:serial_character(output) andserial_check/serial_read(input, polled byfd_read_console).kernel/drivers/ata.c,kernel/drivers/fdc.c— Hardware disk drivers (ATA PIO and floppy DMA); called viafs/block.asm’sread_sector/write_sectordispatch (AX = 0-based sector number).kernel/drivers/ne2k.c— NE2000 ISA NIC driver (polled-mode Ethernet); I/O base0x300, IRQ 3.kernel/drivers/opl3.c— SB16 OPL3 register-write driver: chip probe + outb-based register writes used by/dev/midi(no IRQ; the kernel drains the queue from IRQ 0).kernel/drivers/ps2.c— PS/2 keyboard driver:ps2_init,ps2_check,ps2_read.kernel/drivers/rtc.c— RTC / PIT timer: tick counter,rtc_sleep_msbusy-wait, CMOS date read.kernel/drivers/vga.c— VGA driver: text and mode-13h helpers (vga_set_mode,vga_clear_screen,vga_fill_block,vga_set_palette_color, …) plusfd_ioctl_vga(the/dev/vgaioctl dispatcher forVGA_IOCTL_MODE/VGA_IOCTL_FILL_BLOCK/VGA_IOCTL_SET_PALETTE).
Filesystem and VFS
kernel/fs/fd.c— File descriptor table and dispatch:fd_open(synthesizes/dev/vgaintoFD_TYPE_VGAwithout touching the filesystem),fd_read,fd_write,fd_close,fd_fstat,fd_ioctl. Per-fd-type handlers live underkernel/fs/fd/(audio.c,console.c,fs.c,midi.c,net.c).kernel/fs/fd/midi.c—/dev/midievent-ring + dispatch (FD_TYPE_MIDI = 6). 256-slot ring of 6-byte(delay_lo, delay_hi, bank, reg, value, reserved)commands; the IRQ 0 drainer pops up to 16 events per 1 ms tick and forwards register writes throughkernel/drivers/opl3.c. Implementsfd_read_midi,fd_write_midi,fd_close_midi, andfd_ioctl_midi(MIDI_IOCTL_DRAIN). Single-opener.kernel/fs/block.asm— Block I/O dispatcher:read_sector,write_sector(dispatches to fdc/ata based onboot_disk).kernel/fs/bbfs.asm— BBoeOS filesystem (VFS backend):bbfs_chmod,bbfs_create,bbfs_find,bbfs_init,bbfs_load,bbfs_mkdir,bbfs_rename,bbfs_update_size, plus internal helpers (find_file,scan_directory_entries, etc.).kernel/fs/ext2.asm— ext2 filesystem (second VFS backend, auto-detected byvfs_init).kernel/fs/vfs.c— VFS layer: runtime function-pointer table (vfs_find_fn, etc.),vfs_found_*state struct, thin wrapper functions (vfs_find,vfs_create,vfs_rmdir, …). Detects bbfs vs ext2 at boot and points the function pointers at the corresponding backend.
Networking
kernel/net/net.asm— Four-line orchestrator that%includes the protocol modules:net/arp.asm,net/udp.asm, plusbuild/kernel-c/net/icmp.kasmandbuild/kernel-c/net/ip.kasm(cc.py-compiled fromkernel/net/icmp.candkernel/net/ip.c). The NE2000 hardware driver itself lives indrivers/ne2k.c.
Userland programs
user/programs/— user-facing programs written in the C subset:arp,asm,cat,chmod,cp,date,dns,draw,echo,edit,ls,mkdir,mv,ping,rm,rmdir,shell,uptime.user/programs/edit.c— Full-screen text editor with gap buffer, Ctrl+S save, Ctrl+Q quit. All editor state is file-scope so cc.py parks it in BSS rather than auto-pinning to registers thatbuffer_character_atclobbers (it uses EDX/ECX as scratch). The 448 KB gap buffer (edit_buffer[EDIT_BUFFER_SIZE], sized at0x70000to fit under the-m 1user-pool ceiling) and 2.5 KB kill buffer (edit_kill_buffer[EDIT_KILL_BUFFER_SIZE]) are BSS arrays — the per-program PD thataddress_space_createbuilds gets enough zero-filled user pages to back them via the trailer-magic protocol. Any source file in the tree fits with room to spare (the largest,user/programs/asm.c, is ~131 KB). A singleread(fd, edit_buffer, EDIT_BUFFER_SIZE)fills the buffer in one call:SYS_IO_READreturns the full 32-bit byte count viaEAX(the dispatcher routes io_read through.iret_cf_eax, skipping the AX sign-extend that the rest of the syscall surface uses), so no chunking is needed up to the 448 KB buffer cap.user/programs/asm.c— Self-hosted x86 assembler (two-pass; byte-identical to NASM for everything inuser/static/). Phase 1 port: the driver and handlers still live inside a single file-scopeasm("...")block that wrapsarchive/asm.asm’s original NASM source; follow-up PRs extract pieces into pure C one family at a time. Supported directives and mnemonics are documented in the inline-asm body.
Doom port
ports/doom/fetch_chocolate.sh— pinned-commit fetcher for Chocolate Doom’s OPL music sources (i_oplmusic.c,mus2mid.c,memio.c,opl_queue.c,midifile.c,opl.h). Drops them intothird_party/chocolate-doom-opl/(gitignored) so the build can compile them. Drives Doom’s MIDI playback through themusic_opl_moduleinterface;ports/doom/build.pyinvokes the script before compiling.ports/doom/chocolate_compat.h— narrowly-scoped (~95 line) shim that papers over the chocolate-vs-doomgeneric drift (a few macros + typedefs Chocolate’s OPL stack expects but the doomgeneric tree doesn’t expose).ports/doom/opl_bboeos.c— OPL backend bridging Chocolate’si_oplmusic.cto the kernel’s/dev/midi. TranslatesOPL_WriteRegister/OPL_AdjustCallbacks/OPL_SetPausedcalls into 6-byte midi commands;OPL_InitreturnsOPL_INIT_OPL3soOPL_InitRegistersenables the second register bank.
Smoke tests
tests/programs/— smoke tests written in the same C subset, kept separate from user programs because they exist solely to exercise specific kernel paths fromtests/test_programs.pythat need a real boot to verify:bigbss(256 KB BSS allocation across per-program PD pages — also kmap window end-to-end smoke),bits(bitwise operators|^~<<>>&and compound-assignment forms),booltest(booleanized comparison BinOps used as values),cftest/fctest(call-flow / function-call edge cases),gptest(user-fault#GPkill path inidt.asm’sexc_common— executescliat CPL=3, expects shell respawn),loop(basic while-loop control flow with per-characterprintf),loop_array(sizeof(arr)/sizeof(elt)folding + indexed string-array reads viawrite),nullderef(#PFon virt 0 with the unmapped PTE[0] guard),okptest(user-buffer pointer validation viaaccess_ok),play_midi(opens/dev/midi, queries presence, writes a few short notes — smoke test for the kernel queue + drainer),stackbomb(16-page user-stack overflow into the unmapped guard region),stacktop(assertsUSER_STACK_TOP = KERNEL_VIRT_BASEto catch constant drift). Pure cc.py codegen tests (file-scopeasm()escape,#includedirective,asm_registerglobal aliasing, file-scope BSS globals, brace-initialized global arrays) live intests/test_kernel_cc.pyas compile-and-inspect-asm pytest cases — no QEMU boot needed. Built and packaged into the drive image’sbin/only whenmake_os.shis run with--with-test-programs; the default build keeps them out of the image so a normal boot ships only the user programs.tests/test_programs.pypasses the flag automatically.tests/unit/test_midi_queue.py— pytest unit tests for the kernel midi event ring + drain. Compilestests/programs/midi_queue_harness.cin host-native mode and exercises ring push / drain bounds, timing accumulator, FLUSH semantics, and overflow handling without booting QEMU.tests/test_doom_music_qemu.py— Doom OPL3 music integration test. Boots Doom in QEMU withwads/doom1.wadand grep’s the serial log forBBoe_MusicInit’sOPL music enabled/OPL music unavailablemarker. Doesn’t capture audio (QEMU’ssb16device doesn’t emit OPL FM) — seedocs/requirements.mdfor the QEMU OPL caveat.