aboutsummaryrefslogtreecommitdiffstats
path: root/src/fs.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs.asm')
-rw-r--r--src/fs.asm369
1 files changed, 336 insertions, 33 deletions
diff --git a/src/fs.asm b/src/fs.asm
index 7e59f3e..d543b0f 100644
--- a/src/fs.asm
+++ b/src/fs.asm
@@ -221,23 +221,28 @@ entry_ptr:
ld l, a
ret ; (fs_dirbuf is page-aligned, no carry)
-; load_rootdir - read the root directory's data block into fs_dirbuf
-load_rootdir:
- ld a, [fs_inobuf + 16 + I_BLOCKS] ; root inode (1) blocks[0]
+; dir_load - read directory wFsDir's block into fs_dirbuf (single-block dirs).
+dir_load:
+ ld a, [wFsDir]
+ call inode_ptr
+ ld a, l
+ add I_BLOCKS
+ ld l, a
+ ld a, [hl] ; blocks[0]
+ ld [wFsDirBlk], a
ld hl, fs_dirbuf
jp read_block
-; write_rootdir - write fs_dirbuf back to the root directory's data block
-write_rootdir:
- ld a, [fs_inobuf + 16 + I_BLOCKS]
+dir_write:
+ ld a, [wFsDirBlk]
ld hl, fs_dirbuf
jp write_block
; -----------------------------------------------------------------------------
-; dir_find - name in wFsNameBuf -> A = inode (0 if not found)
+; dir_find - dir inode in wFsDir, name in wFsNameBuf -> A = inode (0 if none)
; -----------------------------------------------------------------------------
dir_find::
- call load_rootdir
+ call dir_load
ld c, 0
.l
call entry_ptr ; HL = &entry
@@ -255,16 +260,15 @@ dir_find::
xor a
ret
.found
- dec hl ; back to entry base
+ dec hl
ld a, [hl]
ret
; -----------------------------------------------------------------------------
-; dir_add - A = inode to link (name in wFsNameBuf) -> A = 1 ok / 0 dir full
+; dir_add - dir in wFsDir, child in wFsInode, name in wFsNameBuf -> A = 1/0
; -----------------------------------------------------------------------------
dir_add::
- ld [wFsInode], a
- call load_rootdir
+ call dir_load
ld c, 0
.l
call entry_ptr
@@ -280,7 +284,7 @@ dir_add::
.free
ld a, [wFsInode]
ld [hl+], a ; entry.inode
- ld de, wFsNameBuf ; copy the name
+ ld de, wFsNameBuf
ld b, 15
.cp
ld a, [de]
@@ -288,15 +292,15 @@ dir_add::
inc de
dec b
jr nz, .cp
- call write_rootdir
+ call dir_write
ld a, 1
ret
; -----------------------------------------------------------------------------
-; dir_remove - name in wFsNameBuf -> A = inode removed (0 if not found)
+; dir_remove - dir in wFsDir, name in wFsNameBuf -> A = inode removed (0 none)
; -----------------------------------------------------------------------------
dir_remove::
- call load_rootdir
+ call dir_load
ld c, 0
.l
call entry_ptr
@@ -314,16 +318,173 @@ dir_remove::
xor a
ret
.found
- dec hl ; entry base
+ dec hl
ld a, [hl]
ld [wFsInode], a
xor a
- ld [hl], a ; clear entry.inode
- call write_rootdir
+ ld [hl], a
+ call dir_write
ld a, [wFsInode]
ret
; -----------------------------------------------------------------------------
+; cur_cwd -> A = current process's cwd inode
+; -----------------------------------------------------------------------------
+cur_cwd:
+ push hl ; callers (resolve) keep a path cursor in HL
+ ld a, [wCurProc]
+ add PROC_CWD
+ ld l, a
+ ld a, [wCurProc+1]
+ adc 0
+ ld h, a
+ ld a, [hl]
+ pop hl
+ ret
+
+; -----------------------------------------------------------------------------
+; resolve - path in wFsPath -> A = inode (0 if any component missing)
+; -----------------------------------------------------------------------------
+resolve:
+ ld hl, wFsPath
+ ld a, [hl]
+ cp $2F ; '/'
+ jr nz, .rel
+ inc hl
+ ld a, ROOT_INO
+ jr .setcur
+.rel
+ call cur_cwd
+.setcur
+ ld [wFsCur], a
+.loop
+ ld a, [hl] ; skip '/' separators
+ cp $2F
+ jr nz, .comp
+ inc hl
+ jr .loop
+.comp
+ ld a, [hl]
+ or a
+ jr z, .done ; end of path
+ call extract_comp ; wFsNameBuf = component, HL past it
+ push hl ; dir_find clobbers HL (the path cursor)
+ ld a, [wFsCur]
+ ld [wFsDir], a
+ call dir_find ; A = child inode
+ pop hl
+ or a
+ ret z ; missing component
+ ld [wFsCur], a
+ jr .loop
+.done
+ ld a, [wFsCur]
+ ret
+
+; extract_comp - copy the path component at [HL] into wFsNameBuf, advancing HL
+; to the next '/' or NUL. (<=15 chars)
+extract_comp:
+ ld de, wFsNameBuf
+ ld b, 15
+.l
+ ld a, [hl]
+ or a
+ jr z, .end
+ cp $2F
+ jr z, .end
+ ld [de], a
+ inc de
+ inc hl
+ dec b
+ jr nz, .l
+.end
+ xor a
+ ld [de], a
+ ret
+
+; -----------------------------------------------------------------------------
+; resolve_parent - path in wFsPath -> A = parent dir inode (0 if a dir in the
+; path is missing), wFsNameBuf = the final component.
+; -----------------------------------------------------------------------------
+resolve_parent:
+ ; find the last '/'
+ ld hl, wFsPath
+ xor a
+ ld [wFsSlashPtr], a
+ ld [wFsSlashPtr+1], a
+.scan
+ ld a, [hl]
+ or a
+ jr z, .scandone
+ cp $2F
+ jr nz, .noslash
+ ld a, l
+ ld [wFsSlashPtr], a
+ ld a, h
+ ld [wFsSlashPtr+1], a
+.noslash
+ inc hl
+ jr .scan
+.scandone
+ ld a, [wFsSlashPtr]
+ or a
+ jr nz, .haveslash
+ ld a, [wFsSlashPtr+1]
+ or a
+ jr nz, .haveslash
+ ; no '/': parent = cwd, name = whole path
+ ld hl, wFsPath
+ call comp_to_namebuf
+ call cur_cwd
+ ret
+.haveslash
+ ld a, [wFsSlashPtr]
+ ld l, a
+ ld a, [wFsSlashPtr+1]
+ ld h, a ; HL = &lastslash
+ ; if the slash is the very first char, parent = root
+ ld a, l
+ cp LOW(wFsPath)
+ jr nz, .split
+ ld a, h
+ cp HIGH(wFsPath)
+ jr nz, .split
+ inc hl ; name = after the '/'
+ call comp_to_namebuf
+ ld a, ROOT_INO
+ ret
+.split
+ push hl ; &lastslash
+ xor a
+ ld [hl], a ; terminate the dir part at the slash
+ call resolve ; resolve the dir part -> A = parent inode
+ pop hl ; &lastslash
+ or a
+ ret z ; a directory in the path is missing
+ ld [wFsCur], a
+ inc hl ; name = after the '/'
+ call comp_to_namebuf ; set the final name AFTER resolve (it clobbers it)
+ ld a, [wFsCur]
+ ret
+
+; comp_to_namebuf - copy the NUL-terminated string at HL into wFsNameBuf (<=15)
+comp_to_namebuf:
+ ld de, wFsNameBuf
+ ld b, 15
+.l
+ ld a, [hl]
+ ld [de], a
+ or a
+ ret z
+ inc hl
+ inc de
+ dec b
+ jr nz, .l
+ xor a
+ ld [de], a
+ ret
+
+; -----------------------------------------------------------------------------
; file_truncate - A = inode ; free all data blocks, size = 0 (cached inode)
; -----------------------------------------------------------------------------
file_truncate::
@@ -411,9 +572,18 @@ fs_format::
ld a, BLK_INODE0 + 1
ld hl, fs_inobuf + 256
call write_block
- ; root dir data block (block 4): empty
+ ; root dir data block (block 4): "." and ".." both -> root (inode 1)
ld hl, fs_dirbuf
call zero256
+ ld a, ROOT_INO
+ ld [fs_dirbuf + 0], a ; entry 0 inode
+ ld a, $2E ; '.'
+ ld [fs_dirbuf + 1], a
+ ld a, ROOT_INO
+ ld [fs_dirbuf + 16], a ; entry 1 inode
+ ld a, $2E
+ ld [fs_dirbuf + 17], a ; '.'
+ ld [fs_dirbuf + 18], a ; '.'
ld a, BLK_DATA0
ld hl, fs_dirbuf
jp write_block
@@ -458,6 +628,8 @@ fs_init::
call read_block
ld a, $FF
ld [wFsCurBlk], a
+ ld a, ROOT_INO
+ ld [wListDir], a
ret
; -----------------------------------------------------------------------------
@@ -477,10 +649,10 @@ OFPtr::
pop de
ret
-; copy the NUL-terminated name at DE (process memory) into wFsNameBuf.
-copy_name:
- ld hl, wFsNameBuf
- ld b, 15
+; copy the NUL-terminated path at DE (process memory) into wFsPath (<=39).
+copy_path:
+ ld hl, wFsPath
+ ld b, 39
.c
ld a, [de]
ld [hl+], a
@@ -494,13 +666,17 @@ copy_name:
ret
; =============================================================================
-; sys_open(DE = name, B = mode 0=read/1=write) -> A = fd or $FF
+; sys_open(DE = path, B = mode 0=read/1=write) -> A = fd or $FF
; =============================================================================
sys_open::
ld a, b
ld [wFsMode], a
- call copy_name ; name -> wFsNameBuf (before any block I/O)
- call dir_find ; A = inode or 0
+ call copy_path ; path -> wFsPath
+ call resolve_parent ; A = parent dir inode, wFsNameBuf = final name
+ or a
+ jr z, .fail
+ ld [wFsDir], a
+ call dir_find ; A = target inode or 0
ld [wFsInode], a
ld a, [wFsMode]
or a
@@ -535,8 +711,7 @@ sys_open::
ld [hl], a ; nlink
ld a, [wFsInode]
call write_inode
- ld a, [wFsInode]
- call dir_add
+ call dir_add ; wFsDir=parent, wFsInode=child, wFsNameBuf=name
or a
jr z, .fail
jr .allocof
@@ -776,7 +951,7 @@ sys_putb::
; =============================================================================
; sys_list(B = index, DE = namebuf) -> A = 1 (name copied) / 0 (no more)
-; index selects the index-th *used* entry in the root directory.
+; index selects the index-th non-hidden entry in the *current* directory.
; =============================================================================
sys_list::
ld a, e
@@ -785,7 +960,9 @@ sys_list::
ld [wFsOFPtr+1], a
ld a, b
ld [wFsSlot], a ; wanted index
- call load_rootdir
+ ld a, [wListDir]
+ ld [wFsDir], a
+ call dir_load
ld c, 0 ; entry cursor
ld b, 0 ; used counter
.l
@@ -793,6 +970,11 @@ sys_list::
ld a, [hl]
or a
jr z, .next
+ inc hl
+ ld a, [hl] ; name[0]
+ dec hl
+ cp $2E ; '.' -> hidden (skip ".", "..")
+ jr z, .next
ld a, [wFsSlot]
cp b
jr z, .found
@@ -821,10 +1003,14 @@ sys_list::
ret
; =============================================================================
-; sys_remove(DE = name) -> A = 0 ok / $FF not found
+; sys_remove(DE = path) -> A = 0 ok / $FF not found
; =============================================================================
sys_remove::
- call copy_name
+ call copy_path
+ call resolve_parent ; A = parent inode, wFsNameBuf = name
+ or a
+ jr z, .no
+ ld [wFsDir], a
call dir_remove ; A = inode or 0
or a
jr z, .no
@@ -843,3 +1029,120 @@ sys_remove::
.no
ld a, $FF
ret
+
+; =============================================================================
+; sys_mkdir(DE = path) -> A = 0 ok / $FF
+; =============================================================================
+sys_mkdir::
+ call copy_path
+ call resolve_parent ; A = parent inode, wFsNameBuf = name
+ or a
+ jr z, .fail
+ ld [wFsDir], a ; parent dir
+ call dir_find ; already exists?
+ or a
+ jr nz, .fail
+ call alloc_inode
+ or a
+ jr z, .fail
+ ld [wFsInode], a ; new directory inode
+ call alloc_block
+ or a
+ jr z, .fail
+ ld [wFsSlot], a ; new directory's data block
+ ; init the inode (type=dir, nlink=1, size=32, blocks[0]=data block)
+ ld a, [wFsInode]
+ call inode_ptr
+ push hl
+ ld b, 16
+ xor a
+.zi
+ ld [hl+], a
+ dec b
+ jr nz, .zi
+ pop hl
+ ld a, IT_DIR
+ ld [hl+], a
+ ld a, 1
+ ld [hl+], a
+ ld a, 32
+ ld [hl+], a
+ xor a
+ ld [hl+], a
+ ld a, [wFsSlot]
+ ld [hl], a
+ ld a, [wFsInode]
+ call write_inode
+ ; the new dir's data block: "." -> self, ".." -> parent
+ ld hl, fs_dirbuf
+ call zero256
+ ld a, [wFsInode]
+ ld [fs_dirbuf + 0], a
+ ld a, $2E
+ ld [fs_dirbuf + 1], a
+ ld a, [wFsDir]
+ ld [fs_dirbuf + 16], a
+ ld a, $2E
+ ld [fs_dirbuf + 17], a
+ ld [fs_dirbuf + 18], a
+ ld a, [wFsSlot]
+ ld hl, fs_dirbuf
+ call write_block
+ ; link the new dir into the parent (wFsDir/wFsInode/wFsNameBuf all set)
+ call dir_add
+ or a
+ jr z, .fail
+ xor a
+ ret
+.fail
+ ld a, $FF
+ ret
+
+; =============================================================================
+; sys_chdir(DE = path) -> A = 0 ok / $FF (not found or not a directory)
+; =============================================================================
+sys_chdir::
+ call copy_path
+ call resolve ; A = inode or 0
+ or a
+ jr z, .fail
+ ld [wFsInode], a
+ call inode_ptr
+ ld a, [hl] ; I_TYPE
+ cp IT_DIR
+ jr nz, .fail
+ ld a, [wCurProc]
+ add PROC_CWD
+ ld l, a
+ ld a, [wCurProc+1]
+ adc 0
+ ld h, a
+ ld a, [wFsInode]
+ ld [hl], a ; PROC_CWD = inode
+ xor a
+ ret
+.fail
+ ld a, $FF
+ ret
+
+; =============================================================================
+; sys_opendir(DE = path) -> A = 0 ok / $FF ; sets the directory ls enumerates.
+; Empty path or "." means the current directory.
+; =============================================================================
+sys_opendir::
+ call copy_path
+ call resolve
+ or a
+ jr z, .fail
+ ld [wFsInode], a
+ call inode_ptr
+ ld a, [hl]
+ cp IT_DIR
+ jr nz, .fail
+ ld a, [wFsInode]
+ ld [wListDir], a
+ xor a
+ ret
+.fail
+ ld a, $FF
+ ret