aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-16 07:53:19 +0200
committeruser <user@clank>2026-07-16 07:53:19 +0200
commit7b5c532625a864a3fbaf76f44df6aa99b0d6861e (patch)
tree6a6d67644993ccaea4b71bdc0881798887a6d1aa /src
parentlibc: option parsing (hasflag/optval); wc -l/-w/-c, head -n N (diff)
downloadgbos-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.
Diffstat (limited to '')
-rw-r--r--src/boot.asm1
-rw-r--r--src/fs.asm451
-rw-r--r--src/kdata.asm7
-rw-r--r--src/programs.asm21
-rw-r--r--src/syscall.asm8
5 files changed, 486 insertions, 2 deletions
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: