| Commit message (Collapse) | Author | Files | Lines |
|
sys_write sets wBatch; render_cursor_tile - the single choke point all
of term_putc's visible mutations pass through - then marks a dirty
span (per LINE-SLOT, so spans survive scroll rotation for free)
instead of rendering. term_write_end renders every dirty tile exactly
once, then the cursor. Scrolls stay immediate (blank rebuild is cheap
and artifact-free); file-bound writes flush for free.
The enabler is in libc: puts() was a SYS_PUTC trap PER CHARACTER, so
almost no console output ever went through sys_write. It now issues
one SYS_WRITE for the whole string - one trap, one batch, and every
line-printer (echo, irc, the shell) gets the batched path.
Measured: 37-char write = 128k cycles (3.5k/char) vs ~8k/char down
the per-char trap path. Full demo passes; GIF regenerated.
|
|
KEY1 prepare + STOP at KernelInit (skipped on warm reboot if already
fast). The PPU/LCD keep their normal rate; the timer and DIV double
with the CPU, compensated at the two consumers:
- TimerISR now fires at 128 Hz; a toggle byte keeps wTicks at 64 Hz
(uptime semantics unchanged)
- sys_sleep halves the DIV-delta accumulator's high byte (256 DIV
ticks = 1/128 s in double speed) so gsleep/msleep stay real-time
Measured: count 60 (scroll-heavy) 3.77s -> 1.89s wall; wTicks 64 Hz
over 4s; ping's 800ms msleep pacing unchanged (2.77s for 4 echoes).
sl0pboy already emulated KEY1/STOP + per-speed timer/PPU rates.
|
|
The kernel had no clock: TimerISR was a reti stub, IEF_TIMER masked, and
IME was never enabled - the vectors were decorative. sys_sleep just polls
DIV deltas per-process; nothing counted globally.
Now: TAC runs the hardware timer at 16384 Hz with TMA=0, so TIMA overflows
at exactly 64 Hz; TimerISR increments a monotonic 32-bit wTicks (wraps
after ~2.1 years). Scheduling stays cooperative - the ISR is transparent.
Enabling IME in a kernel written for zero interrupts needs care wherever
SP points into memory whose bank is being switched (an IRQ pushes onto SP):
- read_block/write_block map the disk bank over the $A000 window that
holds the caller's stack -> di/ei around the transfer (~2ms, well under
the 15.6ms tick period, so no tick is ever lost)
- hSwitchTo switches SVBK + cart-RAM banks under the outgoing stack ->
di on entry, ei once the incoming stack is mapped
- fork already runs on KSTACK_TOP2 (fixed WRAM) - safe as-is
- term_putc's SVBK switch only remaps $Dxxx, stacks live in $Axxx/$Cxxx
SYS_UPTIME (36) copies the counter (4B LE, di/ei so the read can't tear)
to a user buffer; libc gticks(); usr/uptime.c formats 'up [Nd] H:MM:SS'.
uptime avoids SDCC long div/shift entirely: sm83.lib modules link into
their own areas that land in the $A000 RAM window (latent build.sh trap,
documented there) - bytewise >>6 plus bounded subtraction loops instead.
|
|
Print everything the kernel knows implicitly at boot, Linux-flavored, on
the LCD console and mirrored over the link (gbhub logs each GB's boot):
gbos sm83 microkernel
console: CGB (boot a=$11) <- boot ROM's A/B, saved at entry
cart: mbc5, 1M rom, 128K sram <- our own cart header ($0147-49)
mem: 32K wram 16K vram 127B hram <- CGB constants
proc: 8 slots, 31 programs <- link-time table sizes
net: slip on link port, 4 sockets
fs: gbfs v3 mounted, 120/128 blk free <- bitmap popcount; 'formatted'
tty: 40x18 console, SELECT = osk on first boot
init: spawning pid 1
New src/dmesg.asm with kernel print helpers (kputc/kputs/kputhex/kputdec)
that bypass KPutc (no process context at boot). term_init now runs before
net/fs init so the spew is visible as subsystems come up.
|
|
A Game Boy sitting at the shell prompt now services the network instead of
being deaf until you run netd: KGetc (the console input wait) pumps net_pump
each poll, so inbound pings are auto-answered while idle. net_pump gained an
in-frame model and routes any non-framed link bytes to a small console-input
ring (con_push/con_pop), so a headless-injected command still reaches the shell
while SLIP frames go to the stack. Removes the old SLIP-skip-in-KGetc hack.
Verified: two Game Boys on the hub, GB0 idle at the prompt (no netd), GB1
`ping 10.0.0.2` -> 4/4 replies, routed GB1->hub->GB0->hub->GB1.
(Separate, pre-existing: gbhub *spawn* mode and windowed gbjoin can drop an idle
link socket - under investigation; daemon mode + headless is solid.)
|
|
There was no time source at all (IRQ vectors just reti; scheduler is purely
cooperative). The DIV register (FF04) free-runs at 16384 Hz regardless of
interrupts, so sys_sleep accumulates DIV deltas across SchedYields (other procs
keep running) until the requested number of 1/64-second units elapse.
- SYS_SLEEP(34): B = 1/64s units; libc gsleep(units) / msleep(ms) wrappers.
- ping now msleep(800) between echoes, so it paces like real ping instead of
blasting all four at once.
Verified real-time (capped emulator): replies land ~0.85s apart. In --uncapped
runs the delay is GB-time (fast wall-clock), as expected.
|
|
The link port is both the console-input fallback and the network. At the shell
prompt the host's multicast (mDNS/LLMNR/IGMP) was fed to the serial and KGetc
read those packet bytes as console input -> garbage in the shell, OSK unusable.
Two-part fix:
- KGetc now skips SLIP frames (0xC0-delimited) on the serial console path, so
inbound packets never surface as console input. Plain injected bytes (the
headless tunbridge command path) still pass through. New wConInSkip flag.
- netboot/tunbridge only forward IP packets destined to 10.0.0.2 (drop the
multicast noise at the bridge).
Verified: GB sits at a clean "/#" prompt while all packets (noise included) are
forwarded; being-pinged (3/3) and wget still work. On the emulator, the OSK is
SELECT=space, START=enter, A=z, B=x, d-pad=WASD/arrows.
|
|
- directories generalized: dir_find/dir_add/dir_remove take any dir inode;
every dir carries '.'/'..'. Path resolution (resolve / resolve_parent) walks
absolute or cwd-relative paths; open/mkdir/chdir/remove/opendir take paths.
- per-process cwd: PROC_CWD in the PCB (root by default, inherited on fork);
sys_chdir sets it, sys_opendir points ls at any directory.
- shell: 'cd' builtin + cwd-aware prompt; 'mkdir'/'ls <dir>' tools; 'exit'/'quit'
and EOF call poweroff() (clean shutdown via the $ED opcode -> reliable save).
- three HL/buffer-clobber bugs fixed along the way: resolve didn't preserve the
path cursor across dir_find; cur_cwd clobbered HL; resolve_parent set the final
name before resolve() overwrote wFsNameBuf (now copied after).
- verified: nested mkdir/cd/save, multi-level paths (cat docs/sub/b), and the
whole tree persists across a reboot.
- README: document directories + the poweroff opcode.
|
|
- fs.asm: a real block filesystem on the persistent block device - superblock,
block/inode bitmaps, a 32-entry inode table (type, size, 8 direct block
pointers -> files up to 2 KiB), and a root directory of 16-byte entries.
Metadata cached in WRAMX; one-block data cache streams file/dir blocks.
- same syscall interface (open/getb/putb/list/remove) -> tools unchanged;
ls now enumerates until flist() runs out (16 files, was hardcoded 8).
- old 8-slot WRAM FS removed; cart RAM partitioned: process banks 0-7, disk 8-11.
- two register-clobber bugs fixed: alloc_block/alloc_inode returned the
bitmap-block number (write_bitmap clobbers C); db_use loaded the wrong block
on a transition (db_flush->write_block clobbers C) -> multi-block files broke.
- verified: 10+ files, multi-block (300-byte) files, delete, and persistence
across reboots. Debug 'blk' disk tool retained (fill/peek).
- README: document the block filesystem.
|
|
- PCB gains PROC_PROG (running program id), set by exec, inherited on fork.
- sys_kill(pid): reject pid 1 (immortal) and unknown/dead pids; mark the target
zombie, reparent its children to init, wake its parent if blocked in wait;
schedule away if a process kills itself. Reuses FindPcbByPid/ReparentToInit.
- sys_ps(slot,buf)->{pid,state,prog}; sys_progname(id) via NameTable reverse
lookup. NameTable gains sh/ps/kill.
- libc: kill/psget/progname; tools ps (pid state cmd) and kill <pid>.
- verified: ps lists sh(B)+ps(R); kill 1 refused; kill 42 fails.
|
|
- kernel I/O routing: PCB gains PROC_STDIN/PROC_STDOUT (default console $FF),
inherited across fork. KGetc/KPutc route read/write/putc to the console or a
file fd. New syscalls: putc(16), setin(17), setout(18), lookup(19).
read/write now go through the routing; putc/puts/nl use SYS_PUTC.
- sys_lookup + NameTable move command-name resolution into the kernel.
- shell rewritten in C (c/sh.c): tokenizes a line, parses > / < / |, and drives
fork+setin/setout+exec+wait. Pipes run as 'a > __pipe ; b < __pipe' (temp
file). asm shell + cmdtab removed; StrEqual/SkipName kept for sys_lookup.
- libc: fork/exec/wait/setin/setout/lookup wrappers.
- verified: echo>file, cat file, wc<file, cat readme|wc -l, echo ...|wc.
|
|
- fs.asm: 8-slot RAM filesystem in WRAMX ($D000-$DFFF), name[8]+len[2]+data[502];
syscalls open/close/getb/putb/list/remove, seeded with a 'readme' at boot.
- libc: open/close/fgetc/fputc/flist/fremove wrappers + O_READ/O_WRITE/NOFD.
- tools: ls, save (stdin->file, one line), rm; cat/wc/head now take a file arg.
- fix: syscall dispatch clobbers A, so putb takes its byte in E (was reading the
handler's low address byte, 0xDC); ls NUL-terminates 8-char names.
- verified: save/cat/ls/rm cycle; wc readme -> '2 7 38'; head -n 1 readme.
- README: document the filesystem + the A-clobber ABI note.
|
|
- exit(): zombie + status, reparent orphans to init, wake blocked parent,
schedule away forever (resources reclaimed by the reaper)
- wait(): scan for a zombie child; reap (free cart-RAM bank + PCB slot) and
return pid/code; block PS_BLOCKED + yield if children still live; $FF if none
- cart-RAM banks are now a free list (wBankUsed bitmap): AllocRamBank/FreeRamBank
- scheduler: FindNextReady returns carry when nothing runnable; yield idles
instead of blindly picking slot 0
- helpers: FindPcbByPid, ReparentToInit
- demo: init forks 3 workers -> exec -> exit(pid) -> wait/reap => wwwR2R3R4!
verified bank recycling with 6 workers over 3 banks (w2w3w4w5w6w7)
- README: document the lifecycle + syscall status
|
|
- sys_fork: switch to a kernel stack, bump-allocate a cart-RAM bank, copy the
parent's 8 KiB bank via a WRAM bounce buffer, plant a switch-in frame so the
child returns 0 to the post-fork PC, fill the child PCB (shared ROM text)
- add AllocRamBank (bump allocator) + FindFreeSlot helpers
- demo: init forks; parent prints P, child prints C (PCPC...); nested fork
gives three distinct pids (123...)
- README: document the fork mechanism, mark syscall status
|