diff options
| author | user <user@clank> | 2026-07-16 07:53:19 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-16 07:53:19 +0200 |
| commit | 7b5c532625a864a3fbaf76f44df6aa99b0d6861e (patch) | |
| tree | 6a6d67644993ccaea4b71bdc0881798887a6d1aa | |
| parent | libc: option parsing (hasflag/optval); wc -l/-w/-c, head -n N (diff) | |
| download | gbos-7b5c532625a864a3fbaf76f44df6aa99b0d6861e.tar.gz gbos-7b5c532625a864a3fbaf76f44df6aa99b0d6861e.tar.xz gbos-7b5c532625a864a3fbaf76f44df6aa99b0d6861e.zip | |
filesystem: a RAM FS (WRAMX) + ls/cat/save/rm; wc/head read files
- 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.
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | README.md | 45 | ||||
| -rw-r--r-- | c/cat.c | 18 | ||||
| -rw-r--r-- | c/gbos.h | 11 | ||||
| -rw-r--r-- | c/head.c | 27 | ||||
| -rw-r--r-- | c/libc.s | 48 | ||||
| -rw-r--r-- | c/ls.c | 9 | ||||
| -rw-r--r-- | c/rm.c | 7 | ||||
| -rw-r--r-- | c/save.c | 19 | ||||
| -rw-r--r-- | c/wc.c | 21 | ||||
| -rw-r--r-- | include/gbos.inc | 30 | ||||
| -rw-r--r-- | src/boot.asm | 1 | ||||
| -rw-r--r-- | src/fs.asm | 451 | ||||
| -rw-r--r-- | src/kdata.asm | 7 | ||||
| -rw-r--r-- | src/programs.asm | 21 | ||||
| -rw-r--r-- | src/syscall.asm | 8 |
16 files changed, 697 insertions, 29 deletions
@@ -19,7 +19,8 @@ $(BUILD)/%.o: src/%.asm | $(BUILD) # C programs: compiled with SDCC into ROM-bank blobs and INCBIN'd by programs.asm CBLOBS := c/chello.bin c/echo.bin c/true.bin c/false.bin c/uname.bin \ - c/pid.bin c/cat.bin c/wc.bin c/head.bin c/args.bin + c/pid.bin c/cat.bin c/wc.bin c/head.bin c/args.bin \ + c/ls.bin c/save.bin c/rm.bin $(BUILD)/programs.o: $(CBLOBS) # C programs also depend on the shared libc sources @@ -214,15 +214,20 @@ unsigned char argc = argv_parse(argv, 8); /* args one two -> argc=3 */ | tool | what it does | |------|--------------| | `echo` | print its arguments (`echo hello world`) | -| `cat` | copy stdin to stdout until EOF (Ctrl-D / pipe end) | -| `wc` | count lines / words / chars of stdin | -| `head` | print the first *n* lines of stdin (`head 5`, default 10) | +| `cat` | print a file (`cat readme`) or copy stdin to stdout | +| `wc` | count lines/words/chars of a file or stdin (`wc -l readme`) | +| `head` | first *n* lines of a file or stdin (`head -n 5 readme`) | +| `ls` | list files | +| `save` | read a line from stdin into a file (`save notes`) | +| `rm` | delete a file (`rm notes`) | | `args` | argc/argv demo (`args one two three`) | | `uname`| print the system name | | `pid` | print the process's pid (decimal) | | `true` / `false` | exit 0 / 1 | | `chello` | the C "hello" demo | +Options use `hasflag`/`optval` in libc: `wc -l`/`-w`/`-c`, `head -n N`. + ``` $ echo C tools on a Game Boy C tools on a Game Boy @@ -247,6 +252,35 @@ so we use SDCC's native asxxxx path and INCBIN the blob instead. String literals with embedded control chars are also mangled in rgbds mode; C programs pass explicit lengths and emit newlines via `nl()`. +## Filesystem + +A tiny **RAM filesystem** lives in **WRAMX (`$D000-$DFFF`, 4 KiB)** — always +mapped in DMG mode and simultaneously accessible with a process's `$A000` data +bank, so the kernel copies between file and process memory with no bounce +buffer. 8 fixed slots of 512 bytes: `name[8]` + `len[2]` + `data[502]`. Seeded +with a `readme` at boot. + +Syscalls: `open` (4), `close` (5), `getb` (12), `putb` (13, byte in `E`), +`list` (14), `remove` (15). Libc: `open`/`close`/`fgetc`/`fputc`/`flist`/ +`fremove` (`c/libc.s`). + +``` +$ save notes +gbos has a filesystem now +$ cat notes +gbos has a filesystem now +$ wc notes +1 5 26 +$ ls +readme +notes +$ rm notes +``` + +> **ABI note:** the syscall trap indexes its jump table with `A`, so syscall +> args go in `B`/`DE`/`E` (never `A`) and results come back in `A`. That's why +> `putb` takes its byte in `E`. + ## Roadmap - [x] `fork`: copy parent's 8 KiB RAM bank → free bank, child returns 0 @@ -259,7 +293,10 @@ explicit lengths and emit newlines via `nl()`. - [x] CLI tools in C: `echo`, `cat`, `uname`, `pid`, `true`, `false` - [x] Makefile builds all C programs (a `CBLOBS` list) - [x] `wc`, `head`, and an `argc/argv` demo (`args`) + `argv_parse` in libc -- [ ] more tools (`rev`, `grep`), option parsing (`-n`), a RAM filesystem +- [x] option parsing (`hasflag`/`optval`): `wc -l/-w/-c`, `head -n N` +- [x] a RAM filesystem (WRAMX) + `ls`/`cat`/`save`/`rm`; `wc`/`head` read files +- [ ] more tools (`rev`, `grep`, `tail`), shell redirection (`>`/`|`) +- [ ] battery-backed files (move the FS to cart SRAM), directories - [ ] 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) @@ -1,10 +1,16 @@ #include "gbos.h" -/* cat: copy stdin to stdout until EOF (Ctrl-D / pipe end). */ +/* cat [file]: print a file, or copy stdin to stdout until EOF. */ void main(void) { - char c; - for (;;) { - c = readc(); - if (c == EOF) break; - putc(c); + char *argv[4]; + unsigned char argc = argv_parse(argv, 4); + if (argc > 0) { + unsigned char fd = open(argv[0], O_READ); + int ch; + if (fd == NOFD) { puts("cat: no such file"); nl(); return; } + while ((ch = fgetc(fd)) >= 0) putc((char)ch); + close(fd); + } else { + char c; + for (;;) { c = readc(); if (c == EOF) break; putc(c); } } } @@ -19,4 +19,15 @@ unsigned char atou(const char *s); /* parse decimal */ unsigned char argv_parse(char **argv, unsigned char maxv); /* argc/argv */ unsigned char hasflag(char **argv, unsigned char argc, char f); /* -x flag */ char *optval(char **argv, unsigned char argc, char opt); /* -x VALUE */ + +/* filesystem (c/libc.s -> kernel syscalls) */ +unsigned char open(const char *name, unsigned char mode); /* 0=read 1=write */ +void close(unsigned char fd); +int fgetc(unsigned char fd); /* a byte, or -1 at EOF */ +void fputc(unsigned char fd, char ch); +unsigned char flist(unsigned char slot, char *namebuf); /* 1 if slot used */ +void fremove(const char *name); +#define O_READ 0 +#define O_WRITE 1 +#define NOFD 0xFF #endif @@ -1,17 +1,28 @@ #include "gbos.h" -/* head [-n N] [N]: print the first N lines of stdin (default 10). */ +/* head [-n N] [N] [file]: first N lines of a file or stdin (default 10). */ void main(void) { char *argv[8]; unsigned char argc = argv_parse(argv, 8); char *nv = optval(argv, argc, 'n'); - unsigned char n = 10, lines = 0; - char c; + unsigned char n = 10, lines = 0, i, fd = NOFD; + char *fname = 0; + int ch; if (nv) n = atou(nv); - else if (argc > 0 && argv[0][0] >= '0' && argv[0][0] <= '9') n = atou(argv[0]); + for (i = 0; i < argc; i++) { + if (argv[i][0] == '-') continue; + if (nv == argv[i]) continue; + if (!nv && argv[i][0] >= '0' && argv[i][0] <= '9') { n = atou(argv[i]); continue; } + fname = argv[i]; break; + } + if (fname) { + fd = open(fname, O_READ); + if (fd == NOFD) { puts("head: no such file"); nl(); return; } + } for (;;) { - c = readc(); - if (c == EOF) break; - if (lines < n) putc(c); - if (c == '\n') lines++; + if (fd == NOFD) { char c = readc(); if (c == EOF) break; ch = c; } + else { ch = fgetc(fd); if (ch < 0) break; } + if (lines < n) putc((char)ch); + if (ch == '\n') lines++; } + if (fd != NOFD) close(fd); } @@ -1,5 +1,6 @@ .module libc .globl _writes, _putc, _puts, _nl, _strlen, _readc, _sexit, _getpid, _getargs + .globl _open, _close, _fgetc, _fputc, _flist, _fremove ;; SDCC sm83 sdcccall(1): 1st arg -> A (byte) or DE (pointer); ret A / BC. ;; rst $30 (the trap) clobbers BC/DE/HL; SDCC treats them caller-saved, so ;; wrappers need not preserve them - but we compute cleanly regardless. @@ -84,3 +85,50 @@ _getpid: ld c, #8 ; SYS_GETPID rst #0x30 ret + + ;; unsigned char open(const char *name /*DE*/, unsigned char mode /*A*/) -> A(fd) +_open: + ld b, a ; B = mode + ld c, #4 ; SYS_OPEN + rst #0x30 + ret + + ;; void close(unsigned char fd /*A*/) +_close: + ld b, a + ld c, #5 ; SYS_CLOSE + rst #0x30 + ret + + ;; int fgetc(unsigned char fd /*A*/) -> BC (byte, or -1 at EOF) +_fgetc: + ld b, a + ld c, #12 ; SYS_GETB + rst #0x30 + jr c, 1$ + ld c, a + ld b, #0 + ret +1$: ld bc, #0xffff + ret + + ;; void fputc(unsigned char fd /*A*/, char ch /*E*/) + ;; byte goes to the kernel in E (the trap clobbers A during dispatch) +_fputc: + ld b, a ; B = fd (ch stays in E) + ld c, #13 ; SYS_PUTB + rst #0x30 + ret + + ;; unsigned char flist(unsigned char slot /*A*/, char *namebuf /*DE*/) -> A +_flist: + ld b, a + ld c, #14 ; SYS_LIST + rst #0x30 + ret + + ;; void fremove(const char *name /*DE*/) +_fremove: + ld c, #15 ; SYS_REMOVE + rst #0x30 + ret @@ -0,0 +1,9 @@ +#include "gbos.h" +/* ls: list files in the RAM filesystem. */ +void main(void) { + char name[9]; + unsigned char i; + for (i = 0; i < 8; i++) { + if (flist(i, name)) { name[8] = 0; puts(name); nl(); } + } +} @@ -0,0 +1,7 @@ +#include "gbos.h" +/* rm <name>: delete a file. */ +void main(void) { + char *argv[4]; + unsigned char argc = argv_parse(argv, 4); + if (argc > 0) fremove(argv[0]); +} diff --git a/c/save.c b/c/save.c new file mode 100644 index 0000000..dd2b8cd --- /dev/null +++ b/c/save.c @@ -0,0 +1,19 @@ +#include "gbos.h" +/* save <name>: read one line from stdin and store it as a file. */ +void main(void) { + char *argv[4]; + unsigned char argc = argv_parse(argv, 4); + unsigned char fd; + char c; + if (argc < 1) { puts("usage: save <name>"); nl(); return; } + fd = open(argv[0], O_WRITE); + if (fd == NOFD) { puts("save: cannot create"); nl(); return; } + for (;;) { + c = readc(); + if (c == EOF) break; + putc(c); /* echo */ + fputc(fd, c); + if (c == '\n') break; + } + close(fd); +} @@ -1,5 +1,5 @@ #include "gbos.h" -/* wc [-l] [-w] [-c]: count lines, words, chars of stdin (default: all). */ +/* wc [-l][-w][-c] [file]: count lines/words/chars of a file or stdin. */ void main(void) { char *argv[8]; unsigned char argc = argv_parse(argv, 8); @@ -7,17 +7,24 @@ void main(void) { unsigned char ww = hasflag(argv, argc, 'w'); unsigned char wch = hasflag(argv, argc, 'c'); unsigned int lines = 0, words = 0, chars = 0; - unsigned char inword = 0, first = 1; - char c; + unsigned char inword = 0, first = 1, i, fd = NOFD; + char *fname = 0; + int ch; if (!wl && !ww && !wch) { wl = ww = wch = 1; } + for (i = 0; i < argc; i++) if (argv[i][0] != '-') { fname = argv[i]; break; } + if (fname) { + fd = open(fname, O_READ); + if (fd == NOFD) { puts("wc: no such file"); nl(); return; } + } for (;;) { - c = readc(); - if (c == EOF) break; + if (fd == NOFD) { char c = readc(); if (c == EOF) break; ch = c; } + else { ch = fgetc(fd); if (ch < 0) break; } chars++; - if (c == '\n') lines++; - if (c == ' ' || c == '\n' || c == '\r' || c == '\t') inword = 0; + if (ch == '\n') lines++; + if (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') inword = 0; else if (!inword) { inword = 1; words++; } } + if (fd != NOFD) close(fd); if (wl) { putu(lines); first = 0; } if (ww) { if (!first) putc(' '); putu(words); first = 0; } if (wch) { if (!first) putc(' '); putu(chars); } diff --git a/include/gbos.inc b/include/gbos.inc index 156876b..7c3f325 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -84,7 +84,32 @@ DEF SYS_GETPID EQU 8 DEF SYS_KILL EQU 9 DEF SYS_BRK EQU 10 DEF SYS_YIELD EQU 11 -DEF SYS_MAX EQU 12 +DEF SYS_GETB EQU 12 ; read a byte from an open file (B=fd -> A, CF=EOF) +DEF SYS_PUTB EQU 13 ; append a byte to an open file (B=fd, E=byte) +DEF SYS_LIST EQU 14 ; list a directory slot (B=slot, DE=namebuf) +DEF SYS_REMOVE EQU 15 ; delete a file (DE=name) +DEF SYS_MAX EQU 16 +; (SYS_OPEN=4 / SYS_CLOSE=5 are now implemented by the filesystem) + +; ----------------------------------------------------------------------------- +; RAM filesystem (lives in WRAMX $D000-$DFFF, 4 KiB; fixed in DMG mode). +; 8 files x 512 bytes: name[8] (first byte 0 = free) + len[2] + data[502]. +; ----------------------------------------------------------------------------- +DEF FS_BASE EQU $D000 +DEF FS_MAX_FILES EQU 8 +DEF FS_FILE_SIZE EQU 512 +DEF FS_NAME EQU 0 +DEF FS_LEN EQU 8 +DEF FS_DATA EQU 10 +DEF FS_MAX_DATA EQU 502 + +; open-file table (kernel WRAM0): inuse, slot, mode, pos(2) +DEF OF_MAX EQU 4 +DEF OF_INUSE EQU 0 +DEF OF_SLOT EQU 1 +DEF OF_MODE EQU 2 +DEF OF_POS EQU 3 +DEF OF_SIZE EQU 5 ; ----------------------------------------------------------------------------- ; Program table indices (see programs.asm). exec() takes one of these in B. @@ -102,5 +127,8 @@ DEF PROG_CAT EQU 9 DEF PROG_WC EQU 10 DEF PROG_HEAD EQU 11 DEF PROG_ARGS EQU 12 +DEF PROG_LS EQU 13 +DEF PROG_SAVE EQU 14 +DEF PROG_RM EQU 15 ENDC diff --git a/src/boot.asm b/src/boot.asm index 73ea860..7c175ce 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -49,6 +49,7 @@ KernelInit: call ClearKernelRAM call CopyHramCode ; move context-switch trampoline into HRAM call ProcInit ; wipe process table + call fs_init ; set up the RAM filesystem ; --- spawn the init task (pid 1); it fork()s the rest --- ld hl, TaskInit diff --git a/src/fs.asm b/src/fs.asm new file mode 100644 index 0000000..692fb45 --- /dev/null +++ b/src/fs.asm @@ -0,0 +1,451 @@ +; ============================================================================= +; fs.asm - a tiny RAM filesystem in WRAMX ($D000-$DFFF, 4 KiB). +; +; 8 fixed slots of 512 bytes: name[8] (first byte 0 = free) + len[2] + data[502]. +; Slot i base = $D000 + i*512 -> high = $D0 + i*2, low = $00. +; Because WRAMX is always mapped (DMG) and the caller's buffers live in the +; mapped $A000 window, the kernel can copy between them directly. +; ============================================================================= +INCLUDE "include/gbos.inc" + +SECTION "fs", ROM0 + +; ----------------------------------------------------------------------------- +; FsSlotBase - in: A = slot (0..7) out: HL = slot base. (A clobbered) +; ----------------------------------------------------------------------------- +FsSlotBase:: + add a ; slot*2 + add HIGH(FS_BASE) ; + $D0 + ld h, a + ld l, 0 + ret + +; ----------------------------------------------------------------------------- +; FsNameEq - compare the <=8-byte NUL-terminated names at DE and HL. +; out: Z set if equal. preserves DE, HL. +; ----------------------------------------------------------------------------- +FsNameEq:: + push de + push hl + ld b, 8 +.cmp + ld a, [de] + cp [hl] + jr nz, .ne + or a ; equal byte; if NUL, strings ended equal + jr z, .eq + inc de + inc hl + dec b + jr nz, .cmp +.eq + pop hl + pop de + xor a ; Z set + ret +.ne + pop hl + pop de + ld a, 1 + and a ; Z clear + ret + +; ----------------------------------------------------------------------------- +; FsFind - in: DE = name out: A = slot (0..7) or $FF. +; ----------------------------------------------------------------------------- +FsFind:: + ld c, 0 +.l + ld a, c + call FsSlotBase ; HL = base + ld a, [hl] ; name[0] + or a + jr z, .next ; free slot + call FsNameEq ; DE vs HL + jr z, .found +.next + inc c + ld a, c + cp FS_MAX_FILES + jr c, .l + ld a, $FF + ret +.found + ld a, c + ret + +; ----------------------------------------------------------------------------- +; FsAllocSlot - out: A = free slot or $FF. +; ----------------------------------------------------------------------------- +FsAllocSlot:: + ld c, 0 +.l + ld a, c + call FsSlotBase + ld a, [hl] + or a + jr z, .found + inc c + ld a, c + cp FS_MAX_FILES + jr c, .l + ld a, $FF + ret +.found + ld a, c + ret + +; ----------------------------------------------------------------------------- +; FsSetName - copy the name at DE into slot wFsSlot (<=8 bytes, incl. NUL). +; ----------------------------------------------------------------------------- +FsSetName:: + ld a, [wFsSlot] + call FsSlotBase ; HL = base (= name) + push de + ld b, 8 +.cp + ld a, [de] + ld [hl+], a + or a + jr z, .done + inc de + dec b + jr nz, .cp +.done + pop de + ret + +; ----------------------------------------------------------------------------- +; OFPtr - in: A = fd out: HL = &open-file entry. preserves DE. +; ----------------------------------------------------------------------------- +OFPtr:: + push de + ld h, 0 + ld l, a + ld d, h + ld e, l ; DE = fd + add hl, hl ; 2 + add hl, hl ; 4 + add hl, de ; 5*fd + ld de, wOFTable + add hl, de + pop de + ret + +; ============================================================================= +; sys_open(DE = name, B = mode 0=read/1=write) -> A = fd or $FF +; ============================================================================= +sys_open:: + ld a, b + ld [wFsMode], a + call FsFind ; A = slot or $FF (DE = name) + ld [wFsSlot], a + ld a, [wFsMode] + or a + jr nz, .write + ; --- read mode: must exist --- + ld a, [wFsSlot] + cp $FF + jr z, .fail + jr .alloc +.write + ; --- write mode: create or truncate --- + ld a, [wFsSlot] + cp $FF + jr nz, .trunc + call FsAllocSlot + cp $FF + jr z, .fail + ld [wFsSlot], a + call FsSetName ; DE still = name +.trunc + ld a, [wFsSlot] + call FsSlotBase + ld a, l + add FS_LEN + ld l, a ; HL = &len + xor a + ld [hl+], a + ld [hl], a ; len = 0 +.alloc + ; --- allocate an open-file entry --- + ld c, 0 +.of + ld a, c + call OFPtr ; HL = &OF[c] + ld a, [hl] + or a + jr z, .gotof + inc c + ld a, c + cp OF_MAX + jr c, .of +.fail + ld a, $FF + ret +.gotof + ld a, 1 + ld [hl+], a ; inuse + ld a, [wFsSlot] + ld [hl+], a ; slot + ld a, [wFsMode] + ld [hl+], a ; mode + xor a + ld [hl+], a ; pos lo + ld [hl], a ; pos hi + ld a, c + ret + +; ============================================================================= +; sys_close(B = fd) +; ============================================================================= +sys_close:: + ld a, b + cp OF_MAX + jr nc, .done + call OFPtr + xor a + ld [hl], a ; inuse = 0 +.done + xor a + ret + +; ============================================================================= +; sys_getb(B = fd) -> A = byte, CF set on EOF +; ============================================================================= +sys_getb:: + ld a, b + cp OF_MAX + jr nc, .eof + call OFPtr ; HL = OF entry + ld a, [hl] + or a + jr z, .eof + ld a, l + ld [wFsOFPtr], a + ld a, h + ld [wFsOFPtr+1], a + inc hl ; OF_SLOT + ld a, [hl] + ld [wFsSlot], a + inc hl ; OF_MODE + inc hl ; OF_POS lo + ld a, [hl+] + ld e, a + ld a, [hl] + ld d, a ; DE = pos + ; file length -> BC + ld a, [wFsSlot] + call FsSlotBase ; HL = base + ld a, l + add FS_LEN + ld l, a + ld a, [hl+] + ld c, a + ld a, [hl] + ld b, a ; BC = len + ; pos >= len ? EOF + ld a, e + sub c + ld a, d + sbc b + jr nc, .eof + ; addr = base + FS_DATA + pos + ld a, [wFsSlot] + call FsSlotBase ; HL = base (low = 0) + ld a, FS_DATA + add e + ld l, a + ld a, h + adc d + ld h, a ; HL = &data[pos] + ld a, [hl] + ld [wFsByte], a + inc de ; pos++ + ; store pos back + ld a, [wFsOFPtr] + add OF_POS + ld l, a + ld a, [wFsOFPtr+1] + adc 0 + ld h, a + ld a, e + ld [hl+], a + ld a, d + ld [hl], a + ld a, [wFsByte] + and a ; CF = 0 (success) + ret +.eof + scf + ret + +; ============================================================================= +; sys_putb(B = fd, E = byte) -- byte is in E, not A: the syscall dispatch +; clobbers A (it indexes the jump table with it). +; ============================================================================= +sys_putb:: + ld a, e + ld [wFsByte], a + ld a, b + cp OF_MAX + jr nc, .done + call OFPtr + ld a, [hl] + or a + jr z, .done + ld a, l + ld [wFsOFPtr], a + ld a, h + ld [wFsOFPtr+1], a + inc hl + ld a, [hl] + ld [wFsSlot], a + inc hl + inc hl ; OF_POS lo + ld a, [hl+] + ld e, a + ld a, [hl] + ld d, a ; DE = pos + ; pos >= FS_MAX_DATA ? full + ld a, e + sub LOW(FS_MAX_DATA) + ld a, d + sbc HIGH(FS_MAX_DATA) + jr nc, .done + ; addr = base + FS_DATA + pos + ld a, [wFsSlot] + call FsSlotBase + ld a, FS_DATA + add e + ld l, a + ld a, h + adc d + ld h, a + ld a, [wFsByte] + ld [hl], a ; write byte + inc de ; pos++ + ; len = pos (sequential append) + ld a, [wFsSlot] + call FsSlotBase + ld a, l + add FS_LEN + ld l, a + ld a, e + ld [hl+], a + ld a, d + ld [hl], a + ; store pos back + ld a, [wFsOFPtr] + add OF_POS + ld l, a + ld a, [wFsOFPtr+1] + adc 0 + ld h, a + ld a, e + ld [hl+], a + ld a, d + ld [hl], a +.done + xor a + ret + +; ============================================================================= +; sys_list(B = slot, DE = namebuf[8]) -> A = 1 if used (name copied), else 0 +; ============================================================================= +sys_list:: + ld a, b + cp FS_MAX_FILES + jr nc, .no + call FsSlotBase ; HL = base (name) + ld a, [hl] + or a + jr z, .no + ld b, 8 +.cp + ld a, [hl+] + ld [de], a + inc de + dec b + jr nz, .cp + ld a, 1 + ret +.no + xor a + ret + +; ============================================================================= +; sys_remove(DE = name) -> A = 0 ok / $FF not found +; ============================================================================= +sys_remove:: + call FsFind + cp $FF + jr z, .no + call FsSlotBase + xor a + ld [hl], a ; name[0] = 0 -> free + xor a + ret +.no + ld a, $FF + ret + +; ============================================================================= +; fs_init - mark all slots free, clear the OF table, seed a "readme" file. +; ============================================================================= +fs_init:: + ld c, 0 +.clr + ld a, c + call FsSlotBase + xor a + ld [hl], a ; name[0] = 0 + inc c + ld a, c + cp FS_MAX_FILES + jr c, .clr + ; zero the open-file table + ld hl, wOFTable + ld b, OF_MAX * OF_SIZE + xor a +.zof + ld [hl+], a + dec b + jr nz, .zof + ; seed slot 0 = "readme" + xor a + ld [wFsSlot], a + ld de, .rname + call FsSetName + ; copy content -> data + xor a + call FsSlotBase ; HL = base + ld a, FS_DATA + ld l, a ; HL = &data (base low = 0) + ld de, .rtext + ld bc, .rtext_end - .rtext +.ccpy + ld a, [de] + ld [hl+], a + inc de + dec bc + ld a, b + or c + jr nz, .ccpy + ; set len + xor a + call FsSlotBase + ld a, l + add FS_LEN + ld l, a + ld a, LOW(.rtext_end - .rtext) + ld [hl+], a + ld a, HIGH(.rtext_end - .rtext) + ld [hl], a + ret +.rname + db "readme", 0 +.rtext + db "welcome to gbos!", $0D, $0A + db "files live in RAM.", $0D, $0A +.rtext_end diff --git a/src/kdata.asm b/src/kdata.asm index 5b91030..9aca466 100644 --- a/src/kdata.asm +++ b/src/kdata.asm @@ -38,3 +38,10 @@ wExitParentPid:: DS 1 wWaitMyPid:: DS 1 wWaitRetPid:: DS 1 wWaitRetCode:: DS 1 + +; filesystem: open-file table + scratch +wOFTable:: DS OF_MAX * OF_SIZE +wFsMode:: DS 1 +wFsSlot:: DS 1 +wFsByte:: DS 1 +wFsOFPtr:: DS 2 diff --git a/src/programs.asm b/src/programs.asm index a3a6db2..161ee2f 100644 --- a/src/programs.asm +++ b/src/programs.asm @@ -79,6 +79,15 @@ ProgHead: SECTION "prog_args", ROMX[$4000], BANK[14] ProgArgs: INCBIN "c/args.bin" +SECTION "prog_ls", ROMX[$4000], BANK[15] +ProgLs: + INCBIN "c/ls.bin" +SECTION "prog_save", ROMX[$4000], BANK[16] +ProgSave: + INCBIN "c/save.bin" +SECTION "prog_rm", ROMX[$4000], BANK[17] +ProgRm: + INCBIN "c/rm.bin" ; ----------------------------------------------------------------------------- ; PROG_SH (bank 3) - a tiny shell. @@ -217,6 +226,12 @@ ProgSh: db PROG_HEAD db "args", 0 db PROG_ARGS + db "ls", 0 + db PROG_LS + db "save", 0 + db PROG_SAVE + db "rm", 0 + db PROG_RM db 0 ; terminator ; ----------------------------------------------------------------------------- @@ -277,3 +292,9 @@ ProgramTable:: dw ProgHead db LOW(BANK(ProgArgs)), HIGH(BANK(ProgArgs)) dw ProgArgs + db LOW(BANK(ProgLs)), HIGH(BANK(ProgLs)) + dw ProgLs + db LOW(BANK(ProgSave)), HIGH(BANK(ProgSave)) + dw ProgSave + db LOW(BANK(ProgRm)), HIGH(BANK(ProgRm)) + dw ProgRm diff --git a/src/syscall.asm b/src/syscall.asm index bf75865..5ed3d6f 100644 --- a/src/syscall.asm +++ b/src/syscall.asm @@ -38,14 +38,18 @@ SyscallTable: dw sys_fork ; 1 FORK dw sys_read ; 2 READ dw sys_write ; 3 WRITE - dw sys_nosys ; 4 OPEN (TODO) - dw sys_nosys ; 5 CLOSE (TODO) + dw sys_open ; 4 OPEN + dw sys_close ; 5 CLOSE dw sys_exec ; 6 EXEC dw sys_wait ; 7 WAIT dw sys_getpid ; 8 GETPID dw sys_nosys ; 9 KILL (TODO) dw sys_nosys ; 10 BRK (TODO) dw sys_yield ; 11 YIELD + dw sys_getb ; 12 GETB + dw sys_putb ; 13 PUTB + dw sys_list ; 14 LIST + dw sys_remove ; 15 REMOVE ; ----------------------------------------------------------------------------- sys_nosys: |
