From a86738ae84a502360e161d262f924d10878bed2a Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Sun, 12 May 2019 11:20:31 -0400 Subject: [PATCH] tools/emul/shell: add filesystem support With the help of the newly-introduced cfspack tool, we can mount a filesystem in our emulated shell and play around. Read-only for now. Unpacking incoming. --- tools/cfspack/.gitignore | 2 + tools/cfspack/Makefile | 4 ++ tools/cfspack/README.md | 23 ++++++++ tools/cfspack/cfspack.c | 117 +++++++++++++++++++++++++++++++++++++++ tools/emul/.gitignore | 2 + tools/emul/Makefile | 5 +- tools/emul/shell.c | 54 +++++++++++++++++- tools/emul/shell_.asm | 63 ++++++++++++++++----- 8 files changed, 253 insertions(+), 17 deletions(-) create mode 100644 tools/cfspack/.gitignore create mode 100644 tools/cfspack/Makefile create mode 100644 tools/cfspack/README.md create mode 100644 tools/cfspack/cfspack.c diff --git a/tools/cfspack/.gitignore b/tools/cfspack/.gitignore new file mode 100644 index 0000000..4c6cac5 --- /dev/null +++ b/tools/cfspack/.gitignore @@ -0,0 +1,2 @@ +/cfspack +/cfsunpack diff --git a/tools/cfspack/Makefile b/tools/cfspack/Makefile new file mode 100644 index 0000000..6468dc1 --- /dev/null +++ b/tools/cfspack/Makefile @@ -0,0 +1,4 @@ +TARGETS = cfspack + +cfspack: cfspack.c + $(CC) -o $@ $^ diff --git a/tools/cfspack/README.md b/tools/cfspack/README.md new file mode 100644 index 0000000..7069eeb --- /dev/null +++ b/tools/cfspack/README.md @@ -0,0 +1,23 @@ +# cfspack + +A tool/library to pack a directory into a CFS blob and unpack a CFS blob into +a directory. + +## Usage + +To pack a directory into a CFS blob, run: + + cfspack /path/to/directory + +The blob is spit to stdout. If there are subdirectories, they will be prefixes +to the filenames under it. + +The program errors out if a file name is too long (> 26 bytes) or too big +(> 0x10000 - 0x20 bytes). + +To unpack a blob to a directory: + + cfsunpack /path/to/dest < blob + +If destination exists, files are created alongside existing ones. If a file to +unpack already exists, it is overwritten. diff --git a/tools/cfspack/cfspack.c b/tools/cfspack/cfspack.c new file mode 100644 index 0000000..e65d906 --- /dev/null +++ b/tools/cfspack/cfspack.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#define BLKSIZE 0x100 +#define HEADERSIZE 0x20 +#define MAX_FN_LEN 25 // 26 - null char +#define MAX_FILE_SIZE (BLKSIZE * 0x100) - HEADERSIZE + +int spitblock(char *fullpath, char *fn) +{ + FILE *fp = fopen(fullpath, "r"); + fseek(fp, 0, SEEK_END); + long fsize = ftell(fp); + if (fsize > MAX_FILE_SIZE) { + fclose(fp); + fprintf(stderr, "File too big: %s %ld\n", fullpath, fsize); + return 1; + } + /* Compute block count. + * We always have at least one, which contains 0x100 bytes - 0x20, which is + * metadata. The rest of the blocks have a steady 0x100. + */ + unsigned char blockcount = 1; + int fsize2 = fsize - (BLKSIZE - HEADERSIZE); + if (fsize2 > 0) { + blockcount += (fsize2 / BLKSIZE); + } + if (blockcount * BLKSIZE < fsize) { + blockcount++; + } + putchar('C'); + putchar('F'); + putchar('S'); + putchar(blockcount); + // file size is little endian + putchar(fsize & 0xff); + putchar((fsize >> 8) & 0xff); + int fnlen = strlen(fn); + for (int i=0; id_name, ".") == 0) || strcmp(ep->d_name, "..") == 0) { + continue; + } + if (ep->d_type != DT_DIR && ep->d_type != DT_REG) { + fprintf(stderr, "Only regular file or directories are supported\n"); + return 1; + } + int slen = strlen(ep->d_name); + if (prefixlen + slen> MAX_FN_LEN) { + fprintf(stderr, "Filename too long: %s/%s\n", prefix, ep->d_name); + return 1; + } + char fullpath[0x1000]; + strcpy(fullpath, path); + strcat(fullpath, "/"); + strcat(fullpath, ep->d_name); + char newprefix[MAX_FN_LEN]; + strcpy(newprefix, prefix); + if (prefixlen > 0) { + strcat(newprefix, "/"); + } + strcat(newprefix, ep->d_name); + if (ep->d_type == DT_DIR) { + int r = spitdir(fullpath, newprefix); + if (r != 0) { + return r; + } + } else { + int r = spitblock(fullpath, newprefix); + if (r != 0) { + return r; + } + } + } + closedir(dp); + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + fprintf(stderr, "Usage: cfspack /path/to/dir\n"); + return 1; + } + char *srcpath = argv[1]; + return spitdir(srcpath, ""); +} + diff --git a/tools/emul/.gitignore b/tools/emul/.gitignore index 397648f..ca936d9 100644 --- a/tools/emul/.gitignore +++ b/tools/emul/.gitignore @@ -2,3 +2,5 @@ /zasm /*-kernel.h /*-user.h +/cfsin +/cfsout diff --git a/tools/emul/Makefile b/tools/emul/Makefile index f1d164f..52804eb 100644 --- a/tools/emul/Makefile +++ b/tools/emul/Makefile @@ -13,7 +13,7 @@ $(KERNEL_HEADERS): zasm-user.h: zasm_user.asm scas -o - -I ../../apps/zasm $< | ./bin2c.sh USERSPACE | tee $@ > /dev/null -shell: shell.c libz80/libz80.o shell-kernel.h +shell: shell.c libz80/libz80.o shell-kernel.h ../cfspack/cfspack zasm: zasm.c libz80/libz80.o zasm-kernel.h zasm-user.h $(TARGETS): cc $< libz80/libz80.o -o $@ @@ -22,6 +22,9 @@ libz80/libz80.o: libz80/z80.c make -C libz80/codegen opcodes gcc -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c +../cfspack/cfspack: + make -C ../cfspack + .PHONY: clean clean: rm -f $(TARGETS) $(KERNEL_HEADERS) $(USER_HEADERS) diff --git a/tools/emul/shell.c b/tools/emul/shell.c index ae544c4..0f7e74d 100644 --- a/tools/emul/shell.c +++ b/tools/emul/shell.c @@ -4,7 +4,11 @@ #include "libz80/z80.h" #include "shell-kernel.h" -/* Collapse OS vanilla shell +/* Collapse OS shell with filesystem + * + * On startup, if "cfsin" directory exists, it packs it as a afke block device + * and loads it in. Upon halting, unpcks the contents of that block device in + * "cfsout" directory. * * Memory layout: * @@ -15,14 +19,23 @@ * I/O Ports: * * 0 - stdin / stdout + * 1 - Filesystem blockdev data read/write. Reading and writing to it advances + * the pointer. + * 2 - Filesystem blockdev seek / tell. Low byte + * 3 - Filesystem blockdev seek / tell. High byte */ // in sync with shell.asm #define STDIO_PORT 0x00 -#define STDIN_ST_PORT 0x01 +#define FS_DATA_PORT 0x01 +#define FS_SEEKL_PORT 0x02 +#define FS_SEEKH_PORT 0x03 static Z80Context cpu; -static uint8_t mem[0xffff]; +static uint8_t mem[0xffff] = {0}; +static uint8_t fsdev[0xffff] = {0}; +static uint16_t fsdev_size = 0; +static uint16_t fsdev_ptr = 0; static int running; static uint8_t io_read(int unused, uint16_t addr) @@ -34,6 +47,16 @@ static uint8_t io_read(int unused, uint16_t addr) running = 0; } return c; + } else if (addr == FS_DATA_PORT) { + if (fsdev_ptr < fsdev_size) { + return fsdev[fsdev_ptr++]; + } else { + return 0; + } + } else if (addr == FS_SEEKL_PORT) { + return fsdev_ptr && 0xff; + } else if (addr == FS_SEEKH_PORT) { + return fsdev_ptr >> 8; } else { fprintf(stderr, "Out of bounds I/O read: %d\n", addr); return 0; @@ -49,6 +72,14 @@ static void io_write(int unused, uint16_t addr, uint8_t val) } else { putchar(val); } + } else if (addr == FS_DATA_PORT) { + if (fsdev_ptr < fsdev_size) { + fsdev[fsdev_ptr++] = val; + } + } else if (addr == FS_SEEKL_PORT) { + fsdev_ptr = (fsdev_ptr & 0xff00) | val; + } else if (addr == FS_SEEKH_PORT) { + fsdev_ptr = (fsdev_ptr & 0x00ff) | (val << 8); } else { fprintf(stderr, "Out of bounds I/O write: %d / %d\n", addr, val); } @@ -66,6 +97,23 @@ static void mem_write(int unused, uint16_t addr, uint8_t val) int main() { + // Setup fs blockdev + FILE *fp = popen("../cfspack/cfspack cfsin", "r"); + if (fp != NULL) { + printf("Initializing filesystem\n"); + int i = 0; + int c = fgetc(fp); + while (c != EOF) { + fsdev[i] = c & 0xff; + i++; + c = fgetc(fp); + } + fsdev_size = i; + pclose(fp); + } else { + printf("Can't initialize filesystem. Leaving blank.\n"); + } + // Turn echo off: the shell takes care of its own echoing. struct termios termInfo; if (tcgetattr(0, &termInfo) == -1) { diff --git a/tools/emul/shell_.asm b/tools/emul/shell_.asm index 12776e8..ac286c6 100644 --- a/tools/emul/shell_.asm +++ b/tools/emul/shell_.asm @@ -2,16 +2,11 @@ RAMSTART .equ 0x4000 RAMEND .equ 0x5000 STDIO_PORT .equ 0x00 +FS_DATA_PORT .equ 0x01 +FS_SEEKL_PORT .equ 0x02 +FS_SEEKH_PORT .equ 0x03 -jr init - -init: - di - ; setup stack - ld hl, RAMEND - ld sp, hl - call shellInit - jp shellLoop +jp init #include "core.asm" .define STDIO_GETC call emulGetC @@ -20,19 +15,40 @@ STDIO_RAMSTART .equ RAMEND #include "stdio.asm" BLOCKDEV_RAMSTART .equ STDIO_RAMEND -BLOCKDEV_COUNT .equ 1 +BLOCKDEV_COUNT .equ 2 #include "blockdev.asm" ; List of devices .dw emulGetC, emulPutC, 0, 0 +.dw fsdevGetC, fsdevPutC, fsdevSeek, fsdevTell #include "blockdev_cmds.asm" -SHELL_RAMSTART .equ BLOCKDEV_RAMEND +.equ FS_RAMSTART BLOCKDEV_RAMEND +.equ FS_HANDLE_COUNT 2 +#include "fs.asm" + +SHELL_RAMSTART .equ FS_RAMEND .define SHELL_IO_GETC call blkGetCW .define SHELL_IO_PUTC call blkPutC -SHELL_EXTRA_CMD_COUNT .equ 2 +SHELL_EXTRA_CMD_COUNT .equ 6 #include "shell.asm" -.dw blkBselCmd, blkSeekCmd +.dw blkBselCmd, blkSeekCmd, fsOnCmd, flsCmd, fnewCmd, fdelCmd + +init: + di + ; setup stack + ld hl, RAMEND + ld sp, hl + call fsInit + ld a, 1 ; select fsdev + ld de, BLOCKDEV_GETC + call blkSel + call fsOn + xor a ; select ACIA + ld de, BLOCKDEV_GETC + call blkSel + call shellInit + jp shellLoop emulGetC: ; Blocks until a char is returned @@ -44,3 +60,24 @@ emulPutC: out (STDIO_PORT), a ret +fsdevGetC: + in a, (FS_DATA_PORT) + ret + +fsdevPutC: + out (FS_DATA_PORT), a + ret + +fsdevSeek: + ld a, l + out (FS_SEEKL_PORT), a + ld a, h + out (FS_SEEKH_PORT), a + ret + +fsdevTell: + in a, (FS_SEEKL_PORT) + ld l, a + in a, (FS_SEEKH_PORT) + ld h, a + ret