@@ -1 +1,2 @@ | |||||
*.o | |||||
/kernel/user.h | /kernel/user.h |
@@ -1,4 +1,4 @@ | |||||
CFSPACK = ../tools/cfspack/cfspack | |||||
CFSPACK_OBJ = ../tools/cfspack/libcfs.o | |||||
TARGETS = shell/shell zasm/zasm runbin/runbin | TARGETS = shell/shell zasm/zasm runbin/runbin | ||||
KERNEL = ../kernel | KERNEL = ../kernel | ||||
APPS = ../apps | APPS = ../apps | ||||
@@ -9,6 +9,7 @@ SHELLAPPS = zasm ed | |||||
SHELLTGTS = ${SHELLAPPS:%=cfsin/%} | SHELLTGTS = ${SHELLAPPS:%=cfsin/%} | ||||
CFSIN_CONTENTS = $(SHELLTGTS) cfsin/user.h | CFSIN_CONTENTS = $(SHELLTGTS) cfsin/user.h | ||||
OBJS = emul.o libz80/libz80.o | OBJS = emul.o libz80/libz80.o | ||||
SHELLOBJS = $(OBJS) $(CFSPACK_OBJ) | |||||
.PHONY: all | .PHONY: all | ||||
all: $(TARGETS) $(AVRABIN) $(CFSIN_CONTENTS) | all: $(TARGETS) $(AVRABIN) $(CFSIN_CONTENTS) | ||||
@@ -20,8 +21,8 @@ shell/shell.bin: shell/glue.asm $(ZASMBIN) | |||||
shell/shell-bin.h: shell/shell.bin | shell/shell-bin.h: shell/shell.bin | ||||
./bin2c.sh KERNEL < shell/shell.bin | tee $@ > /dev/null | ./bin2c.sh KERNEL < shell/shell.bin | tee $@ > /dev/null | ||||
shell/shell: shell/shell.c $(OBJS) shell/shell-bin.h | |||||
$(CC) shell/shell.c $(OBJS) -o $@ | |||||
shell/shell: shell/shell.c $(SHELLOBJS) shell/shell-bin.h | |||||
$(CC) shell/shell.c $(SHELLOBJS) -o $@ | |||||
zasm/kernel-bin.h: zasm/kernel.bin | zasm/kernel-bin.h: zasm/kernel.bin | ||||
./bin2c.sh KERNEL < zasm/kernel.bin | tee $@ > /dev/null | ./bin2c.sh KERNEL < zasm/kernel.bin | tee $@ > /dev/null | ||||
@@ -4,6 +4,7 @@ | |||||
#include <termios.h> | #include <termios.h> | ||||
#include "../emul.h" | #include "../emul.h" | ||||
#include "shell-bin.h" | #include "shell-bin.h" | ||||
#include "../../tools/cfspack/cfs.h" | |||||
/* Collapse OS shell with filesystem | /* Collapse OS shell with filesystem | ||||
* | * | ||||
@@ -138,6 +139,17 @@ int main(int argc, char *argv[]) | |||||
fprintf(stderr, "Can't open %s\n", optarg); | fprintf(stderr, "Can't open %s\n", optarg); | ||||
return 1; | return 1; | ||||
} | } | ||||
fprintf(stderr, "Initializing filesystem from %s\n", optarg); | |||||
int i = 0; | |||||
int c; | |||||
while ((c = fgetc(fp)) != EOF && i < MAX_FSDEV_SIZE) { | |||||
fsdev[i++] = c & 0xff; | |||||
} | |||||
if (i == MAX_FSDEV_SIZE) { | |||||
fprintf(stderr, "Filesytem image too large.\n"); | |||||
return 1; | |||||
} | |||||
pclose(fp); | |||||
break; | break; | ||||
default: | default: | ||||
fprintf(stderr, "Usage: shell [-f fsdev]\n"); | fprintf(stderr, "Usage: shell [-f fsdev]\n"); | ||||
@@ -146,25 +158,14 @@ int main(int argc, char *argv[]) | |||||
} | } | ||||
// Setup fs blockdev | // Setup fs blockdev | ||||
if (fp == NULL) { | if (fp == NULL) { | ||||
fp = popen("../cfspack/cfspack cfsin", "r"); | |||||
if (fp == NULL) { | |||||
fprintf(stderr, "Initializing filesystem from cfsin\n"); | |||||
fp = fmemopen(fsdev, MAX_FSDEV_SIZE, "w"); | |||||
set_spit_stream(fp); | |||||
if (spitdir("cfsin", "", NULL) != 0) { | |||||
fprintf(stderr, "Can't initialize filesystem. Leaving blank.\n"); | fprintf(stderr, "Can't initialize filesystem. Leaving blank.\n"); | ||||
} | } | ||||
fclose(fp); | |||||
} | } | ||||
if (fp != NULL) { | |||||
fprintf(stderr, "Initializing filesystem\n"); | |||||
int i = 0; | |||||
int c; | |||||
while ((c = fgetc(fp)) != EOF && i < MAX_FSDEV_SIZE) { | |||||
fsdev[i++] = c & 0xff; | |||||
} | |||||
if (i == MAX_FSDEV_SIZE) { | |||||
fprintf(stderr, "Filesytem image too large.\n"); | |||||
return 1; | |||||
} | |||||
pclose(fp); | |||||
} | |||||
bool tty = isatty(fileno(stdin)); | bool tty = isatty(fileno(stdin)); | ||||
struct termios termInfo; | struct termios termInfo; | ||||
if (tty) { | if (tty) { | ||||
@@ -1,4 +1,3 @@ | |||||
*.o | |||||
/memdump | /memdump | ||||
/blkdump | /blkdump | ||||
/upload | /upload |
@@ -1,10 +1,12 @@ | |||||
TARGETS = cfspack cfsunpack | TARGETS = cfspack cfsunpack | ||||
CFSPACK_OBJS = cfspack.o libcfs.o | |||||
.PHONY: all | .PHONY: all | ||||
all: $(TARGETS) | all: $(TARGETS) | ||||
cfspack: cfspack.c | |||||
$(CC) $(CFLAGS) -o $@ cfspack.c | |||||
cfspack: $(CFSPACK_OBJS) | |||||
$(CC) $(LDFLAGS) -o $@ $(CFSPACK_OBJS) | |||||
cfsunpack: cfsunpack.c | cfsunpack: cfsunpack.c | ||||
$(CC) $(CFLAGS) -o $@ cfsunpack.c | $(CC) $(CFLAGS) -o $@ cfsunpack.c | ||||
@@ -0,0 +1,10 @@ | |||||
#define BLKSIZE 0x100 | |||||
#define HEADERSIZE 0x20 | |||||
#define MAX_FN_LEN 25 // 26 - null char | |||||
#define MAX_FILE_SIZE (BLKSIZE * 0x100) - HEADERSIZE | |||||
void set_spit_stream(FILE *stream); | |||||
int is_regular_file(char *path); | |||||
void spitempty(); | |||||
int spitblock(char *fullpath, char *fn); | |||||
int spitdir(char *path, char *prefix, char **patterns); |
@@ -7,139 +7,7 @@ | |||||
#include <libgen.h> | #include <libgen.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#define BLKSIZE 0x100 | |||||
#define HEADERSIZE 0x20 | |||||
#define MAX_FN_LEN 25 // 26 - null char | |||||
#define MAX_FILE_SIZE (BLKSIZE * 0x100) - HEADERSIZE | |||||
int is_regular_file(char *path) | |||||
{ | |||||
struct stat path_stat; | |||||
stat(path, &path_stat); | |||||
return S_ISREG(path_stat.st_mode); | |||||
} | |||||
void spitempty() | |||||
{ | |||||
putchar('C'); | |||||
putchar('F'); | |||||
putchar('S'); | |||||
for (int i=0; i<0x20-3; i++) { | |||||
putchar(0); | |||||
} | |||||
} | |||||
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 + HEADERSIZE) { | |||||
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, char **patterns) | |||||
{ | |||||
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, patterns); | |||||
if (r != 0) { | |||||
return r; | |||||
} | |||||
} else { | |||||
char **p = patterns; | |||||
// if we have no pattern, we match all | |||||
int matches = (*p) == NULL ? 1 : 0; | |||||
while (*p) { | |||||
if (fnmatch(*p, ep->d_name, 0) == 0) { | |||||
matches = 1; | |||||
break; | |||||
} | |||||
p++; | |||||
} | |||||
if (!matches) { | |||||
continue; | |||||
} | |||||
int r = spitblock(fullpath, newprefix); | |||||
if (r != 0) { | |||||
return r; | |||||
} | |||||
} | |||||
} | |||||
closedir(dp); | |||||
return 0; | |||||
} | |||||
#include "cfs.h" | |||||
void usage() | void usage() | ||||
{ | { | ||||
@@ -0,0 +1,150 @@ | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <unistd.h> | |||||
#include <dirent.h> | |||||
#include <string.h> | |||||
#include <fnmatch.h> | |||||
#include <libgen.h> | |||||
#include <sys/stat.h> | |||||
#include "cfs.h" | |||||
#define PUTC(c) putc(c, spitstream) | |||||
static FILE *spitstream = stdout; | |||||
void set_spit_stream(FILE *stream) | |||||
{ | |||||
spitstream = stream; | |||||
} | |||||
int is_regular_file(char *path) | |||||
{ | |||||
struct stat path_stat; | |||||
stat(path, &path_stat); | |||||
return S_ISREG(path_stat.st_mode); | |||||
} | |||||
void spitempty() | |||||
{ | |||||
PUTC('C'); | |||||
PUTC('F'); | |||||
PUTC('S'); | |||||
for (int i=0; i<0x20-3; i++) { | |||||
PUTC(0); | |||||
} | |||||
} | |||||
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 + HEADERSIZE) { | |||||
blockcount++; | |||||
} | |||||
PUTC('C'); | |||||
PUTC('F'); | |||||
PUTC('S'); | |||||
PUTC(blockcount); | |||||
// file size is little endian | |||||
PUTC(fsize & 0xff); | |||||
PUTC((fsize >> 8) & 0xff); | |||||
int fnlen = strlen(fn); | |||||
for (int i=0; i<MAX_FN_LEN; i++) { | |||||
if (i < fnlen) { | |||||
PUTC(fn[i]); | |||||
} else { | |||||
PUTC(0); | |||||
} | |||||
} | |||||
// And the last FN char which is always null | |||||
PUTC(0); | |||||
char buf[MAX_FILE_SIZE] = {0}; | |||||
rewind(fp); | |||||
fread(buf, fsize, 1, fp); | |||||
fclose(fp); | |||||
fwrite(buf, (blockcount * BLKSIZE) - HEADERSIZE, 1, spitstream); | |||||
fflush(spitstream); | |||||
return 0; | |||||
} | |||||
int spitdir(char *path, char *prefix, char **patterns) | |||||
{ | |||||
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, patterns); | |||||
if (r != 0) { | |||||
return r; | |||||
} | |||||
} else { | |||||
char **p = patterns; | |||||
// if we have no pattern, we match all | |||||
if (p && *p) { | |||||
int matches = 0; | |||||
while (*p) { | |||||
if (fnmatch(*p, ep->d_name, 0) == 0) { | |||||
matches = 1; | |||||
break; | |||||
} | |||||
p++; | |||||
} | |||||
if (!matches) { | |||||
continue; | |||||
} | |||||
} | |||||
int r = spitblock(fullpath, newprefix); | |||||
if (r != 0) { | |||||
return r; | |||||
} | |||||
} | |||||
} | |||||
closedir(dp); | |||||
return 0; | |||||
} |