| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
gbfs has carried I_NLINK since the beginning and nothing ever incremented it,
so a file could only have one name. SYS_LINK adds a second directory entry
pointing at the same inode: the data is stored once, and both names read it.
That makes rm's job different. It now decrements the link count and only frees
the inode and its blocks when the LAST name goes - removing one of two links
used to free blocks the other still pointed at.
Refused, with the reasons that matter here: linking a directory (it would make
a cycle the tree walkers cannot survive), an existing name, and anything under
/bin, which is ROM.
The syscall takes a request block because the trap needs HL for its dispatch
table - and ln copies both paths to the stack first, since that block is a
static living at $A000, on top of the command line it is reading.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The commands only existed in the kernel's program table, so the only way to
find out what the machine could do was to already know (or read the source).
They are now a directory in the tree:
/# ls / /# xxd -l 16 /bin/uname
bin 0000: cd 6b 43 06 00 0e 00 f7 .kC.....
readme 0008: 18 fe 47 0e 03 f7 c9 21 ..G....!
/# ls -l /bin | head -2
- 1 16384 worker
/bin is synthetic, not disk: dir_find resolves "bin" in the root to BIN_INO
and names inside it through the NameTable, so `ls /bin` lists exactly what the
shell can run - one table, one truth, no second list to keep in sync. The root
listing carries "bin" as a synthetic first entry so it is discoverable by
walking the tree.
A program image is inode BIN_FILE|progid: it stats as a 16 KiB file (a program
IS its ROM bank) and reads through rom_getb, which maps that bank, takes the
byte, and maps the CALLER'S text bank back before returning - the caller is
itself executing from $4000, so leaving the wrong bank mapped would return
into another program's code. Verified byte-for-byte against the host's view of
the blob. The tree is read-only: create/remove/write inside /bin all fail.
xxd grows -l (stop after N bytes), because a program image is 2048 dump lines
- more than a pipe's temp file can hold, let alone the screen.
Two traps worth remembering, both found the hard way here: `and BIN_FILE`
destroys A, so the inode must be reloaded before inode_ptr (missing it once
made ls -l stat inode 0 and call every file an empty non-directory); and ls
now copies its directory argument to the stack first, because stat()'s request
block is a static living at $A000 - on top of the command line it was reading.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Four things userland had no way to ask for:
- SYS_STAT: type, link count, size and inode for a path. The trap needs HL
for its dispatch table, so a call taking both a path and an output buffer
passes a request block - the shape SYS_NET already established. usr/stat.h
wraps it, header-only like sock.h, because the block must live in the
caller's RAM.
- SYS_FSSTAT: blocks/inodes, total and free. The bitmaps are already cached
write-through in fs_bmbuf, so this is the popcount boot_fs already prints.
- SYS_ISATTY: is this process's stdin the console? The shell needs it to tell
an interactive session from a script.
- O_APPEND: open a file positioned at EOF. There is no seek syscall, so this
is the only way to add to a file - without it nothing running ON the Game
Boy can build a multi-line file, shell scripts included.
Together these back ls -l, df, and `>>`.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
sys_getb/sys_putb indexed inode.blocks[pos/256] with no limit at all, so a
file that grew past its pointers just kept walking: first through the 4
unused bytes at the tail of the 16-byte inode, then straight into the NEXT
inode, reading its type/nlink/size as block numbers.
It hides well. Reads and writes alias identically, so a big file can be
written and read back byte-for-byte and look fine - until something else
touches the neighbouring inode, after which the tail of the file is garbage
from whatever block those bytes now name. Found by serving a 7 KB file over
httpd: corruption began at exactly offset 3072, and only sometimes.
- NDIRECT 8 -> 12: bytes 4..15 are all block pointers now, which is what the
runaway indexing was already doing by accident. Files go to 3 KiB, no
on-disk layout change, no format bump.
- getb past the last direct block reports EOF; putb drops the byte like a
full disk. A capped file beats a corrupted neighbour.
count 250 > big (7142 bytes) now stops at 3072 and its neighbours survive.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the shell's temp-file pipe hack with proper in-kernel FIFOs.
Kernel (src/pipe.asm, new):
- A small pool of bounded ring buffers (PIPE_MAX=2, 64 B each) with
writers/readers refcounts. Pipe fds are $F0+idx*2 (read) / +1 (write);
$FF stays "console".
- SYS_PIPE allocates one (writers=readers=1) and returns the read fd
(write = read+1). getb/putb/close dispatch pipe fds here; sys_exit drops
the refcounts held as PROC_STDIN/PROC_STDOUT.
- Blocking with SchedYield, which is the flow control: read blocks while
empty with a writer (EOF once writers hit 0), write blocks while full
with a reader, and if the last reader is gone the writer is killed
(SIGPIPE -> exit 141). Cooperative-scheduler friendly.
Shell (c/sh.c):
- run_pipeline(): split on '|', make a pipe between adjacent stages, and
fork ALL stages concurrently (no wait between), wiring stdin/stdout;
then wait for all. Per-stage >/< still honored; orphaned pipe ends are
closed on a lookup miss so EOF/EPIPE propagate.
- Drop the __pipe temp file and its 2 KB / serialized limits.
libc: pipe(). New c/ptest.c exercises the FIFO (write, read back, EOF).
Now works (old version couldn't): multi-stage a|b|c; streaming beyond 2 KB
(count 120 | wc = 3372 bytes through a 64 B buffer); early-exit SIGPIPE
(count 200 | true kills count instead of hanging/overflowing a file).
|
| |
|
|
|
|
|
|
| |
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.)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- 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.
|
|
|
- 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.
|