mirror of
https://github.com/hsoft/collapseos.git
synced 2024-12-25 05:28:06 +11:00
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.
This commit is contained in:
parent
d6f5cf5b90
commit
a86738ae84
2
tools/cfspack/.gitignore
vendored
Normal file
2
tools/cfspack/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/cfspack
|
||||||
|
/cfsunpack
|
4
tools/cfspack/Makefile
Normal file
4
tools/cfspack/Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
TARGETS = cfspack
|
||||||
|
|
||||||
|
cfspack: cfspack.c
|
||||||
|
$(CC) -o $@ $^
|
23
tools/cfspack/README.md
Normal file
23
tools/cfspack/README.md
Normal file
@ -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.
|
117
tools/cfspack/cfspack.c
Normal file
117
tools/cfspack/cfspack.c
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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; i<MAX_FN_LEN; i++) {
|
||||||
|
if (i < fnlen) {
|
||||||
|
putchar(fn[i]);
|
||||||
|
} else {
|
||||||
|
putchar(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And the last FN char which is always null
|
||||||
|
putchar(0);
|
||||||
|
char buf[MAX_FILE_SIZE] = {0};
|
||||||
|
rewind(fp);
|
||||||
|
fread(buf, fsize, 1, fp);
|
||||||
|
fclose(fp);
|
||||||
|
fwrite(buf, (blockcount * BLKSIZE) - HEADERSIZE, 1, stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spitdir(char *path, char *prefix)
|
||||||
|
{
|
||||||
|
DIR *dp;
|
||||||
|
struct dirent *ep;
|
||||||
|
|
||||||
|
int prefixlen = strlen(prefix);
|
||||||
|
dp = opendir(path);
|
||||||
|
if (dp == NULL) {
|
||||||
|
fprintf(stderr, "Couldn't open directory.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while (ep = readdir(dp)) {
|
||||||
|
if ((strcmp(ep->d_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, "");
|
||||||
|
}
|
||||||
|
|
2
tools/emul/.gitignore
vendored
2
tools/emul/.gitignore
vendored
@ -2,3 +2,5 @@
|
|||||||
/zasm
|
/zasm
|
||||||
/*-kernel.h
|
/*-kernel.h
|
||||||
/*-user.h
|
/*-user.h
|
||||||
|
/cfsin
|
||||||
|
/cfsout
|
||||||
|
@ -13,7 +13,7 @@ $(KERNEL_HEADERS):
|
|||||||
zasm-user.h: zasm_user.asm
|
zasm-user.h: zasm_user.asm
|
||||||
scas -o - -I ../../apps/zasm $< | ./bin2c.sh USERSPACE | tee $@ > /dev/null
|
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
|
zasm: zasm.c libz80/libz80.o zasm-kernel.h zasm-user.h
|
||||||
$(TARGETS):
|
$(TARGETS):
|
||||||
cc $< libz80/libz80.o -o $@
|
cc $< libz80/libz80.o -o $@
|
||||||
@ -22,6 +22,9 @@ libz80/libz80.o: libz80/z80.c
|
|||||||
make -C libz80/codegen opcodes
|
make -C libz80/codegen opcodes
|
||||||
gcc -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c
|
gcc -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c
|
||||||
|
|
||||||
|
../cfspack/cfspack:
|
||||||
|
make -C ../cfspack
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGETS) $(KERNEL_HEADERS) $(USER_HEADERS)
|
rm -f $(TARGETS) $(KERNEL_HEADERS) $(USER_HEADERS)
|
||||||
|
@ -4,7 +4,11 @@
|
|||||||
#include "libz80/z80.h"
|
#include "libz80/z80.h"
|
||||||
#include "shell-kernel.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:
|
* Memory layout:
|
||||||
*
|
*
|
||||||
@ -15,14 +19,23 @@
|
|||||||
* I/O Ports:
|
* I/O Ports:
|
||||||
*
|
*
|
||||||
* 0 - stdin / stdout
|
* 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
|
// in sync with shell.asm
|
||||||
#define STDIO_PORT 0x00
|
#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 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 int running;
|
||||||
|
|
||||||
static uint8_t io_read(int unused, uint16_t addr)
|
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;
|
running = 0;
|
||||||
}
|
}
|
||||||
return c;
|
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 {
|
} else {
|
||||||
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
|
fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
|
||||||
return 0;
|
return 0;
|
||||||
@ -49,6 +72,14 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
|
|||||||
} else {
|
} else {
|
||||||
putchar(val);
|
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 {
|
} else {
|
||||||
fprintf(stderr, "Out of bounds I/O write: %d / %d\n", addr, val);
|
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()
|
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.
|
// Turn echo off: the shell takes care of its own echoing.
|
||||||
struct termios termInfo;
|
struct termios termInfo;
|
||||||
if (tcgetattr(0, &termInfo) == -1) {
|
if (tcgetattr(0, &termInfo) == -1) {
|
||||||
|
@ -2,16 +2,11 @@
|
|||||||
RAMSTART .equ 0x4000
|
RAMSTART .equ 0x4000
|
||||||
RAMEND .equ 0x5000
|
RAMEND .equ 0x5000
|
||||||
STDIO_PORT .equ 0x00
|
STDIO_PORT .equ 0x00
|
||||||
|
FS_DATA_PORT .equ 0x01
|
||||||
|
FS_SEEKL_PORT .equ 0x02
|
||||||
|
FS_SEEKH_PORT .equ 0x03
|
||||||
|
|
||||||
jr init
|
jp init
|
||||||
|
|
||||||
init:
|
|
||||||
di
|
|
||||||
; setup stack
|
|
||||||
ld hl, RAMEND
|
|
||||||
ld sp, hl
|
|
||||||
call shellInit
|
|
||||||
jp shellLoop
|
|
||||||
|
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
.define STDIO_GETC call emulGetC
|
.define STDIO_GETC call emulGetC
|
||||||
@ -20,19 +15,40 @@ STDIO_RAMSTART .equ RAMEND
|
|||||||
#include "stdio.asm"
|
#include "stdio.asm"
|
||||||
|
|
||||||
BLOCKDEV_RAMSTART .equ STDIO_RAMEND
|
BLOCKDEV_RAMSTART .equ STDIO_RAMEND
|
||||||
BLOCKDEV_COUNT .equ 1
|
BLOCKDEV_COUNT .equ 2
|
||||||
#include "blockdev.asm"
|
#include "blockdev.asm"
|
||||||
; List of devices
|
; List of devices
|
||||||
.dw emulGetC, emulPutC, 0, 0
|
.dw emulGetC, emulPutC, 0, 0
|
||||||
|
.dw fsdevGetC, fsdevPutC, fsdevSeek, fsdevTell
|
||||||
|
|
||||||
#include "blockdev_cmds.asm"
|
#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_GETC call blkGetCW
|
||||||
.define SHELL_IO_PUTC call blkPutC
|
.define SHELL_IO_PUTC call blkPutC
|
||||||
SHELL_EXTRA_CMD_COUNT .equ 2
|
SHELL_EXTRA_CMD_COUNT .equ 6
|
||||||
#include "shell.asm"
|
#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:
|
emulGetC:
|
||||||
; Blocks until a char is returned
|
; Blocks until a char is returned
|
||||||
@ -44,3 +60,24 @@ emulPutC:
|
|||||||
out (STDIO_PORT), a
|
out (STDIO_PORT), a
|
||||||
ret
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user