diff options
Diffstat (limited to 'src/fs.asm')
| -rw-r--r-- | src/fs.asm | 451 |
1 files changed, 451 insertions, 0 deletions
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 |
