@@ -24,9 +24,10 @@ tools. | |||||
## Getting started | ## Getting started | ||||
Usage documentation is in-system, so access to documentation requires you to | Usage documentation is in-system, so access to documentation requires you to | ||||
run Collapse OS. Fortunately, doing so in an emulator is easy. | |||||
run Collapse OS. Fortunately, building and running Collapse OS on a POSIX | |||||
environment is easy. | |||||
See `/emul/README.md` for getting an emulated system running. | |||||
See `/cvm/README.md` for instructions. | |||||
Then, run `0 LIST` for an introduction, follow instructions from there. | Then, run `0 LIST` for an introduction, follow instructions from there. | ||||
@@ -0,0 +1,47 @@ | |||||
# C VM | |||||
This is a C implementation of Collapse OS' native words. It allows Collapse OS | |||||
to run natively on any POSIX environment. | |||||
## Requirements | |||||
You need `ncurses` to build the `forth` executable. In debian-based distros, | |||||
it's `libncurses5-dev`. | |||||
## Build | |||||
Running `make` will yield `forth` and `stage` executables. | |||||
## Usage | |||||
To play around Collapse OS, you'll want to run `./forth`. Type `0 LIST` for | |||||
help. | |||||
The program is a curses interface with a limited, fixed size so that it can | |||||
provide a AT-XY interface. | |||||
You can get a REPL by launching the program with [`rlwrap(1)`][rlwrap] like | |||||
this: | |||||
rlwrap -e '' -m -S '> ' ./forth /dev/stdin | |||||
## Problems? | |||||
If the `forth` executable works badly (hangs, spew garbage, etc.), | |||||
it's probably because you've broken your bootstrap binary. It's easy to | |||||
mistakenly break. To verify if you've done that, look at your git status. If | |||||
`forth.bin` is modified, try resetting it and then run `make clean all`. Things | |||||
should go better afterwards. | |||||
A modified `blkfs` can also break things (although even with a completely broken | |||||
blkfs, you should still get to prompt), you might want to run `make pack` to | |||||
ensure that the `blkfs` file is in sync with the contents of the `blk/` folder. | |||||
If that doesn't work, there's also the nuclear option of `git reset --hard` | |||||
and `git clean -fxd`. | |||||
If that still doesn't work, it might be because the current commit you're on | |||||
is broken, but that is rather rare: the repo on Github is plugged on Travis | |||||
and it checks that everything is smooth. | |||||
[rlwrap]: https://linux.die.net/man/1/rlwrap |
@@ -1,4 +1,2 @@ | |||||
/stage | |||||
/forth | /forth | ||||
/*-bin.h | |||||
/blkfs | |||||
/forth.bin |
@@ -1,47 +1,30 @@ | |||||
TARGETS = forth stage | |||||
TARGETS = forth | |||||
OBJS = emul.o libz80/libz80.o | OBJS = emul.o libz80/libz80.o | ||||
BLKPACK = ../tools/blkpack | |||||
BLKUNPACK = ../tools/blkunpack | |||||
CDIR = ../cvm | |||||
STAGE = $(CDIR)/stage | |||||
BLKFS = $(CDIR)/blkfs | |||||
.PHONY: all | .PHONY: all | ||||
all: $(TARGETS) | all: $(TARGETS) | ||||
$(BLKPACK): | |||||
$(MAKE) -C ../tools | |||||
.PHONY: $(BLKUNPACK) | |||||
$(BLKUNPACK): $(BLKPACK) | |||||
stage: stage.c $(OBJS) blkfs | |||||
$(CC) stage.c $(OBJS) -o $@ | |||||
blkfs: $(BLKPACK) | |||||
$(BLKPACK) ../blk > $@ | |||||
forth: forth.c $(OBJS) blkfs | |||||
forth: forth.c $(OBJS) $(CDIR)/blkfs | |||||
$(CC) forth.c $(OBJS) -lncurses -o $@ | $(CC) forth.c $(OBJS) -lncurses -o $@ | ||||
libz80/libz80.o: libz80/z80.c | libz80/libz80.o: libz80/z80.c | ||||
$(MAKE) -C libz80/codegen opcodes | $(MAKE) -C libz80/codegen opcodes | ||||
$(CC) -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c | $(CC) -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c | ||||
emul.o: emul.c | |||||
$(CC) -DFBIN_PATH=\"`pwd`/forth.bin\" -DBLKFS_PATH=\"`pwd`/blkfs\" -c -o emul.o emul.c | |||||
emul.o: emul.c forth.bin $(BLKFS) | |||||
$(CC) -DFBIN_PATH=\"`pwd`/forth.bin\" -DBLKFS_PATH=\"`pwd`/$(BLKFS)\" -c -o emul.o emul.c | |||||
.PHONY: updatebootstrap | |||||
updatebootstrap: stage xcomp.fs pack | |||||
./stage < xcomp.fs > new.bin | |||||
mv new.bin forth.bin | |||||
forth.bin: xcomp.fs $(STAGE) | |||||
$(CDIR)/stage < xcomp.fs > $@ | |||||
.PHONY: pack | |||||
pack: | |||||
rm blkfs && $(MAKE) blkfs | |||||
$(BLKFS): $(STAGE) | |||||
.PHONY: unpack | |||||
unpack: | |||||
$(BLKUNPACK) ../blk < blkfs | |||||
$(STAGE): | |||||
$(MAKE) -C $(CDIR) all | |||||
.PHONY: clean | .PHONY: clean | ||||
clean: | clean: | ||||
rm -f $(TARGETS) emul.o *-bin.h blkfs libz80/libz80.o | |||||
rm -f $(TARGETS) emul.o *.bin libz80/libz80.o |
@@ -1,80 +0,0 @@ | |||||
#include <stdint.h> | |||||
#include <stdio.h> | |||||
#include <unistd.h> | |||||
#include "emul.h" | |||||
/* Staging binaries | |||||
The role of a stage executable is to compile definitions in a dictionary and | |||||
then spit the difference between the starting binary and the new binary. | |||||
That binary can then be grafted to an exiting Forth binary to augment its | |||||
dictionary. | |||||
We could, if we wanted, run only with the bootstrap binary and compile core | |||||
defs at runtime, but that would mean that those defs live in RAM. In may system, | |||||
RAM is much more constrained than ROM, so it's worth it to give ourselves the | |||||
trouble of compiling defs to binary. | |||||
*/ | |||||
#define RAMSTART 0 | |||||
#define STDIO_PORT 0x00 | |||||
// To know which part of RAM to dump, we listen to port 2, which at the end of | |||||
// its compilation process, spits its HERE addr to port 2 (MSB first) | |||||
#define HERE_PORT 0x02 | |||||
static int running; | |||||
// We support double-pokes, that is, a first poke to tell where to start the | |||||
// dump and a second one to tell where to stop. If there is only one poke, it's | |||||
// then ending HERE and we start at sizeof(KERNEL). | |||||
static uint16_t start_here = 0; | |||||
static uint16_t end_here = 0; | |||||
static uint8_t iord_stdio() | |||||
{ | |||||
int c = getc(stdin); | |||||
if (c == EOF) { | |||||
running = 0; | |||||
} | |||||
return (uint8_t)c; | |||||
} | |||||
static void iowr_stdio(uint8_t val) | |||||
{ | |||||
// comment if you don't like verbose staging output | |||||
putc(val, stderr); | |||||
} | |||||
static void iowr_here(uint8_t val) | |||||
{ | |||||
start_here <<=8; | |||||
start_here |= (end_here >> 8); | |||||
end_here <<= 8; | |||||
end_here |= val; | |||||
} | |||||
int main(int argc, char *argv[]) | |||||
{ | |||||
Machine *m = emul_init(); | |||||
if (m == NULL) { | |||||
return 1; | |||||
} | |||||
m->ramstart = RAMSTART; | |||||
m->iord[STDIO_PORT] = iord_stdio; | |||||
m->iowr[STDIO_PORT] = iowr_stdio; | |||||
m->iowr[HERE_PORT] = iowr_here; | |||||
// Run! | |||||
running = 1; | |||||
while (running && emul_step()); | |||||
// We're done, now let's spit dict data | |||||
for (int i=start_here; i<end_here; i++) { | |||||
putchar(m->mem[i]); | |||||
} | |||||
emul_deinit(); | |||||
emul_printdebug(); | |||||
return 0; | |||||
} | |||||
@@ -4,11 +4,12 @@ git submodule init | |||||
git submodule update | git submodule update | ||||
git clean -fxd | git clean -fxd | ||||
make -C cvm | |||||
make -C emul | make -C emul | ||||
make -C tests | make -C tests | ||||
# verify that forth.bin is stable | # verify that forth.bin is stable | ||||
cp emul/forth.bin ref.bin | |||||
make -C emul updatebootstrap | |||||
cmp emul/forth.bin ref.bin | |||||
cp cvm/forth.bin ref.bin | |||||
make -C cvm updatebootstrap | |||||
cmp cvm/forth.bin ref.bin | |||||
rm ref.bin | rm ref.bin |