mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-02 18:10:55 +11:00
Compare commits
No commits in common. "eed67c4768624d9169c190711f037ee725e42f4a" and "e3c885085d1603afece4e0d870d2ca775286c71f" have entirely different histories.
eed67c4768
...
e3c885085d
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1 @@
|
||||
*.o
|
||||
/kernel/user.h
|
||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,3 +1,3 @@
|
||||
[submodule "emul/libz80"]
|
||||
path = emul/libz80
|
||||
[submodule "tools/emul/libz80"]
|
||||
path = tools/emul/libz80
|
||||
url = https://github.com/ggambetta/libz80.git
|
||||
|
10
README.md
10
README.md
@ -34,11 +34,11 @@ path to giving Collapse OS a try.
|
||||
* `recipes`: collection of recipes that assemble parts together on a specific
|
||||
machine.
|
||||
* `doc`: User guide for when you've successfully installed Collapse OS.
|
||||
* `tools`: Tools for working with Collapse OS from "modern" environments. For
|
||||
example, tools for facilitating data upload to a Collapse OS machine
|
||||
through a serial port.
|
||||
* `emul`: Emulated applications, such as zasm and the shell.
|
||||
* `tests`: Automated test suite for the whole project.
|
||||
* `tools`: Tools for working with Collapse OS from "modern" environments. Mostly
|
||||
development tools, but also contains emulated zasm, which is
|
||||
necessary to build Collapse OS from a non-Collapse OS machine.
|
||||
|
||||
Each folder has a README with more details.
|
||||
|
||||
## Status
|
||||
|
||||
|
@ -65,9 +65,12 @@ look like:
|
||||
|
||||
Once this is written, you can build it with `zasm`, which takes code from stdin
|
||||
and spits binary to stdout. Because out code has includes, however, you need
|
||||
to supply zasm with include folders or files. The invocation would look like
|
||||
to supply zasm with a block device containing a CFS containing the files to
|
||||
include. This sounds, compicated, but it's managed by the `tools/zasm.sh` shell
|
||||
script. The invocation would look like (it builds a CFS with the contents of
|
||||
both `kernel/` and `apps/` folders):
|
||||
|
||||
emul/zasm/zasm kernel/ apps/ < glue.asm > collapseos.bin
|
||||
tools/zasm.sh kernel/ apps/ < glue.asm > collapseos.bin
|
||||
|
||||
## Building zasm
|
||||
|
||||
@ -76,7 +79,7 @@ Collapse OS has its own assembler written in z80 assembly. We call it
|
||||
but because it is written in z80 assembler, it needs to be emulated (with
|
||||
[libz80][libz80]).
|
||||
|
||||
So, the first step is to build zasm. Open `emul/README.md` and follow
|
||||
So, the first step is to build zasm. Open `tools/emul/README.md` and follow
|
||||
instructions there.
|
||||
|
||||
## Platform constants
|
||||
@ -159,5 +162,5 @@ and this much depends on the part you select. But if you want a shell, you will
|
||||
usually end it with `basStart`, which never returns.
|
||||
|
||||
[rc2014]: https://rc2014.co.uk/
|
||||
[zasm]: ../emul/README.md
|
||||
[zasm]: ../tools/emul/README.md
|
||||
[libz80]: https://github.com/ggambetta/libz80
|
||||
|
@ -1,10 +1,9 @@
|
||||
TARGET = os.bin
|
||||
BASEDIR = ../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
ZASM = ../../tools/zasm.sh
|
||||
KERNEL = ../../kernel
|
||||
APPS = ../../apps
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
$(TARGET): glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
@ -77,7 +77,7 @@ is decoupled from the ACIA and can get its IO from anything. See comments in
|
||||
|
||||
We only have the shell to build, so it's rather straightforward:
|
||||
|
||||
../../emul/zasm/zasm ../../kernel < glue.asm > os.bin
|
||||
../../tools/zasm.sh ../../kernel < glue.asm > os.bin
|
||||
|
||||
Running `make` will also work.
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
TARGET = os.bin
|
||||
BASEDIR = ../../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
ZASM = ../../../tools/zasm.sh
|
||||
KERNEL = ../../../kernel
|
||||
APPS = ../../../apps
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
$(TARGET): glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
||||
|
@ -33,6 +33,7 @@ jp aciaInt
|
||||
.equ STDIO_PUTC aciaPutC
|
||||
.inc "stdio.asm"
|
||||
|
||||
.inc "lib/args.asm"
|
||||
.equ AT28W_RAMSTART STDIO_RAMEND
|
||||
.inc "at28w/main.asm"
|
||||
|
||||
|
@ -2,10 +2,9 @@ PROGNAME = ps2ctl
|
||||
AVRDUDEMCU ?= t45
|
||||
AVRDUDEARGS ?= -c usbtiny -P usb
|
||||
TARGETS = $(PROGNAME).hex os.bin
|
||||
BASEDIR = ../../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
ZASM = ../../../tools/zasm.sh
|
||||
KERNEL = ../../../kernel
|
||||
APPS = ../../../apps
|
||||
|
||||
# Rules
|
||||
|
||||
@ -15,13 +14,13 @@ all: $(TARGETS)
|
||||
@echo Done!
|
||||
|
||||
send: $(PROGNAME).hex
|
||||
avrdude $(AVRDUDEARGS) -p $(AVRDUDEMCU) -U flash:w:$(PROGNAME).hex
|
||||
avrdude $(AVRDUDEARGS) -p $(AVRDUDEMCU) -U flash:w:$<
|
||||
|
||||
$(PROGNAME).hex: $(PROGNAME).asm
|
||||
avra -o $@ $(PROGNAME).asm
|
||||
avra -o $@ $<
|
||||
|
||||
os.bin: glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGETS) *.eep.hex *.obj os.bin
|
||||
|
@ -1,19 +1,19 @@
|
||||
TARGETS = os.bin cfsin/helo
|
||||
BASEDIR = ../../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
CFSPACK = $(BASEDIR)/tools/cfspack/cfspack
|
||||
TOOLS = ../../../tools
|
||||
ZASM = $(TOOLS)/zasm.sh
|
||||
KERNEL = ../../../kernel
|
||||
APPS = ../../../apps
|
||||
CFSPACK = $(TOOLS)/cfspack/cfspack
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGETS) sdcard.cfs
|
||||
os.bin: glue.asm
|
||||
cfsin/helo: helo.asm
|
||||
$(TARGETS):
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
||||
$(CFSPACK):
|
||||
make -C $(BASEDIR)/tools/cfspack
|
||||
make -C $(TOOLS)/cfspack
|
||||
|
||||
sdcard.cfs: cfsin $(CFSPACK)
|
||||
$(CFSPACK) cfsin > $@
|
||||
$(CFSPACK) $< > $@
|
||||
|
@ -1,20 +1,21 @@
|
||||
SHELLAPPS = zasm sdct memt at28w
|
||||
APPTARGETS = ${SHELLAPPS:%=cfsin/%}
|
||||
CFSTARGETS = $(APPTARGETS) cfsin/user.h
|
||||
BASEDIR = ../../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
CFSPACK = $(BASEDIR)/tools/cfspack/cfspack
|
||||
BASE = ../../..
|
||||
TOOLS = $(BASE)/tools
|
||||
ZASM = $(TOOLS)/zasm.sh
|
||||
KERNEL = $(BASE)/kernel
|
||||
APPS = $(BASE)/apps
|
||||
CFSPACK = $(TOOLS)/cfspack/cfspack
|
||||
|
||||
.PHONY: all
|
||||
all: os.bin sdcard.cfs
|
||||
|
||||
os.bin: glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
||||
$(CFSPACK):
|
||||
make -C $(BASEDIR)/tools/cfspack
|
||||
make -C $(TOOLS)/cfspack
|
||||
|
||||
sdcard.cfs: $(CFSTARGETS) $(CFSPACK)
|
||||
$(CFSPACK) cfsin > $@
|
||||
@ -23,7 +24,7 @@ $(APPTARGETS): $(ZASMBIN)
|
||||
$(ZASM) $(KERNEL) $(APPS) user.h < $(APPS)/${@:cfsin/%=%}/glue.asm > $@
|
||||
|
||||
cfsin/user.h: user.h
|
||||
cp user.h $@
|
||||
cp $< $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
@ -1,10 +1,9 @@
|
||||
TARGET = os.sms
|
||||
BASEDIR = ../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
ZASM = ../../tools/zasm.sh
|
||||
KERNEL = ../../kernel
|
||||
APPS = ../../apps
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
$(TARGET): glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
@ -2,10 +2,9 @@ PROGNAME = ps2ctl
|
||||
AVRDUDEMCU ?= t45
|
||||
AVRDUDEARGS ?= -c usbtiny -P usb
|
||||
TARGETS = $(PROGNAME).hex os.sms
|
||||
BASEDIR = ../../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
ZASM = ../../../tools/zasm.sh
|
||||
KERNEL = ../../../kernel
|
||||
APPS = ../../../apps
|
||||
|
||||
# Rules
|
||||
|
||||
@ -15,13 +14,13 @@ all: $(TARGETS)
|
||||
@echo Done!
|
||||
|
||||
send: $(PROGNAME).hex
|
||||
avrdude $(AVRDUDEARGS) -p $(AVRDUDEMCU) -U flash:w:$(PROGNAME).hex
|
||||
avrdude $(AVRDUDEARGS) -p $(AVRDUDEMCU) -U flash:w:$<
|
||||
|
||||
$(PROGNAME).hex: $(PROGNAME).asm
|
||||
avra -o $@ $(PROGNAME).asm
|
||||
avra -o $@ $<
|
||||
|
||||
os.sms: glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGETS) *.eep.hex *.obj os.bin
|
||||
|
@ -1,21 +1,20 @@
|
||||
BASEDIR = ../../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
ZASM = ../../../tools/zasm.sh
|
||||
KERNEL = ../../../kernel
|
||||
APPS = ../../../apps
|
||||
|
||||
.PHONY: all clean
|
||||
all: os.sms
|
||||
|
||||
# -o value synced with offset in glue.asm
|
||||
ed.bin: $(APPS)/ed/glue.asm
|
||||
$(ZASM) -o 1f $(KERNEL) $(APPS) user.h < $(APPS)/ed/glue.asm > $@
|
||||
$(ZASM) -o 19 $(KERNEL) $(APPS) user.h < $< > $@
|
||||
|
||||
# -o value synced with offset in glue.asm
|
||||
zasm.bin: $(APPS)/zasm/glue.asm
|
||||
$(ZASM) -o 24 $(KERNEL) $(APPS) user.h < $(APPS)/zasm/glue.asm > $@
|
||||
$(ZASM) -o 1d $(KERNEL) $(APPS) user.h < $< > $@
|
||||
|
||||
os.sms: glue.asm ed.bin zasm.bin
|
||||
$(ZASM) $(KERNEL) $(APPS) ed.bin zasm.bin < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) ed.bin zasm.bin < $< > $@
|
||||
|
||||
clean:
|
||||
rm -f os.sms ed.bin zasm.bin
|
||||
|
@ -1,19 +1,18 @@
|
||||
TARGET = os.rom
|
||||
BASEDIR = ../..
|
||||
ZASM = $(BASEDIR)/emul/zasm/zasm
|
||||
KERNEL = $(BASEDIR)/kernel
|
||||
APPS = $(BASEDIR)/apps
|
||||
ZASM = ../../tools/zasm.sh
|
||||
KERNEL = ../../kernel
|
||||
APPS = ../../apps
|
||||
MKTIUPGRADE = mktiupgrade
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
$(TARGET): glue.asm
|
||||
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
|
||||
$(ZASM) $(KERNEL) $(APPS) < $< > $@
|
||||
truncate -s 1M $@
|
||||
|
||||
os.8xu: $(TARGET)
|
||||
$(MKTIUPGRADE) -p -k keys/0A.key -d TI-84+ $(TARGET) $@ 00
|
||||
$(MKTIUPGRADE) -p -k keys/0A.key -d TI-84+ $< $@ 00
|
||||
|
||||
.PHONY: send
|
||||
send: os.8xu
|
||||
tilp -n --calc ti84+ --cable DirectLink os.8xu
|
||||
tilp -n --calc ti84+ --cable DirectLink $<
|
||||
|
14
runtests.sh
14
runtests.sh
@ -4,9 +4,15 @@ git submodule init
|
||||
git submodule update
|
||||
git clean -fxd
|
||||
|
||||
make -C emul
|
||||
make -C tests
|
||||
cd tools/emul
|
||||
make
|
||||
|
||||
cd ../tests
|
||||
make
|
||||
|
||||
# let's try again with an updated zasm
|
||||
make -C emul updatebootstrap all
|
||||
make -C tests
|
||||
cd ../emul
|
||||
make updatebootstrap all
|
||||
|
||||
cd ../tests
|
||||
make
|
||||
|
1
tools/.gitignore
vendored
1
tools/.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
*.o
|
||||
/memdump
|
||||
/blkdump
|
||||
/upload
|
||||
|
@ -1,12 +1,10 @@
|
||||
TARGETS = cfspack cfsunpack
|
||||
|
||||
CFSPACK_OBJS = cfspack.o libcfs.o
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGETS)
|
||||
|
||||
cfspack: $(CFSPACK_OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(CFSPACK_OBJS)
|
||||
cfspack: cfspack.c
|
||||
$(CC) $(CFLAGS) -o $@ cfspack.c
|
||||
|
||||
cfsunpack: cfsunpack.c
|
||||
$(CC) $(CFLAGS) -o $@ cfsunpack.c
|
||||
|
@ -1,10 +0,0 @@
|
||||
#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,7 +7,139 @@
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "cfs.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;
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
|
@ -1,152 +0,0 @@
|
||||
#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 = NULL;
|
||||
|
||||
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()
|
||||
{
|
||||
if (spitstream == NULL) spitstream = stdout;
|
||||
PUTC('C');
|
||||
PUTC('F');
|
||||
PUTC('S');
|
||||
for (int i=0; i<0x20-3; i++) {
|
||||
PUTC(0);
|
||||
}
|
||||
}
|
||||
|
||||
int spitblock(char *fullpath, char *fn)
|
||||
{
|
||||
if (spitstream == NULL) spitstream = stdout;
|
||||
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;
|
||||
}
|
0
emul/.gitignore → tools/emul/.gitignore
vendored
0
emul/.gitignore → tools/emul/.gitignore
vendored
@ -1,28 +1,27 @@
|
||||
CFSPACK_OBJ = ../tools/cfspack/libcfs.o
|
||||
CFSPACK = ../cfspack/cfspack
|
||||
TARGETS = shell/shell zasm/zasm runbin/runbin
|
||||
KERNEL = ../kernel
|
||||
APPS = ../apps
|
||||
KERNEL = ../../kernel
|
||||
APPS = ../../apps
|
||||
ZASMBIN = zasm/zasm
|
||||
AVRABIN = zasm/avra
|
||||
ZASMSH = ../zasm.sh
|
||||
SHELLAPPS = zasm ed
|
||||
SHELLTGTS = ${SHELLAPPS:%=cfsin/%}
|
||||
CFSIN_CONTENTS = $(SHELLTGTS) cfsin/user.h
|
||||
OBJS = emul.o libz80/libz80.o
|
||||
SHELLOBJS = $(OBJS) $(CFSPACK_OBJ)
|
||||
ZASMOBJS = $(SHELLOBJS)
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGETS) $(AVRABIN) $(CFSIN_CONTENTS)
|
||||
|
||||
# -o in sync with SHELL_CODE in shell/glue.asm
|
||||
shell/shell.bin: shell/glue.asm $(ZASMBIN)
|
||||
$(ZASMBIN) $(KERNEL) shell/user.h $(APPS) < shell/glue.asm | tee $@ > /dev/null
|
||||
$(ZASMSH) $(KERNEL) shell/user.h $(APPS) < shell/glue.asm | tee $@ > /dev/null
|
||||
|
||||
shell/shell-bin.h: shell/shell.bin
|
||||
./bin2c.sh KERNEL < shell/shell.bin | tee $@ > /dev/null
|
||||
|
||||
shell/shell: shell/shell.c $(SHELLOBJS) shell/shell-bin.h
|
||||
$(CC) shell/shell.c $(SHELLOBJS) -o $@
|
||||
shell/shell: shell/shell.c $(OBJS) shell/shell-bin.h
|
||||
$(CC) shell/shell.c $(OBJS) -o $@
|
||||
|
||||
zasm/kernel-bin.h: zasm/kernel.bin
|
||||
./bin2c.sh KERNEL < zasm/kernel.bin | tee $@ > /dev/null
|
||||
@ -30,17 +29,17 @@ zasm/kernel-bin.h: zasm/kernel.bin
|
||||
zasm/zasm-bin.h: zasm/zasm.bin
|
||||
./bin2c.sh USERSPACE < zasm/zasm.bin | tee $@ > /dev/null
|
||||
|
||||
$(ZASMBIN): zasm/zasm.c $(ZASMOBJS) zasm/kernel-bin.h zasm/zasm-bin.h
|
||||
$(CC) zasm/zasm.c $(ZASMOBJS) -o $@
|
||||
$(ZASMBIN): zasm/zasm.c $(OBJS) zasm/kernel-bin.h zasm/zasm-bin.h $(CFSPACK)
|
||||
$(CC) zasm/zasm.c $(OBJS) -o $@
|
||||
|
||||
zasm/avra.bin: $(ZASMBIN)
|
||||
$(ZASMBIN) $(KERNEL) $(APPS) zasm/user.h < $(APPS)/zasm/gluea.asm > $@
|
||||
$(ZASMSH) $(KERNEL) $(APPS) zasm/user.h < $(APPS)/zasm/gluea.asm > $@
|
||||
|
||||
zasm/avra-bin.h: zasm/avra.bin
|
||||
./bin2c.sh USERSPACE < zasm/avra.bin | tee $@ > /dev/null
|
||||
|
||||
$(AVRABIN): zasm/zasm.c $(ZASMOBJS) zasm/kernel-bin.h zasm/avra-bin.h
|
||||
$(CC) -D AVRA zasm/zasm.c $(ZASMOBJS) -o $@
|
||||
$(AVRABIN): zasm/zasm.c $(OBJS) zasm/kernel-bin.h zasm/avra-bin.h
|
||||
$(CC) -D AVRA zasm/zasm.c $(OBJS) -o $@
|
||||
|
||||
runbin/runbin: runbin/runbin.c $(OBJS)
|
||||
$(CC) runbin/runbin.c $(OBJS) -o $@
|
||||
@ -52,20 +51,20 @@ libz80/libz80.o: libz80/z80.c
|
||||
emul.o: emul.c
|
||||
$(CC) -c -o emul.o emul.c
|
||||
|
||||
$(CFSPACK_OBJ): ${@:%.o=%.c}
|
||||
$(MAKE) -C ../tools/cfspack
|
||||
$(CFSPACK):
|
||||
$(MAKE) -C ../cfspack
|
||||
|
||||
# -o in sync with USER_CODE in shell/user.h
|
||||
$(SHELLTGTS): $(ZASMBIN)
|
||||
$(ZASMBIN) -o 42 $(KERNEL) $(APPS) shell/user.h < $(APPS)/${@:cfsin/%=%}/glue.asm > $@
|
||||
$(ZASMSH) -o 42 $(KERNEL) $(APPS) shell/user.h < $(APPS)/${@:cfsin/%=%}/glue.asm > $@
|
||||
|
||||
cfsin/user.h: shell/user.h
|
||||
cp shell/user.h $@
|
||||
|
||||
.PHONY: updatebootstrap
|
||||
updatebootstrap: $(ZASMBIN)
|
||||
$(ZASMBIN) $(KERNEL) < zasm/glue.asm > zasm/kernel.bin
|
||||
$(ZASMBIN) $(KERNEL) $(APPS) zasm/user.h < $(APPS)/zasm/glue.asm > zasm/zasm.bin
|
||||
$(ZASMSH) $(KERNEL) < zasm/glue.asm > zasm/kernel.bin
|
||||
$(ZASMSH) $(KERNEL) $(APPS) zasm/user.h < $(APPS)/zasm/glue.asm > zasm/zasm.bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
@ -8,7 +8,7 @@ emulator.
|
||||
First, make sure that the `libz80` git submodule is checked out. If not, run
|
||||
`git submodule init && git submodule update`.
|
||||
|
||||
After that, you can run `make` and it builds all applications.
|
||||
After that, you can run `make` and it builds all tools.
|
||||
|
||||
## shell
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <termios.h>
|
||||
#include "../emul.h"
|
||||
#include "shell-bin.h"
|
||||
#include "../../tools/cfspack/cfs.h"
|
||||
|
||||
/* Collapse OS shell with filesystem
|
||||
*
|
||||
@ -139,7 +138,21 @@ int main(int argc, char *argv[])
|
||||
fprintf(stderr, "Can't open %s\n", optarg);
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr, "Initializing filesystem from %s\n", optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usage: shell [-f fsdev]\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Setup fs blockdev
|
||||
if (fp == NULL) {
|
||||
fp = popen("../cfspack/cfspack cfsin", "r");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Can't initialize filesystem. Leaving blank.\n");
|
||||
}
|
||||
}
|
||||
if (fp != NULL) {
|
||||
fprintf(stderr, "Initializing filesystem\n");
|
||||
int i = 0;
|
||||
int c;
|
||||
while ((c = fgetc(fp)) != EOF && i < MAX_FSDEV_SIZE) {
|
||||
@ -150,22 +163,8 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
pclose(fp);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usage: shell [-f fsdev]\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Setup fs blockdev
|
||||
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");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
bool tty = isatty(fileno(stdin));
|
||||
struct termios termInfo;
|
||||
if (tty) {
|
@ -1,10 +1,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include "../emul.h"
|
||||
#include "../../tools/cfspack/cfs.h"
|
||||
#include "kernel-bin.h"
|
||||
#ifdef AVRA
|
||||
#include "avra-bin.h"
|
||||
@ -54,7 +51,6 @@
|
||||
// By default, we don't spit what zasm prints. Too noisy. Define VERBOSE if
|
||||
// you want to spit this content to stderr.
|
||||
//#define VERBOSE
|
||||
#define MAX_FSDEV_SIZE 0x80000
|
||||
|
||||
// STDIN buffer, allows us to seek and tell
|
||||
static uint8_t inpt[STDIN_BUFSIZE];
|
||||
@ -62,7 +58,8 @@ static int inpt_size;
|
||||
static int inpt_ptr;
|
||||
static uint8_t middle_of_seek_tell = 0;
|
||||
|
||||
static uint8_t fsdev[MAX_FSDEV_SIZE] = {0};
|
||||
static uint8_t fsdev[0x80000] = {0};
|
||||
static uint32_t fsdev_size = 0;
|
||||
static uint32_t fsdev_ptr = 0;
|
||||
static uint8_t fsdev_seek_tell_cnt = 0;
|
||||
|
||||
@ -91,7 +88,7 @@ static uint8_t iord_stdin_seek()
|
||||
|
||||
static uint8_t iord_fsdata()
|
||||
{
|
||||
if (fsdev_ptr < MAX_FSDEV_SIZE) {
|
||||
if (fsdev_ptr < fsdev_size) {
|
||||
return fsdev[fsdev_ptr++];
|
||||
} else {
|
||||
return 0;
|
||||
@ -102,7 +99,7 @@ static uint8_t iord_fsseek()
|
||||
{
|
||||
if (fsdev_seek_tell_cnt != 0) {
|
||||
return fsdev_seek_tell_cnt;
|
||||
} else if (fsdev_ptr >= MAX_FSDEV_SIZE) {
|
||||
} else if (fsdev_ptr >= fsdev_size) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
@ -133,7 +130,7 @@ static void iowr_stdin_seek(uint8_t val)
|
||||
|
||||
static void iowr_fsdata(uint8_t val)
|
||||
{
|
||||
if (fsdev_ptr < MAX_FSDEV_SIZE) {
|
||||
if (fsdev_ptr < fsdev_size) {
|
||||
fsdev[fsdev_ptr++] = val;
|
||||
}
|
||||
}
|
||||
@ -162,51 +159,12 @@ static void iowr_stderr(uint8_t val)
|
||||
#endif
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: zasm [-o org] [include-dir-or-file...] < source > binary\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *init_org = "00";
|
||||
while (1) {
|
||||
int c = getopt(argc, argv, "o:");
|
||||
if (c < 0) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
case 'o':
|
||||
init_org = optarg;
|
||||
if (strlen(init_org) != 2) {
|
||||
fprintf(stderr, "Initial org must be a two-character hex string");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
if (argc > 3) {
|
||||
fprintf(stderr, "Too many args\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (argc-optind > 0) {
|
||||
FILE *fp = fmemopen(fsdev, MAX_FSDEV_SIZE, "w");
|
||||
set_spit_stream(fp);
|
||||
char *patterns[4] = {"*.h", "*.asm", "*.bin", 0};
|
||||
for (int i=optind; i<argc; i++) {
|
||||
int res;
|
||||
if (is_regular_file(argv[i])) {
|
||||
// special case: just one file
|
||||
res = spitblock(argv[i], basename(argv[i]));
|
||||
} else {
|
||||
res = spitdir(argv[i], "", patterns);
|
||||
}
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "Error while building the include CFS.\n");
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
Machine *m = emul_init();
|
||||
m->iord[STDIO_PORT] = iord_stdio;
|
||||
m->iord[STDIN_SEEK_PORT] = iord_stdin_seek;
|
||||
@ -224,9 +182,31 @@ int main(int argc, char *argv[])
|
||||
for (int i=0; i<sizeof(USERSPACE); i++) {
|
||||
m->mem[i+USER_CODE] = USERSPACE[i];
|
||||
}
|
||||
char *init_org = "00";
|
||||
if (argc >= 2) {
|
||||
init_org = argv[1];
|
||||
if (strlen(init_org) != 2) {
|
||||
fprintf(stderr, "Initial org must be a two-character hex string");
|
||||
}
|
||||
}
|
||||
// glue.asm knows that it needs to fetch these arguments at this address.
|
||||
m->mem[0xff00] = init_org[0];
|
||||
m->mem[0xff01] = init_org[1];
|
||||
fsdev_size = 0;
|
||||
if (argc == 3) {
|
||||
FILE *fp = fopen(argv[2], "r");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Can't open file %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
int c = fgetc(fp);
|
||||
while (c != EOF) {
|
||||
fsdev[fsdev_size] = c;
|
||||
fsdev_size++;
|
||||
c = fgetc(fp);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
// read stdin in buffer
|
||||
inpt_size = 0;
|
||||
inpt_ptr = 0;
|
@ -1,4 +1,5 @@
|
||||
EMULDIR = ../emul
|
||||
CFSPACK = ../cfspack/cfspack
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
@ -7,3 +8,10 @@ run:
|
||||
cd zasm && ./runtests.sh
|
||||
cd avra && ./runtests.sh
|
||||
cd shell && ./runtests.sh
|
||||
|
||||
$(CFSPACK):
|
||||
$(MAKE) -C ../cfspack
|
||||
|
||||
.PHONY: cfs
|
||||
cfs: $(CFSPACK)
|
||||
$(CFSPACK) shell/cfsin > shell/test.cfs
|
@ -1,12 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
ZASM=../../emul/zasm/avra
|
||||
AVRINC=../../avr
|
||||
ZASM=../../zasm.sh
|
||||
AVRINC=../../../avr
|
||||
|
||||
cmpas() {
|
||||
FN=$1
|
||||
EXPECTED=$(xxd ${FN%.*}.expected)
|
||||
ACTUAL=$(cat ${FN} | "${ZASM}" "${AVRINC}" | xxd)
|
||||
ACTUAL=$(cat ${FN} | "${ZASM}" -a "${AVRINC}" | xxd)
|
||||
if [ "$ACTUAL" = "$EXPECTED" ]; then
|
||||
echo ok
|
||||
else
|
@ -1,7 +1,7 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
EMULDIR=../../emul
|
||||
SHELL="${EMULDIR}/shell/shell"
|
||||
SHELL=../../emul/shell/shell
|
||||
|
||||
replay() {
|
||||
fn=$1
|
@ -2,9 +2,10 @@
|
||||
set -e
|
||||
# TODO: find POSIX substitute to that PIPESTATUS thing
|
||||
|
||||
BASE=../..
|
||||
ZASM="${BASE}/emul/zasm/zasm"
|
||||
RUNBIN="${BASE}/emul/runbin/runbin"
|
||||
BASE=../../..
|
||||
TOOLS=../..
|
||||
ZASM="${TOOLS}/zasm.sh"
|
||||
RUNBIN="${TOOLS}/emul/runbin/runbin"
|
||||
KERNEL="${BASE}/kernel"
|
||||
APPS="${BASE}/apps"
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# no "set -e" because we test errors
|
||||
|
||||
ZASM=../../emul/zasm/zasm
|
||||
ZASM=../../zasm.sh
|
||||
|
||||
chkerr() {
|
||||
echo "Check that '$1' results in error $2"
|
@ -1,9 +1,9 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
BASE=../..
|
||||
KERNEL="${BASE}/kernel"
|
||||
APPS="${BASE}/apps"
|
||||
ZASM="${BASE}/emul/zasm/zasm"
|
||||
KERNEL=../../../kernel
|
||||
APPS=../../../apps
|
||||
ZASM=../../zasm.sh
|
||||
ASMFILE=${APPS}/zasm/instr.asm
|
||||
|
||||
cmpas() {
|
||||
FN=$1
|
44
tools/zasm.sh
Executable file
44
tools/zasm.sh
Executable file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Calls tools/emul/zasm/zasm in a convenient manner by wrapping specified
|
||||
# paths to include in a single CFS file and then pass that file to zasm.
|
||||
# Additionally, it takes a "-o" argument to set the initial ".org" of the
|
||||
# binary. For example, "zasm.sh -o 4f < foo.asm" assembles foo.asm as if it
|
||||
# started with the line ".org 0x4f00".
|
||||
|
||||
# The -a flag makes us switch to the AVR assembler
|
||||
|
||||
# readlink -f doesn't work with macOS's implementation
|
||||
# so, if we can't get readlink -f to work, try python with a realpath implementation
|
||||
ABS_PATH=$(readlink -f "$0" || python -c "import os; print(os.path.realpath('$0'))")
|
||||
DIR=$(dirname "${ABS_PATH}")
|
||||
ZASMBIN="${DIR}/emul/zasm/zasm"
|
||||
|
||||
usage() { echo "Usage: $0 [-a] [-o <hexorg>] <paths-to-include>..." 1>&2; exit 1; }
|
||||
|
||||
org='00'
|
||||
while getopts ":ao:" opt; do
|
||||
case "${opt}" in
|
||||
a)
|
||||
ZASMBIN="${DIR}/emul/zasm/avra"
|
||||
;;
|
||||
o)
|
||||
org=${OPTARG}
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
# wrapper around ./emul/zasm/zasm that prepares includes CFS prior to call
|
||||
CFSPACK="${DIR}/cfspack/cfspack"
|
||||
INCCFS=$(mktemp)
|
||||
|
||||
"${CFSPACK}" -p "*.h" -p "*.asm" -p "*.bin" "$@" > "${INCCFS}"
|
||||
|
||||
"${ZASMBIN}" "${org}" "${INCCFS}"
|
||||
RES=$?
|
||||
rm "${INCCFS}"
|
||||
exit $RES
|
Loading…
Reference in New Issue
Block a user