aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README.md102
1 files changed, 102 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bd6c5cf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,102 @@
+# gbos
+
+A tiny **Unix-flavored microkernel for the Game Boy Color** (MBC5 cartridge).
+Not Linux, not FUZIX — a from-scratch cooperative kernel that borrows the V7
+*process model* (separate read-only text + per-process data, a syscall trap,
+round-robin scheduling) and squeezes it into the GBC's banked, MMU-less memory.
+
+This is a **scaffold**, but a *working* one: it assembles into a valid 32 KiB
+ROM, brings up a process table, and round-robin context-switches between two
+demo tasks that each `write()` a byte and `yield()`. Verified end-to-end in the
+`~/dev/gbc` emulator (headless serial console) — it streams `ABABAB...`.
+
+## Why this shape (the hardware reality)
+
+- No MMU, no memory protection. "Unix" here = the API/process model, not isolation.
+- CPU only ever sees **64 KiB**; RAM is banked.
+- **Kernel code lives in ROM** (`$0000-$3FFF`, up to 8 MiB via MBC5) — free, read-only.
+- **Program text lives in ROM banks** (`$4000-$7FFF`) — the V7 "pure text" idea.
+- **Per-task RW state** (data/bss/heap/stack) lives in one **8 KiB cart-RAM bank**
+ (`$A000-$BFFF`, MBC5, up to 128 KiB / 16 banks).
+- The context switch swaps banks, so it **must** run from **HRAM** (never banked)
+ and never touch a swappable stack mid-swap.
+
+## Memory map
+
+```
+$0000-$3FFF ROM0 kernel core (rst/IRQ vectors, init, sched, syscalls) - fixed
+$4000-$7FFF ROMX current task TEXT (read-only program code)
+$8000-$9FFF VRAM graphics (unused so far)
+$A000-$BFFF SRAM current task DATA/BSS/HEAP/STACK (MBC5 RAM bank)
+$C000-$CFFF WRAM0 kernel globals + kernel stack (never swapped)
+$D000-$DFFF WRAMX per-process u-area (SVBK bank) (reserved, not used yet)
+$FF80-$FFEE HRAM context-switch trampoline (111 bytes)
+$FFEF-$FFF2 HRAM bank shadows + switch target
+```
+
+## Process control block (`include/gbos.inc`)
+
+`STATE, PID, SP, ROMB(16b text bank), RAMB(data bank), WRAMB(u-area), PARENT, EXIT`
+— 10 bytes. `MAX_PROCS = 8`.
+
+## Syscall ABI
+
+User loads `C` = syscall number, args in `DE`/`B`, then `rst $30`. Return in `A`.
+
+| # | name | status |
+|---|---------|--------|
+| 0 | exit | ✅ marks zombie, reschedules |
+| 3 | write | ✅ `DE`=buf `B`=len → serial console |
+| 8 | getpid | ✅ |
+| 11| yield | ✅ cooperative switch |
+| 1,2,4,5,6,7,9,10 | fork/read/open/close/exec/wait/kill/brk | ⛔ `ENOSYS` |
+
+## The context switch (`src/hram.asm`)
+
+The crux. `hSwitchTo(DE=&incomingPCB)`:
+1. push full register context onto the **outgoing** task's stack
+2. save `SP` into the outgoing PCB (WRAM0, always mapped)
+3. program incoming banks: **RAM bank → SVBK → ROM text bank**
+4. load `SP` from the incoming PCB (its banks are now mapped)
+5. pop context, `ret` into the incoming task
+
+Runs from HRAM so changing `SVBK`/RAM-bank never pulls the rug out from `PC` or
+the stack. New tasks are bootstrapped with a fake frame (`ProcSetupStack`) so the
+first switch-in `ret`s straight to their entry point.
+
+## Build
+
+```sh
+make # -> gbos.gb (RGBDS: rgbasm/rgblink/rgbfix)
+make clean
+```
+
+Console output goes to the **serial port** (`tty.asm`). Easiest way to watch it
+is the project emulator's headless serial console:
+
+```sh
+~/dev/gbc/build/gbc gbos.gb --headless --uncapped # prints ABABAB...
+```
+
+A real gbos would render to VRAM; serial is the zero-VRAM debug channel.
+
+### Bring-up bugs already found & fixed (kept as cautionary tales)
+
+- **Stack vs. BSS clear:** the kernel stack (`SP=$D000`) grows down into
+ `$CxFF`, so zeroing all of WRAM0 wiped the live return address. `ClearKernelRAM`
+ now clears only `$C000-$CBFF`, leaving the stack region alone.
+- **Syscall ABI vs. dispatch:** `SyscallTrap` originally used `DE` to index the
+ jump table, clobbering the `write()` buffer pointer passed in `DE`. Dispatch
+ now indexes via `A`/`HL` only, preserving `DE`/`B` for the handler.
+
+## Roadmap
+
+- [ ] `fork`: copy parent's 8 KiB RAM bank → free bank, dup u-area, child returns 0
+- [ ] `exec`: point `PROC_ROMB` at a flashed program, reset RAM-bank layout
+- [ ] Preemptive scheduling: real context save in `TimerISR` → `hSwitchTo`
+- [ ] Use the `$D000-$DFFF` u-area (SVBK) for per-process kernel state / kstack
+- [ ] `brk`/heap allocator inside the task bank (heap up, stack down, collision = ENOMEM)
+- [ ] A filesystem in remaining cart SRAM/flash (minix/v7-ish), paged 8 KiB at a time
+- [ ] Swap whole tasks to cart flash when RAM banks are exhausted (UZI-style)
+- [ ] VRAM console + keyboard/joypad `read()`
+```