diff options
| author | user <user@clank> | 2026-07-16 12:49:19 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-16 12:49:19 +0200 |
| commit | 5fefd233769a56caefdb2ffecaa9fb8b9ec6db86 (patch) | |
| tree | 99f5b285a45c7b3792bb64e79fe58f70a6d01cab /src/fs.asm | |
| parent | fs rewrite stage 2: inodes + root directory (block-based, persistent) (diff) | |
| download | gbos-5fefd233769a56caefdb2ffecaa9fb8b9ec6db86.tar.gz gbos-5fefd233769a56caefdb2ffecaa9fb8b9ec6db86.tar.xz gbos-5fefd233769a56caefdb2ffecaa9fb8b9ec6db86.zip | |
fs rewrite stage 3: nested directories, paths, cwd (cd/mkdir/ls)
- directories generalized: dir_find/dir_add/dir_remove take any dir inode;
every dir carries '.'/'..'. Path resolution (resolve / resolve_parent) walks
absolute or cwd-relative paths; open/mkdir/chdir/remove/opendir take paths.
- per-process cwd: PROC_CWD in the PCB (root by default, inherited on fork);
sys_chdir sets it, sys_opendir points ls at any directory.
- shell: 'cd' builtin + cwd-aware prompt; 'mkdir'/'ls <dir>' tools; 'exit'/'quit'
and EOF call poweroff() (clean shutdown via the $ED opcode -> reliable save).
- three HL/buffer-clobber bugs fixed along the way: resolve didn't preserve the
path cursor across dir_find; cur_cwd clobbered HL; resolve_parent set the final
name before resolve() overwrote wFsNameBuf (now copied after).
- verified: nested mkdir/cd/save, multi-level paths (cat docs/sub/b), and the
whole tree persists across a reboot.
- README: document directories + the poweroff opcode.
Diffstat (limited to '')
| -rw-r--r-- | src/fs.asm | 369 |
1 files changed, 336 insertions, 33 deletions
@@ -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 |
