diff --git a/README.md b/README.md index aa96b30..eef109d 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ manage to build and install Collapse OS without external resources (i.e. internet) on a machine of her design, built from scavenged parts with low-tech tools. +## Forth reboot in process + +You are currently looking at the `forth` branch of the project, which is a +Forth reboot of Collapse OS. You can see why I'm doing this in the [related +github issue][forth-issue]. + +Documentation is lacking, it's not ready yet, this is a WIP branch. + ## See it in action Michael Schierl has put together [a set of emulators running in the browser that @@ -29,8 +37,8 @@ path to giving Collapse OS a try. ## Organisation of this repository -* `kernel`: Pieces of code to be assembled by the user into a kernel. -* `apps`: Pieces of code to be assembled into "userspace" application. +* `forth`: Forth is slowly taking over this project (see issue #4). It comes + from this folder. * `recipes`: collection of recipes that assemble parts together on a specific machine. * `doc`: User guide for when you've successfully installed Collapse OS. @@ -39,8 +47,6 @@ path to giving Collapse OS a try. through a serial port. * `emul`: Emulated applications, such as zasm and the shell. * `tests`: Automated test suite for the whole project. -* `forth`: Forth is slowly taking over this project (see issue #4). It comes - from this folder. ## Status @@ -58,4 +64,5 @@ A more traditional [mailing list][listserv] and IRC (#collapseos on freenode) ch [jsemul]: https://schierlm.github.io/CollapseOS-Web-Emulator/ [discussion]: https://www.reddit.com/r/collapseos [listserv]: http://lists.sonic.net/mailman/listinfo/collapseos +[forth-issue]: https://github.com/hsoft/collapseos/issues/4 diff --git a/emul/Makefile b/emul/Makefile index 1728a8c..54270c4 100644 --- a/emul/Makefile +++ b/emul/Makefile @@ -1,36 +1,21 @@ -CFSPACK_OBJ = ../tools/cfspack/libcfs.o -TARGETS = shell/shell zasm/zasm runbin/runbin forth/forth -KERNEL = ../kernel -APPS = ../apps -ZASMBIN = zasm/zasm -AVRABIN = zasm/avra -SHELLAPPS = zasm ed -SHELLTGTS = ${SHELLAPPS:%=cfsin/%} +TARGETS = runbin/runbin forth/forth # Those Forth source files are in a particular order FORTHSRCS = core.fs str.fs parse.fs readln.fs fmt.fs z80a.fs FORTHSRC_PATHS = ${FORTHSRCS:%=../forth/%} -CFSIN_CONTENTS = $(SHELLTGTS) cfsin/user.h OBJS = emul.o libz80/libz80.o -SHELLOBJS = $(OBJS) $(CFSPACK_OBJ) -ZASMOBJS = $(SHELLOBJS) +SLATEST = ../tools/slatest .PHONY: all -all: $(TARGETS) $(AVRABIN) $(CFSIN_CONTENTS) +all: $(TARGETS) -# -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 +$(SLATEST): + $(MAKE) -C ../tools -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 $@ - -# z80c.bin is not in the prerequisites because its a bootstrap binary that -# should be updated manually through make fbootstrap. -forth/forth0.bin: +# z80c.bin and boot.bin are not in the prerequisites because they're bootstrap +# binaries that should be updated manually through make updatebootstrap. +forth/forth0.bin: $(SLATEST) cat forth/boot.bin forth/z80c.bin > $@ + $(SLATEST) $@ forth/forth0-bin.h: forth/forth0.bin ./bin2c.sh KERNEL < forth/forth0.bin | tee $@ > /dev/null @@ -44,8 +29,9 @@ forth/stage1dbg: forth/stage.c $(OBJS) forth/forth0-bin.h forth/core.bin: $(FORTHSRC_PATHS) forth/stage1 cat $(FORTHSRC_PATHS) ./forth/stop.fs | ./forth/stage1 | tee $@ > /dev/null -forth/forth1.bin: forth/forth0.bin forth/core.bin +forth/forth1.bin: forth/forth0.bin forth/core.bin $(SLATEST) cat forth/forth0.bin forth/core.bin > $@ + $(SLATEST) $@ forth/forth1-bin.h: forth/forth1.bin ./bin2c.sh KERNEL < forth/forth1.bin | tee $@ > /dev/null @@ -56,24 +42,6 @@ forth/stage2: forth/stage.c $(OBJS) forth/forth1-bin.h forth/forth: forth/forth.c $(OBJS) forth/forth1-bin.h $(CC) forth/forth.c $(OBJS) -o $@ -zasm/kernel-bin.h: zasm/kernel.bin - ./bin2c.sh KERNEL < zasm/kernel.bin | tee $@ > /dev/null - -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 $@ - -zasm/avra.bin: $(ZASMBIN) - $(ZASMBIN) $(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 $@ - runbin/runbin: runbin/runbin.c $(OBJS) $(CC) runbin/runbin.c $(OBJS) -o $@ @@ -84,28 +52,12 @@ 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 - -# -o in sync with USER_CODE in shell/user.h -$(SHELLTGTS): $(ZASMBIN) - $(ZASMBIN) -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 - -# We need to double wrap around dummy.fs because at stage3, we have high-level -# words and they write to HERE at initialization. -.PHONY: fbootstrap -fbootstrap: forth/stage2 - cat ./forth/conf.fs ../forth/boot.fs | ./forth/stage2 > forth/boot.bin +updatebootstrap: forth/stage2 + cat ./forth/conf.fs ../forth/boot.fs | ./forth/stage2 | tee forth/boot.bin > /dev/null cat ./forth/conf.fs ../forth/z80c.fs forth/emul.fs ../forth/icore.fs | ./forth/stage2 | tee forth/z80c.bin > /dev/null .PHONY: clean clean: - rm -f $(TARGETS) $(SHELLTGTS) emul.o zasm/*-bin.h shell/*-bin.h + rm -f $(TARGETS) emul.o forth/*-bin.h forth/forth?.bin diff --git a/emul/README.md b/emul/README.md index dc2b313..041994e 100644 --- a/emul/README.md +++ b/emul/README.md @@ -20,82 +20,20 @@ First, make sure that the `libz80` git submodule is checked out. If not, run After that, you can run `make` and it builds all applications. -## shell - -Running `shell/shell` runs the BASIC shell in an emulated machine. The goal of -this machine is not to simulate real hardware, but rather to serve as a -development platform. What we do here is we emulate the z80 part, the 64K -memory space and then hook some fake I/Os to stdin, stdout and a small storage -device that is suitable for Collapse OS's filesystem to run on. - -Through that, it becomes easier to develop userspace applications for Collapse -OS. - -By default, the shell initialized itself with a CFS device containing the -contents of `cfsin/` at launch (it's packed on the fly). You can specify an -alternate CFS device file (it has to be packaed already) through the `-f` flag. - -By default, the shell runs interactively, but you can also pipe contents through -stdin instead. The contents will be interpreted exactly as if you had typed it -yourself and the result will be spit in stdout (it includes your typed in -contents because the Collapse OS console echoes back every character that is -sent to it.). This feature is useful for automated tests in `tools/tests/shell`. - -## zasm - -`zasm/zasm` is `apps/zasm` wrapped in an emulator. It is quite central to the -Collapse OS project because it's used to assemble everything, including itself! - -The program takes no parameter. It reads source code from stdin and spits -binary in stdout. It supports includes and had both `apps/` and `kernel` folder -packed into a CFS that was statically included in the executable at compile -time. - -The file `zasm/zasm.bin` is a compiled binary for `apps/zasm/glue.asm` and -`zasm/kernel.bin` is a compiled binary for `tools/emul/zasm/glue.asm`. It is -used to bootstrap the assembling process so that no assembler other than zasm -is required to build Collapse OS. - -This binary is fed to libz80 to produce the `zasm/zasm` "modern" binary and -once you have that, you can recreate `zasm/zasm.bin` and `zasm/kernel.bin`. - -This is why it's included as a binary in the repo, but yes, it's redundant with -the source code. - -Those binaries can be updated with the `make updatebootstrap` command. If they -are up-to date and that zasm isn't broken, this command should output the same -binary as before. - -## avra - -In the `zasm` folder, there's also `avra` which is a zasm compiled as an AVR -assembler. It works the same way as zasm except it expects AVR mnemonics and -spits AVR binaries. - -## runbin - -This is a very simple tool that reads binary z80 code from stdin, loads it in -memory starting at address 0 and then run the code until it halts. The exit -code of the program is the value of `A` when the program halts. - -This is used for unit tests. - ## forth -Collapse OS' Forth interpreter, which will probably soon replace the whole OS. +Collapse OS' Forth interpreter, which is in the process of replacing the +zasm-based project. -At this point, it is not yet entirely self-hosting, but will be eventually. -Because of that aim, it currently builds in a particular manner. +The Forth interpreter is entirely self-hosting, that is, it assembles its +binary with itself. There are 3 build stages. -**Stage 0**: This stage is created with zasm by assembling `forth/forth.asm` -and `z80c.bin` through `stage0.asm`. This yields `forth0.bin`. We then wrap -this binary with `stage.c` to create the `stage1` binary, which allows us to -get to the next stage. - -`z80c.bin` is a "chicken-and-egg" typf of binary that is committed in the repo. -It is the result of compiling `z80c.fs`, but this needs stage2. +**Stage 0**: At this stage, all we have are our bootstrap binaries, `boot.bin` +and `z80c.bin`. We concatenate them into `forth0.bin` ans then wrap the +emulator around it which is named `stage1` (because it builds the stage 1) to +have a barebone forth interpreter. **Stage 1**: The `stage1` binary allows us to augment `forth0.bin` with the compiled dictionary of a full Forth interpreter. We feed it with @@ -106,17 +44,25 @@ and `stage2` executables. `forth` is the interpreter you'll use. **Stage 2**: `stage2` is used to resolve the chicken-and-egg problem and use the power of a full Forth intepreter, including an assembler, to assemble -`z80c.bin`. This is a manual step executed through `make fbootstrap`. +`z80c.bin`. This is a manual step executed through `make updatebootstrap`. -Normally, running this step should yield the exact same `z80c.bin` as before, -unless of course you've changed the source. +Normally, running this step should yield the exact same `boot.bin` and +`z80c.bin` as before, unless of course you've changed the source. + +## runbin + +This is a very simple tool that reads binary z80 code from stdin, loads it in +memory starting at address 0 and then run the code until it halts. The exit +code of the program is the value of `A` when the program halts. + +This is used for unit tests. ## Problems? If the libz80-wrapped zasm executable works badly (hangs, spew garbage, etc.), it's probably because you've broken your bootstrap binaries. They're easy to mistakenly break. To verify if you've done that, look at your git status. If -`kernel.bin` or `zasm.bin` are modified, try resetting them and then run +`boot.bin` or `z80c.bin` are modified, try resetting them and then run `make clean all`. Things should go better afterwards. If that doesn't work, there's also the nuclear option of `git reset --hard` diff --git a/emul/forth/forth.c b/emul/forth/forth.c index 5225e40..b1a9314 100644 --- a/emul/forth/forth.c +++ b/emul/forth/forth.c @@ -77,11 +77,6 @@ int main(int argc, char *argv[]) m->mem[i] = KERNEL[i]; } - // Our binaries don't have their LATEST offset set yet. We do this - // on the fly, which is the simplest way to proceed ( bash script to update - // LATEST after compilation is too simplicated ) - m->mem[0x08] = sizeof(KERNEL) & 0xff; - m->mem[0x09] = sizeof(KERNEL) >> 8; // Run! running = 1; diff --git a/emul/forth/stage.c b/emul/forth/stage.c index 8d81a60..de239cc 100644 --- a/emul/forth/stage.c +++ b/emul/forth/stage.c @@ -80,12 +80,6 @@ int main(int argc, char *argv[]) m->mem[i] = KERNEL[i]; } - // Our binaries don't have their LATEST offset set yet. We do this - // on the fly, which is the simplest way to proceed ( bash script to update - // LATEST after compilation is too simplicated ) - m->mem[0x08] = sizeof(KERNEL) & 0xff; - m->mem[0x09] = sizeof(KERNEL) >> 8; - // Run! running = 1; diff --git a/runtests.sh b/runtests.sh index fd755da..7dbf2b0 100755 --- a/runtests.sh +++ b/runtests.sh @@ -7,6 +7,6 @@ git clean -fxd make -C emul make -C tests -# let's try again with an updated zasm +# let's try again with an updated boot bin make -C emul updatebootstrap all make -C tests diff --git a/tests/Makefile b/tests/Makefile index 43acc96..2842177 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -2,9 +2,5 @@ EMULDIR = ../emul .PHONY: run run: - $(MAKE) -C $(EMULDIR) zasm/zasm zasm/avra runbin/runbin shell/shell forth/forth - cd unit && ./runtests.sh - cd zasm && ./runtests.sh - cd avra && ./runtests.sh - cd shell && ./runtests.sh + $(MAKE) -C $(EMULDIR) forth/forth cd forth && ./runtests.sh diff --git a/tests/README.md b/tests/README.md index 1002590..e805014 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,49 +1,4 @@ # Testing Collapse OS This folder contains Collapse OS' automated testing suite. To run, it needs -`tools/emul` to be built. You can run all tests with `make`. - -## zasm - -This folder tests zasm's assembling capabilities by assembling test source files -and compare the results with expected binaries. These binaries used to be tested -with a golden standard assembler, scas, but at some point compatibility with -scas was broken, so we test against previously generated binaries, making those -tests essentially regression tests. - -Those reference binaries sometimes change, especially when we update code in -core libraries because some tests include them. In this case, we have to update -binaries to the new expected value by being extra careful not to introduce a -regression in test references. - -## unit - -Those tests target specific routines to test and test them using -`tools/emul/runbin` which: - -1. Loads the specified binary -2. Runs it until it halts -3. Verifies that `A` is zero. If it's not, we're in error and we display the - value of `A`. - -Test source code has no harnessing and is written in a very "hands on" approach. -At the moment, debugging a test failure is a bit tricky because the error code -often doesn't tell us much. - -The convention is to keep a `testNum` counter variable around and call -`nexttest` after each success so that we can easily have an idea of where we -fail. - -Then, if you need to debug the cause of a failure, well, you're on your own. -However, there are tricks. - -1. Run `unit/runtests.sh ` to target a specific test unit. -2. Insert a `halt` to see the value of `A` at any given moment: it will be your - reported error code (if 0, runbin will report a success). - -## shell - -Those tests are in the form of shell "replay" files. Every ".replay" file in -this folder contains the contents to type in the shell. That contents is piped -through the shell and the output is then compared with the corresponding -".expected" file. If they match exactly, the test passes. +`/emul` to be built. You can run all tests with `make`. diff --git a/tools/.gitignore b/tools/.gitignore index 4d9ca6d..93cef83 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -4,3 +4,4 @@ /fontcompile /ttysafe /pingpong +/slatest diff --git a/tools/Makefile b/tools/Makefile index dd5970b..ddf0e01 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -4,8 +4,9 @@ UPLOAD_TGT = upload FONTCOMPILE_TGT = fontcompile TTYSAFE_TGT = ttysafe PINGPONG_TGT = pingpong +SLATEST_TGT = slatest TARGETS = $(MEMDUMP_TGT) $(BLKDUMP_TGT) $(UPLOAD_TGT) $(FONTCOMPILE_TGT) \ - $(TTYSAFE_TGT) $(PINGPONG_TGT) + $(TTYSAFE_TGT) $(PINGPONG_TGT) $(SLATEST_TGT) OBJS = common.o all: $(TARGETS) @@ -20,6 +21,7 @@ $(UPLOAD_TGT): $(UPLOAD_TGT).c $(FONTCOMPILE_TGT): $(FONTCOMPILE_TGT).c $(TTYSAFE_TGT): $(TTYSAFE_TGT).c $(PINGPONG_TGT): $(PINGPONG_TGT).c +$(SLATEST_TGT): $(SLATEST_TGT).c $(TARGETS): $(OBJS) $(CC) $(CFLAGS) $@.c $(OBJS) -o $@ diff --git a/tools/slatest.c b/tools/slatest.c new file mode 100644 index 0000000..06af597 --- /dev/null +++ b/tools/slatest.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +/* Update the "LATEST" offset of target Forth binary according to filesize. */ + +#define OFFSET 0x08 + +int main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: ./slatest fname\n"); + return 1; + } + FILE *fp = fopen(argv[1], "r+"); + if (!fp) { + fprintf(stderr, "Can't open %s.\n", argv[1]); + return 1; + } + fseek(fp, 0, SEEK_END); + unsigned int bytecount = ftell(fp); + fseek(fp, OFFSET, SEEK_SET); + char buf[2]; + buf[0] = bytecount & 0xff; + buf[1] = bytecount >> 8; + fwrite(buf, 2, 1, fp); + fclose(fp); +}