aboutsummaryrefslogtreecommitdiffstats
path: root/src (follow)
Commit message (Collapse)AuthorAgeFilesLines
* osk: toggle-able on-screen keyboard on the window layeruser5 days5-7/+411
| | | | | | | | | | | | | | | | | | | | | | | | | | A joypad-driven keyboard that costs zero permanent screen space: it lives on the GB window layer, so SELECT just flips LCDC bit 5 (window enable) and the terminal's background layer underneath is never disturbed. - src/joypad.asm: read $FF00 with edge detection (wPadCur/Prev/New). - src/osk.asm: 3x20 key grid (letters, digits, space, punctuation, plus Enter/Backspace) built once into spare bank-1 VRAM tiles (104+) and laid out on the $9C00 window map, docked to the bottom 3 rows. The highlighted key is a palette swap on its window attribute byte (CGB BG palette 1 = inverted), so moving the cursor is 1-2 attribute writes with no tile rebuilding. SELECT toggles, d-pad moves, A types. - KGetc's console poll loop now polls the joypad and osk_handle each iteration; a key press returns its byte to the reader exactly like a serial byte, so the shell is oblivious to the input source. Verified in the emulator (via --keys): SELECT shows the keyboard, the highlight tracks the d-pad, and typing "ls"+Enter runs the command and lists the files; a second SELECT hides it and reclaims the full 18 rows. Known limitation: the OSK overlays the bottom 3 terminal rows, so if the prompt has scrolled to the very bottom the current input line can be hidden. A follow-up can add a scroll region (terminal uses rows 0-14 while the OSK is up). Input over serial still works unchanged.
* syscall: mirror console output to the LCD terminaluser5 days3-2/+18
| | | | | | | | | | | | | | | | | | KPutc's console path now renders each byte on the LCD terminal via term_putc in addition to the serial write. Serial is kept so scripted tests still capture output (and for link-cable/debug), while the shell, command output, and echoed input now appear on-screen in the 40-col font with a live cursor and scrolling. Dropped the boot-time term_demo; the terminal starts blank and fills from real console traffic. term_putc now saves/forces/restores SVBK=1 so it always reads and writes the terminal buffer/state in WRAM bank 1, regardless of which process context KPutc is invoked from (defensive; all procs currently use bank 1). The stack lives in non-banked WRAM, so saving SVBK across the switch is safe. Verified: a headless run of `uname; ls; echo hello world` renders each prompt, the echoed command, and its output on the LCD (captured via the new `--headless --shot` path), and serial capture is unchanged.
* term: cursor + O(1) line scrollinguser5 days2-209/+455
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Rework the terminal from a static one-tile-per-screen-position model to LINE-BOUND tiles with an assign[] indirection, so scrolling is cheap: - Each line-slot L owns the 20 VRAM tiles at positions [L*20..L*20+19]. - assign[screen_row] -> line-slot; the tilemap points each screen row at its slot's tiles. - Scroll = rotate assign[], blank+rebuild only the new bottom line (20 tiles), and rewrite the tilemap. O(1 line) instead of rebuilding all 360 tiles. - term_putc: printable + \n \r \b, line wrap at col 40, cursor advance with scroll-on-overflow. - Cursor: an underline OR'd into the current cell's tile via wCurMask (no sprites/OAM); moving it just re-renders the old and new tiles. - term_demo drives 24 lines through term_putc to exercise scroll+cursor. Also fixes a vicious bug this shook out: build_tile advanced its 16-bit glyph pointers with `ld hl, wGlyphL+1 / inc [hl]`, which CLOBBERS HL -- and HL is the live VRAM destination pointer. Whenever a glyph's 8 bytes straddled a page boundary (common with long/varied lines) the next tile bytes were written into WRAM at $D580+, corrupting wGlyphL/wVram/wCurRow/ wCurCol and freezing the display. Now the pointers are bumped via A so HL is preserved. (Note: the emulator build had also been silently failing on an unrelated debug edit, masking this for a while.) Verified by screenshot: 24 lines scroll to show the last 18 + a visible cursor at the "ready$" prompt; serial shell and filesystem still work.
* font: full mixed-case + punctuation glyph set (not uppercase-only)user5 days2-102/+102
| | | | | | | | | | | | | | Replace the small-caps placeholder font with a real baseline-aligned 4x8 set covering all 96 printable ASCII: distinct lowercase with true ascenders (b d f h k l t) and descenders (g j p q y), digits, and punctuation. Metrics: caps/ascenders rows 1-5, x-height rows 2-5, baseline row 5, descenders into rows 6-7. Glyphs are ~3px wide in the 4px cell -- cramped but legible; the tile mechanism itself imposes no character-set limit. genfont.py now takes per-glyph (top, rows) so glyphs sit on the baseline. Demo strings in term_test updated to show mixed case + punctuation. Verified by screenshot: descenders visibly drop below the baseline.
* term: 40-column text terminal on the CGB LCD (4x8 dynamic tiles)user5 days3-0/+482
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Add a background-tile text terminal that packs TWO 4px-wide characters into each 8x8 tile, giving a 40x18 grid instead of the 20x18 you'd get from an 8x8 font. The tilemap is static (one dedicated VRAM tile per screen position); we rebuild a tile's 16 bytes from two glyphs whenever a character changes. 360 tiles exceed the 256 a single tilemap can address, so positions 256-359 live in VRAM bank 1 via the CGB tilemap attribute bank-bit -- hence CGB-only. - Switch the ROM to CGB (rgbfix -C). Safe for the FS: every process has PROC_WRAMB=1, so SVBK stays on bank 1 and the WRAMX FS caches don't move. - CGB BG palette 0 = white bg / black text via BCPS/BCPD. - src/term.asm: term_init (palette + static tilemap + clear buffer), build_tile (combine two 4px glyphs -> one 8x8 tile, correct VRAM bank), term_redraw (rebuild all tiles), term_puts, term_show, term_test. - src/font.asm: 96-glyph 3x5-in-4x8 font, generated by tools/genfont.py. - 40x18 text buffer at $D600 (WRAMX, above the FS caches). - tools/ppmview.py: render a --shot PPM as ASCII so the LCD is inspectable from the shell during development. Verified via emulator screenshot: "GBOS TERMINAL", the alphabet, and a bank-1 row all render legibly at 40 columns. Serial shell + filesystem still work unchanged. Note: a full term_redraw builds 360 tiles and takes ~8 frames; fine for incremental single-char updates, but scrolling needs a smarter path (next milestone). Input (no keyboard) is also still TODO.
* fs: reject open()/read() on a directory (EISDIR)user6 days1-0/+10
| | | | | | | | open() now checks the target's inode type: opening a directory as a file returns $FE (EISDIR) instead of a valid fd, so 'cat docs' no longer streams the raw directory block as bytes. cat reports 'is a directory'; wc/head/save treat any invalid fd (>3) as an open failure. (ls is unaffected - it uses the structural opendir/list path, not open/getb.)
* fs rewrite stage 3: nested directories, paths, cwd (cd/mkdir/ls)user6 days5-36/+360
| | | | | | | | | | | | | | | | - 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 rewrite stage 2: inodes + root directory (block-based, persistent)user6 days4-260/+616
| | | | | | | | | | | | | | | | - 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.
* fs rewrite stage 1: persistent block device (bounce buffer, cart SRAM)user6 days4-1/+178
| | | | | | | | | | | | | | - blk.asm: read_block/write_block move 256-byte blocks between battery-backed cart-RAM banks (8-11, the 'disk') and a WRAM bounce buffer. Banking is hidden in those two routines; they inline the 'restore my bank' step so they never touch the stack while the disk bank is mapped over the process's $A000 window (the stack lives there too -- a call/ret would rug-pull it). - format-on-first-boot: superblock magic in block 0; otherwise the disk persists. - cart RAM bumped to 128 KiB (-r 4); process banks 0-7, disk banks 8-11. - debug tool 'blk fill/peek' + syscalls to validate round-trip and persistence. - verified: round-trip incl. cross-bank (block 100 -> bank 11); block survives a reboot via .sav; magic intact (no reformat on 2nd boot). - old WRAM 8-slot FS still backs the tools until stages 2-3.
* jobs: background execution (&), non-blocking reap, spin demouser6 days2-0/+65
| | | | | | | | | | | - sys_reap: nohang reap of a finished zombie child (-> pid or 0). - shell: 'cmd &' launches in the background (prints [pid], no wait); reaps finished jobs at the prompt ([pid done]). Foreground now waits for its own child specifically (loops wait(), reporting bg completions meanwhile) so kill reports the right pid. - spin: a process that yields forever - a target for ps/kill. - verified: spin &; ps shows it; kill <pid> removes just that one; immortal init; self-finishing bg worker reaped with [pid done].
* kill + ps: implement SYS_KILL (pid 1 immortal) and a ps tooluser6 days4-3/+202
| | | | | | | | | | | - 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.
* shell: I/O redirection (>/<) and pipes (|); rewrite shell in Cuser6 days4-154/+193
| | | | | | | | | | | | | - 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.
* filesystem: a RAM FS (WRAMX) + ls/cat/save/rm; wc/head read filesuser6 days5-2/+486
| | | | | | | | | | | - 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.
* tools: wc, head, and an argc/argv demo (args) + libc.c helpersuser6 days1-0/+21
| | | | | | | | | | - c/libc.c: shared C helpers linked into every program - putu (print decimal), atou (parse decimal), argv_parse (tokenize getargs() into argc/argv). - wc: count lines/words/chars of stdin. head [n]: first n lines (drains rest to EOF). args: argc/argv demo. - pid now uses libc putu; build.sh links libc.c; Makefile CBLOBS + libc dep. - README: document the new tools + argv_parse. - verified: 'args one two three' -> argc=3/argv[..]; wc '2 3 16'; head 2.
* libc + CLI tools: a small C userlanduser6 days1-3/+68
| | | | | | | | | | | - libc: add putc, puts, strlen, getargs (args string), EOF (=4) for readc. - args: shell splits the command line at the first space ("cmd\0args\0" at $A000, inherited by the child via fork); getargs() returns the arg string. - shell: exit on Ctrl-D/EOF; $ prompt over a clean read loop. - tools (c/): echo (argv), cat (stdin->stdout to EOF), uname, pid (decimal print), true, false. Each built to a ROM-bank blob and registered. - Makefile: CBLOBS list builds all C programs. - README: document libc + the tool set.
* C toolchain: run SDCC-compiled C programs as gbos processesuser6 days1-0/+13
| | | | | | | | | | | | | - c/{gbos.h,crt0.s,libc.s,build.sh}: SDCC sm83 -> ROM-bank blob toolchain. libc syscall wrappers save/restore BC/DE/HL around rst $30 (trap clobbers them; SDCC expects them preserved). - build via SDCC native asxxxx path (sdasgb/sdldgb), crt0 linked first so _start is the $4000 entry; blob INCBIN'd into a ROM bank. - chello.c: prints a message + getpid() -> runs as 'chello' shell command. - Makefile: auto-build c/*.bin, track as a dep of programs.o; gitignore blobs. - README: document the C toolchain + the SDCC --asm=rgbds codegen bug that forced the native-toolchain approach. - verified: '$ chello' -> 'hello from C on gbos!' / 'my pid is 2'.
* shell working: fix syscall ABI (trap clobbers HL) + documentuser6 days1-1/+5
| | | | | | | | | | | - root cause of the read/echo corruption: SyscallTrap uses HL to index the dispatch table, so HL is clobbered across a syscall; the shell then wrote its buffer pointer's garbage into the $0000-$7FFF MBC register region, corrupting the mapper. fix: userland saves HL around sys_read (push/pop). - shell now runs commands end to end: $ hello -> hello world!, $ worker -> worker!, $ foo -> ? - README: document that syscalls clobber BC/DE/HL (return in A, B for wait); add read + shell to the syscall table.
* wip: sys_read + serial console shell (read-store bug under investigation)user6 days3-72/+177
| | | | | | | | | | | | - sys_read (SYS_READ): blocking console input via external-clock serial poll; yields while waiting. Verified working (direct-echo cat: 'hi' -> 'hi'). - shell (PROG_SH): prompt/read/parse/fork+exec+wait; worker+hello programs; StrEqual/SkipName helpers; command table. - init now execs the shell. - KNOWN BUG: storing the typed char to the line buffer at $A000 (cart RAM of an exec'd program) corrupts the following sys_read; sys_write reading $A000 also returns $3E. Kernel syscalls themselves are correct; isolating the cart-RAM / exec RAM-bank interaction next.
* kernel: complete the process lifecycle (exit/wait/reap + bank free list)user6 days6-76/+325
| | | | | | | | | | | | | | - 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
* kernel: implement exec()user6 days4-25/+118
| | | | | | | | | | - sys_exec(B=program id): look up ProgramTable, point PROC_ROMB at the program's ROM text bank, map it into $4000-$7FFF, reset stack, jp to entry (no return) - programs.asm: two demo programs (ping/pong) ORG'd at the SAME $4000 in banks 2 and 3 - proves execution is driven purely by PROC_ROMB - demo: init forks; parent execs PROG_PING (bank2, '1'), child execs PROG_PONG (bank3, '2') -> '1212...', 2000/2000 balanced, zero garbage - README: document exec + text-in-ROM model
* kernel: implement fork()user6 days5-27/+249
| | | | | | | | | | - 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
* gbos scaffold: working cooperative microkernel (verified ABAB in emulator)user6 days8-0/+653
- fix ClearKernelRAM clobbering the kernel stack (only clear $C000-$CBFF) - fix SyscallTrap clobbering DE (buffer arg) during table dispatch - README: run via ~/dev/gbc --headless; note bring-up bugs