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 | |
| 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-- | Makefile | 2 | ||||
| -rw-r--r-- | README.md | 23 | ||||
| -rw-r--r-- | c/gbos.h | 4 | ||||
| -rw-r--r-- | c/libc.s | 25 | ||||
| -rw-r--r-- | c/ls.c | 5 | ||||
| -rw-r--r-- | c/mkdir.c | 8 | ||||
| -rw-r--r-- | c/sh.c | 38 | ||||
| -rw-r--r-- | include/gbos.inc | 11 | ||||
| -rw-r--r-- | src/fs.asm | 369 | ||||
| -rw-r--r-- | src/kdata.asm | 8 | ||||
| -rw-r--r-- | src/proc.asm | 9 | ||||
| -rw-r--r-- | src/programs.asm | 7 | ||||
| -rw-r--r-- | src/syscall.asm | 3 |
13 files changed, 463 insertions, 49 deletions
@@ -20,7 +20,7 @@ $(BUILD)/%.o: src/%.asm | $(BUILD) # C programs: compiled with SDCC into ROM-bank blobs and INCBIN'd by programs.asm CBLOBS := c/chello.bin c/echo.bin c/true.bin c/false.bin c/uname.bin \ c/pid.bin c/cat.bin c/wc.bin c/head.bin c/args.bin \ - c/ls.bin c/save.bin c/rm.bin c/sh.bin c/ps.bin c/kill.bin c/spin.bin c/blk.bin + c/ls.bin c/save.bin c/rm.bin c/sh.bin c/ps.bin c/kill.bin c/spin.bin c/blk.bin c/mkdir.bin $(BUILD)/programs.o: $(CBLOBS) # C programs also depend on the shared libc sources @@ -304,9 +304,12 @@ blocks 2-3 inode table (32 inodes x 16 B: type, size, 8 direct block ptrs) blocks 4.. data blocks ``` -- **Inodes + a root directory.** A directory is a file of 16-byte entries - (inode# + name). Files are up to **2 KiB** (8 direct blocks); up to **16** - entries in the root, **32** inodes. +- **Inodes + nested directories.** A directory is a file of 16-byte entries + (inode# + name), each carrying `.`/`..`. Files are up to **2 KiB** (8 direct + blocks); **32** inodes, 16 entries per directory. +- **Paths + per-process cwd.** `open`/`mkdir`/`chdir`/`remove`/`ls` take paths + (absolute `/a/b` or relative `a/b`); each process has a cwd inode (`PROC_CWD`, + inherited on fork). Shell builtins: `cd`, and a cwd-aware prompt. - **Bounce buffer.** Only `read_block`/`write_block` touch cart-RAM banking; everything else works on WRAM buffers. The bitmap + inode table are cached in WRAMX; data/dir blocks stream through a one-block cache. @@ -320,8 +323,15 @@ $ save notes gbos has a real filesystem now $ cat notes gbos has a real filesystem now -$ ls -notes +$ mkdir docs +$ cd docs +/docs$ save note +hi +/docs$ cd / +$ cat docs/note +hi +$ ls docs +note $ wc notes # survives a reboot 1 5 31 ``` @@ -347,7 +357,8 @@ $ wc notes # survives a reboot - [x] **stage 1**: block device (`blk.asm`) via a WRAM bounce buffer - [x] **stage 2**: inodes + root directory; variable-size files (<=2 KiB), bitmap allocators; 16 files / 32 inodes; persists across reboots - - [ ] stage 3: nested directories, path resolution, per-process cwd, `cd`/`mkdir` + - [x] **stage 3**: nested directories, path resolution, per-process cwd, + `cd`/`mkdir`/`ls <dir>` — the whole tree persists across reboots - [ ] Preemptive scheduling: real context save in `TimerISR` → `hSwitchTo` - [ ] Use the `$D000-$DFFF` u-area (SVBK) for per-process kernel state / kstack - [ ] `brk`/heap allocator inside the task bank (heap up, stack down, collision = ENOMEM) @@ -27,6 +27,10 @@ int fgetc(unsigned char fd); /* a byte, or -1 at EOF */ void fputc(unsigned char fd, char ch); unsigned char flist(unsigned char slot, char *namebuf); /* 1 if slot used */ void fremove(const char *name); +unsigned char mkdir(const char *path); +unsigned char chdir(const char *path); +unsigned char opendir(const char *path); +void poweroff(void); /* clean emulator shutdown (saves) */ #define O_READ 0 #define O_WRITE 1 #define NOFD 0xFF @@ -221,3 +221,28 @@ _bpeek2: rst #0x30 ret + + .globl _mkdir, _chdir + ;; unsigned char mkdir(const char *path /*DE*/) -> A (0 ok / $FF) +_mkdir: + ld c, #25 ; SYS_MKDIR + rst #0x30 + ret + ;; unsigned char chdir(const char *path /*DE*/) -> A (0 ok / $FF) +_chdir: + ld c, #26 ; SYS_CHDIR + rst #0x30 + ret + + .globl _opendir + ;; unsigned char opendir(const char *path /*DE*/) -> A (0 ok / $FF) +_opendir: + ld c, #27 ; SYS_OPENDIR + rst #0x30 + ret + + .globl _poweroff + ;; void poweroff(void) - execute the emulator's clean-exit opcode ($ED) +_poweroff: + .db 0xED +1$: jr 1$ @@ -1,7 +1,10 @@ #include "gbos.h" -/* ls: list files in the filesystem. */ +/* ls [dir]: list a directory (default: current). */ void main(void) { + char *argv[4]; char name[16]; + unsigned char argc = argv_parse(argv, 4); unsigned char i; + if (opendir(argc > 0 ? argv[0] : ".")) { puts("ls: no such directory"); nl(); return; } for (i = 0; flist(i, name); i++) { name[15] = 0; puts(name); nl(); } } diff --git a/c/mkdir.c b/c/mkdir.c new file mode 100644 index 0000000..c907518 --- /dev/null +++ b/c/mkdir.c @@ -0,0 +1,8 @@ +#include "gbos.h" +/* mkdir <path>: create a directory. */ +void main(void) { + char *argv[4]; + unsigned char argc = argv_parse(argv, 4); + if (argc < 1) { puts("usage: mkdir <name>"); nl(); return; } + if (mkdir(argv[0])) { puts("mkdir: failed"); nl(); } +} @@ -18,6 +18,30 @@ static unsigned char tokenize(char *s, char **tok, unsigned char max) { } static unsigned char isop(char *t, char c) { return t[0] == c && t[1] == 0; } +static unsigned char iscmd(char *t, char a, char b) { + return t[0] == a && t[1] == b && t[2] == 0; +} + +/* keep the prompt's path string roughly in sync with chdir(). */ +static void set_cwd(char *cwd, char *arg) { + char *d, *s, *last; + if (arg[0] == '/') { /* absolute */ + d = cwd; s = arg; + while (*s) *d++ = *s++; + *d = 0; + } else if (arg[0] == '.' && arg[1] == '.' && arg[2] == 0) { + last = cwd; d = cwd; + while (*d) { if (*d == '/') last = d; d++; } + if (last == cwd) { cwd[0] = '/'; cwd[1] = 0; } else *last = 0; + } else if (arg[0] == '.' && arg[1] == 0) { + /* stay */ + } else { /* relative: append */ + d = cwd; while (*d) d++; + if (!(cwd[0] == '/' && cwd[1] == 0)) *d++ = '/'; + s = arg; while (*s) *d++ = *s++; + *d = 0; + } +} /* build the child command line "name\0arg1 arg2\0" at 0xA000 */ static void set_cmdline(char **tok, unsigned char start, unsigned char end) { @@ -74,22 +98,32 @@ done: void main(void) { char line[80]; char *tok[16]; + char cwd[40]; + cwd[0] = '/'; cwd[1] = 0; for (;;) { unsigned char nt, i, pipepos, c, bg; /* reap finished background jobs */ { unsigned char p; while ((p = reap())) { putc('['); putu(p); puts(" done]"); nl(); } } - puts("$ "); + puts(cwd); puts("$ "); /* read a line (echoing), stop at newline; exit on EOF */ i = 0; for (;;) { c = readc(); - if (c == EOF) return; + if (c == EOF) poweroff(); if (c == '\r' || c == '\n') { nl(); break; } if (i < 79) { line[i++] = c; putc(c); } } line[i] = 0; nt = tokenize(line, tok, 16); if (nt == 0) continue; + if (iscmd(tok[0], 'c', 'd')) { /* cd builtin */ + char *arg = (nt >= 2) ? tok[1] : "/"; + if (chdir(arg) == 0) set_cwd(cwd, arg); + else { puts("cd: no such directory"); nl(); } + continue; + } + if ((tok[0][0] == 'e' && tok[0][1] == 'x') || + (tok[0][0] == 'q' && tok[0][1] == 'u')) poweroff(); bg = 0; if (isop(tok[nt - 1], '&')) { bg = 1; nt--; if (nt == 0) continue; } pipepos = 0; diff --git a/include/gbos.inc b/include/gbos.inc index 770bd55..c4804c4 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -85,6 +85,7 @@ DEF PROC_EXIT RB 1 ; exit code (valid when PS_ZOMBIE) DEF PROC_STDIN RB 1 ; $FF = console, else an open-file fd DEF PROC_STDOUT RB 1 ; $FF = console, else an open-file fd DEF PROC_PROG RB 1 ; program id currently running ($FF = none), for ps +DEF PROC_CWD RB 1 ; current working directory (inode #) DEF PROC_SIZE RB 0 DEF KILL_CODE EQU 137 ; exit status of a killed process (128 + SIGKILL) @@ -118,8 +119,11 @@ DEF SYS_PS EQU 20 ; process table entry (B=slot, DE=buf3 -> A= DEF SYS_PROGNAME EQU 21 ; program id -> name (B=id, DE=namebuf) DEF SYS_REAP EQU 22 ; reap a zombie child, nohang (-> A=pid or 0) DEF SYS_BFILL EQU 23 ; [debug] fill a disk block (B=block, E=byte) -DEF SYS_BPEEK EQU 24 ; [debug] read a disk block[0] (B=block -> A) -DEF SYS_MAX EQU 25 +DEF SYS_BPEEK EQU 24 ; [debug] read a disk block (B=block, E=off -> A) +DEF SYS_MKDIR EQU 25 ; make a directory (DE=path -> A=0/$FF) +DEF SYS_CHDIR EQU 26 ; change directory (DE=path -> A=0/$FF) +DEF SYS_OPENDIR EQU 27 ; point ls at a directory (DE=path -> A=0/$FF) +DEF SYS_MAX EQU 28 ; SYS_KILL (9) is now implemented (B=pid -> A=0/$FF; pid 1 is immortal) ; (SYS_OPEN=4 / SYS_CLOSE=5 are now implemented by the filesystem) @@ -136,7 +140,7 @@ DEF BLK_SUPER EQU 0 DEF BLK_BITMAP EQU 1 DEF BLK_INODE0 EQU 2 ; inode table = blocks 2,3 DEF BLK_DATA0 EQU 4 ; first data block -DEF FS_VERSION EQU 2 +DEF FS_VERSION EQU 3 ; bumped: dirs now carry "."/".." entries DEF NINODES EQU 32 DEF ROOT_INO EQU 1 DEF NDIRECT EQU 8 ; direct block pointers per inode -> files <= 2 KiB @@ -196,5 +200,6 @@ DEF PROG_PS EQU 16 DEF PROG_KILL EQU 17 DEF PROG_SPIN EQU 18 DEF PROG_BLK EQU 19 +DEF PROG_MKDIR EQU 20 ENDC @@ -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 diff --git a/src/kdata.asm b/src/kdata.asm index b239caa..6c520dd 100644 --- a/src/kdata.asm +++ b/src/kdata.asm @@ -48,7 +48,13 @@ wFsOFPtr:: DS 2 wFsCurBlk:: DS 1 ; data block currently in fs_datbuf ($FF = none) wFsDirty:: DS 1 ; fs_datbuf needs write-back wFsInode:: DS 1 ; working inode number -wFsNameBuf:: DS 16 ; kernel-side copy of a name argument +wFsNameBuf:: DS 16 ; kernel-side copy of a name/component +wFsDir:: DS 1 ; directory inode for dir_* ops +wFsDirBlk:: DS 1 ; that directory's data block (for write-back) +wFsCur:: DS 1 ; current inode during path resolution +wFsPath:: DS 40 ; kernel-side copy of a path argument +wFsSlashPtr::DS 2 ; last '/' seen while splitting a path +wListDir:: DS 1 ; directory sys_list enumerates wKChar:: DS 1 ; scratch for KPutc wKillParent::DS 1 ; scratch for sys_kill wPsBuf:: DS 2 ; scratch for sys_ps diff --git a/src/proc.asm b/src/proc.asm index cd79160..c22d40d 100644 --- a/src/proc.asm +++ b/src/proc.asm @@ -226,7 +226,9 @@ ProcCreate:: ld [hl+], a ; PROC_STDIN = console ld [hl+], a ; PROC_STDOUT = console ld a, $FF - ld [hl], a ; PROC_PROG = none + ld [hl+], a ; PROC_PROG = none + ld a, ROOT_INO + ld [hl], a ; PROC_CWD = root ; build the initial stack frame in the task's RAM bank ld a, [wTmpEntry] @@ -458,7 +460,10 @@ sys_fork:: ld [hl+], a ; child STDOUT = parent STDOUT inc de ld a, [de] - ld [hl], a ; child PROC_PROG = parent PROC_PROG + ld [hl+], a ; child PROC_PROG = parent PROC_PROG + inc de + ld a, [de] + ld [hl], a ; child PROC_CWD = parent PROC_CWD ; ---- return to the parent with child pid ---- ld a, [wForkUserSP] diff --git a/src/programs.asm b/src/programs.asm index 53142ec..64efc4d 100644 --- a/src/programs.asm +++ b/src/programs.asm @@ -100,6 +100,9 @@ ProgSpin: SECTION "prog_blk", ROMX[$4000], BANK[21] ProgBlk: INCBIN "c/blk.bin" +SECTION "prog_mkdir", ROMX[$4000], BANK[22] +ProgMkdir: + INCBIN "c/mkdir.bin" ; ----------------------------------------------------------------------------- ; PROG_SH (bank 3) - the shell, now written in C (c/sh.c): parses >/</| and @@ -182,6 +185,8 @@ ProgramTable:: dw ProgSpin db LOW(BANK(ProgBlk)), HIGH(BANK(ProgBlk)) dw ProgBlk + db LOW(BANK(ProgMkdir)), HIGH(BANK(ProgMkdir)) + dw ProgMkdir ; ----------------------------------------------------------------------------- ; NameTable (ROM0) - command name -> program id, searched by sys_lookup. @@ -228,4 +233,6 @@ NameTable:: db PROG_SPIN db "blk", 0 db PROG_BLK + db "mkdir", 0 + db PROG_MKDIR db 0 diff --git a/src/syscall.asm b/src/syscall.asm index 29dc2b0..5557b5f 100644 --- a/src/syscall.asm +++ b/src/syscall.asm @@ -59,6 +59,9 @@ SyscallTable: dw sys_reap ; 22 REAP dw sys_bfill ; 23 BFILL (debug) dw sys_bpeek ; 24 BPEEK (debug) + dw sys_mkdir ; 25 MKDIR + dw sys_chdir ; 26 CHDIR + dw sys_opendir ; 27 OPENDIR ; ----------------------------------------------------------------------------- sys_nosys: |
