To take a C script and run it on the Virtual Machine, the code passes through three distinct phases:
- Compilation (
chibicc->.asm) The custom port ofchibicctranslates C code into the VM's native stack-based assembly. Crucial Note: You MUST use the-Sflag.chibicctries to behave like GCC and will invoke the host Linux assembler by default. Because our assembly is custom, the Linux assembler will crash.-Stells the compiler to output the raw assembly text and stop.
./chibicc -S -o program.asm program.c
- Assembly (
assembler->.bin) The custom assembler reads the.asmtext file and converts the stack instructions (likeENT,PSH,LOD,SYS) into raw 32-bit bytecode that the VM can natively execute.
./assembler program.asm program.bin
- Execution (
vm/app) The hardware emulator (built with Raylib) allocates memory, sets up the VRAM (320x200), initializes the MMIO keyboard array, and begins reading the bytecode.
./cpu program.bin
-
.c: Standard C code. This project contains two types of C code: -
Host Tools: Programs compiled with standard
gccto run on your host Linux machine (e.g.,compiler.c,assembler.c,cpu.c,imager.c). -
VM Programs: Programs compiled with
chibiccto run inside the VM (e.g.,raycaster.c,cursor_test.c). -
.asm: Custom human-readable stack assembly generated bychibiccor written by hand. -
.bin: The final executable bytecode. -
.img: A virtual Hard Drive file packed byimager.c, containing the Sector 9 directory table and.binfiles.
- Math: The VM uses strictly fixed-point integer math. No floating-point (
float/double) is supported. - VRAM: Starts at
1048576(0x100000). 320x200 resolution, 8-bit color. Mapped viachar *vram = (char *)1048576;. - Keyboard MMIO: Starts at
2097152(0x200000). Array of 256 keys. Mapped viachar *keys = (char *)2097152;. - Standard Library: There is no
libc. All interactions with the screen, keyboard, or disk are done via direct memory mapping or Syscalls (SYS) triggered via inline assembly.
This project features a custom build pipeline that compiles C code, assembles it into VM bytecode, and packs it into a virtual hard drive.
The pipeline supports two primary modes of execution:
Use this to rapidly test a single program. The VM bypasses the bootloader and file system, immediately loading the requested .bin file into memory address 0 and executing it.
Command: ./build.sh [program_name]
Example: ./build.sh raycaster
Use this to boot the actual Operating System. The VM will read drive.img, execute the C-based bootloader (boot.bin), and present a menu allowing the user to browse Sector 9 and launch programs dynamically.
Command: ./build.sh [program_name] --os
(Note: In OS mode, the [program_name] argument is just used to ensure the program is assembled and packed into the drive; the bootloader will still be the first thing that runs).
When you run the build script in --os mode, here is the exact flow of data:
- Toolchain Compilation: The host C tools (
cpu,assembler,imager,font_maker) are built viamake. - C Compilation:
chibicccompilesprograms/boot.cinto raw stack assembly (boot_temp.asm). - Linking:
crt0.asm(the C Runtime Entry Point) is concatenated to the top ofboot_temp.asmto createboot.asm. This ensures the VM's Program Counter starts at a safe initialization block. - Assembly: The custom
./assemblerconverts.asmfiles into binary.binbytecode. - Disk Imaging:
./imagerpacksboot.bin,font.bin, and all other program binaries intodrive.img. It automatically generates the Sector 9 Directory Table. - Execution:
./cpulaunches, mountsdrive.img, and begins executing bytecode.