Add /tools/slatest

To update LATEST offset more easily. Will be useful for recipes.
This commit is contained in:
Virgil Dupras 2020-04-02 23:15:43 -04:00
parent 6bf51ae57c
commit 9235fa9223
11 changed files with 81 additions and 204 deletions

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 <name of file to test>` 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`.

1
tools/.gitignore vendored
View File

@ -4,3 +4,4 @@
/fontcompile
/ttysafe
/pingpong
/slatest

View File

@ -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 $@

29
tools/slatest.c Normal file
View File

@ -0,0 +1,29 @@
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
/* 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);
}