; ============================================================================= ; 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