diff --git a/emul/.gitignore b/emul/.gitignore index 344593e..7843c19 100644 --- a/emul/.gitignore +++ b/emul/.gitignore @@ -1,6 +1,8 @@ /shell/shell /forth/stage1 /forth/stage1dbg +/forth/stage2 +/forth/stage2dbg /forth/forth /zasm/zasm /zasm/avra diff --git a/emul/Makefile b/emul/Makefile index b260947..a18d58c 100644 --- a/emul/Makefile +++ b/emul/Makefile @@ -27,8 +27,8 @@ shell/shell-bin.h: shell/shell.bin shell/shell: shell/shell.c $(SHELLOBJS) shell/shell-bin.h $(CC) shell/shell.c $(SHELLOBJS) -o $@ -forth/forth0.bin: forth/glue0.asm $(ZASMBIN) - $(ZASMBIN) $(KERNEL) ../forth < forth/glue0.asm | tee $@ > /dev/null +forth/forth0.bin: forth/stage0.asm $(ZASMBIN) + $(ZASMBIN) $(KERNEL) ../forth forth/stagec.asm < forth/stage0.asm | tee $@ > /dev/null forth/forth0-bin.h: forth/forth0.bin ./bin2c.sh KERNEL < forth/forth0.bin | tee $@ > /dev/null @@ -39,16 +39,30 @@ forth/stage1: forth/stage.c $(OBJS) forth/forth0-bin.h forth/stage1dbg: forth/stage.c $(OBJS) forth/forth0-bin.h $(CC) -DDEBUG forth/stage.c $(OBJS) -o $@ -forth/core.bin: $(FORTHSRC_PATHS) forth/stage1 - cat $(FORTHSRC_PATHS) | ./forth/stage1 | tee $@ > /dev/null - -forth/forth1.bin: forth/glue1.asm forth/core.bin $(ZASMBIN) - $(ZASMBIN) $(KERNEL) ../forth forth/core.bin < forth/glue1.asm | tee $@ > /dev/null +# z80c.bin is not in the prerequisites because its a bootstrap binary that +# should be updated manually through make fbootstrap. +forth/forth1.bin: forth/stage1.asm forth/forth0.bin $(ZASMBIN) + $(ZASMBIN) $(KERNEL) ../forth forth/z80c.bin forth/stagec.asm < forth/stage1.asm | tee $@ > /dev/null forth/forth1-bin.h: forth/forth1.bin ./bin2c.sh KERNEL < forth/forth1.bin | tee $@ > /dev/null -forth/forth: forth/forth.c $(OBJS) forth/forth1-bin.h +forth/stage2: forth/stage.c $(OBJS) forth/forth1-bin.h + $(CC) -DSTAGE2 forth/stage.c $(OBJS) -o $@ + +forth/stage2dbg: forth/stage.c $(OBJS) forth/forth1-bin.h + $(CC) -DSTAGE2 -DDEBUG forth/stage.c $(OBJS) -o $@ + +forth/core.bin: $(FORTHSRC_PATHS) forth/stage2 + cat $(FORTHSRC_PATHS) | ./forth/stage2 | tee $@ > /dev/null + +forth/forth2.bin: forth/stage2.asm forth/core.bin $(ZASMBIN) + $(ZASMBIN) $(KERNEL) ../forth forth/core.bin forth/z80c.bin forth/stagec.asm < forth/stage2.asm | tee $@ > /dev/null + +forth/forth2-bin.h: forth/forth2.bin + ./bin2c.sh KERNEL < forth/forth2.bin | tee $@ > /dev/null + +forth/forth: forth/forth.c $(OBJS) forth/forth2-bin.h $(CC) forth/forth.c $(OBJS) -o $@ zasm/kernel-bin.h: zasm/kernel.bin @@ -94,6 +108,10 @@ updatebootstrap: $(ZASMBIN) $(ZASMBIN) $(KERNEL) < zasm/glue.asm > zasm/kernel.bin $(ZASMBIN) $(KERNEL) $(APPS) zasm/user.h < $(APPS)/zasm/glue.asm > zasm/zasm.bin +.PHONY: fbootstrap +fbootstrap: forth/stage1 + cat $(FORTHSRC_PATHS) ../forth/z80c.fs ../forth/dummy.fs | ./forth/stage1 | tee forth/z80c.bin > /dev/null + .PHONY: clean clean: rm -f $(TARGETS) $(SHELLTGTS) emul.o zasm/*-bin.h shell/*-bin.h diff --git a/emul/README.md b/emul/README.md index c0970f7..1e67a58 100644 --- a/emul/README.md +++ b/emul/README.md @@ -80,6 +80,39 @@ 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. + +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. + +There are 3 build stages. + +**Stage 0**: This stage is created with zasm by assembling `forth/forth.asm` +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. + +The long term goal is to gradually extract contents from `forth.asm` and have +nothing but Forth source files. + +**Stage 1**: The `stage1` binary allows us to augment `forth0.bin` with +contents from `z80c.fs`, which compiles native words using Forth's Z80 +assembler. This yields `z80c.bin`. + +This is where there's a chiken-and-egg issue: Forth's assembler needs our full +Forth interpreter, but that interpreter needs native words from `z80c.fs`. This +is why `z80c.bin` is committed into the git repo and it's built automatically +with `make`. Updating `z80c.bin` is a specific make rule, `fbootstrap`. + +Then, from there, we augment `forth0.bin` with `z80c.bin` and yield +`forth1.bin`, from which we create `stage2`. + +**Stage 2**: From there, the way is clear to compile the dict of our full Forth +interpreter, which we do using `stage2` and produce `forth2.bin`, from which we +can create our final `forth` executable. + ## Problems? If the libz80-wrapped zasm executable works badly (hangs, spew garbage, etc.), diff --git a/emul/forth/forth.c b/emul/forth/forth.c index 873b4c1..b2084a3 100644 --- a/emul/forth/forth.c +++ b/emul/forth/forth.c @@ -3,7 +3,7 @@ #include #include #include "../emul.h" -#include "forth1-bin.h" +#include "forth2-bin.h" // in sync with glue.asm #define RAMSTART 0x900 diff --git a/emul/forth/glue1.asm b/emul/forth/glue1.asm deleted file mode 100644 index 616e024..0000000 --- a/emul/forth/glue1.asm +++ /dev/null @@ -1,31 +0,0 @@ -; Warning: The offsets of native dict entries must be exactly the same between -; glue0.asm and glue1.asm -.equ LATEST RAMSTART ; override -.equ STDIO_PORT 0x00 - - jp init - -.equ GETC emulGetC -.equ PUTC emulPutC -.inc "forth.asm" - -init: - di - ; setup stack - ld sp, 0xffff - call forthMain - halt - -emulGetC: - ; Blocks until a char is returned - in a, (STDIO_PORT) - cp a ; ensure Z - ret - -emulPutC: - out (STDIO_PORT), a - ret - -.out $ ; should be the same as in glue0 -.bin "core.bin" -RAMSTART: diff --git a/emul/forth/stage.c b/emul/forth/stage.c index d7a1772..2897d57 100644 --- a/emul/forth/stage.c +++ b/emul/forth/stage.c @@ -2,7 +2,11 @@ #include #include #include "../emul.h" +#ifdef STAGE2 +#include "forth1-bin.h" +#else #include "forth0-bin.h" +#endif /* Staging binaries @@ -32,7 +36,11 @@ trouble of compiling defs to binary. #define HERE_PORT 0x02 static int running; -static uint16_t ending_here = 0; +// 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() { @@ -54,8 +62,10 @@ static void iowr_stdio(uint8_t val) static void iowr_here(uint8_t val) { - ending_here <<= 8; - ending_here |= val; + start_here <<=8; + start_here |= (end_here >> 8); + end_here <<= 8; + end_here |= val; } int main(int argc, char *argv[]) @@ -76,8 +86,10 @@ int main(int argc, char *argv[]) #ifndef DEBUG // We're done, now let's spit dict data - fprintf(stderr, "hey, %x\n", ending_here); - for (int i=sizeof(KERNEL); imem[i]); } #endif diff --git a/emul/forth/stage0.asm b/emul/forth/stage0.asm new file mode 100644 index 0000000..2895ac0 --- /dev/null +++ b/emul/forth/stage0.asm @@ -0,0 +1,7 @@ + jp init + +.inc "stagec.asm" +.inc "forth.asm" + +CODE_END: +.out $ ; should be the same as in stage{1,2} diff --git a/emul/forth/stage1.asm b/emul/forth/stage1.asm new file mode 100644 index 0000000..4cb46b2 --- /dev/null +++ b/emul/forth/stage1.asm @@ -0,0 +1,8 @@ + jp init + +.inc "stagec.asm" +.inc "forth.asm" + +.out $ ; should be the same as in stage{0,2} +.bin "z80c.bin" +CODE_END: diff --git a/emul/forth/stage2.asm b/emul/forth/stage2.asm new file mode 100644 index 0000000..73612ad --- /dev/null +++ b/emul/forth/stage2.asm @@ -0,0 +1,10 @@ + jp init + +.inc "stagec.asm" +.inc "forth.asm" + +.out $ ; should be the same as in stage{0,1} +.bin "z80c.bin" +.bin "core.bin" +CODE_END: + diff --git a/emul/forth/glue0.asm b/emul/forth/stagec.asm similarity index 77% rename from emul/forth/glue0.asm rename to emul/forth/stagec.asm index b38f4bb..1cbea79 100644 --- a/emul/forth/glue0.asm +++ b/emul/forth/stagec.asm @@ -1,13 +1,8 @@ .equ RAMSTART 0xe800 .equ HERE_INITIAL CODE_END ; override +.equ LATEST CODE_END ; override .equ STDIO_PORT 0x00 - jp init - -.equ GETC emulGetC -.equ PUTC emulPutC -.inc "forth.asm" - init: di ; setup stack @@ -25,6 +20,5 @@ emulPutC: out (STDIO_PORT), a ret -CODE_END: -.out LATEST -.out $ ; should be the same as in glue1 +.equ GETC emulGetC +.equ PUTC emulPutC diff --git a/emul/forth/z80c.bin b/emul/forth/z80c.bin new file mode 100644 index 0000000..b57f1b0 Binary files /dev/null and b/emul/forth/z80c.bin differ diff --git a/forth/forth.asm b/forth/forth.asm index bb59f75..1a45941 100644 --- a/forth/forth.asm +++ b/forth/forth.asm @@ -1487,25 +1487,9 @@ OVER2: push bc ; B jp next -; ( a b c -- b c a) - .db "ROT" - .fill 4 - .dw $-OVER2 - .db 0 -ROT: - .dw nativeWord - pop hl ; C - pop de ; B - pop bc ; A - call chkPS - push de ; B - push hl ; C - push bc ; A - jp next - .db ">R" .fill 5 - .dw $-ROT + .dw $-OVER2 .db 0 P2R: .dw nativeWord diff --git a/forth/z80a.fs b/forth/z80a.fs index 6507b8f..1f9cabc 100644 --- a/forth/z80a.fs +++ b/forth/z80a.fs @@ -1,11 +1,5 @@ ( Z80 assembler ) -: CODE - ( same as CREATE, but with ROUTINE V ) - (entry) - ROUTINE V [LITN] , -; - ( Splits word into msb/lsb, lsb being on TOS ) : SPLITB DUP 0x100 / @@ -130,3 +124,12 @@ ( Specials ) : JRe, 0x18 A, 2 - A, ; : JPNEXT, ROUTINE N [LITN] JPnn, ; + +: CODE + ( same as CREATE, but with ROUTINE V ) + (entry) + ROUTINE V [LITN] , +; + +: ;CODE JPNEXT, ; + diff --git a/forth/z80c.fs b/forth/z80c.fs new file mode 100644 index 0000000..4e2f89e --- /dev/null +++ b/forth/z80c.fs @@ -0,0 +1,33 @@ +( Core words in z80. This requires a full Forth interpreter + to run, but is also necessary for core.fs. This means that + it needs to be compiled from a prior bootstrapped binary. + + This stage is tricky due to the fact that references in + Forth are all absolute, except for prev word refs. This + means that there are severe limitations to the kind of code + you can put here. + + You shouldn't define any word with reference to other words. + This means no regular definition. You can, however, execute + any word from our high level Forth, as long as it doesn't + spit word references. + + ROUTINE stuff is fine. It's not supposed to change. + + These restrictions are temporary, I'll figure something out + so that we can end up fully bootstrap Forth from within + itself. +) + +( a b c -- b c a ) +CODE ROT + HL POPqq, ( C ) + DE POPqq, ( B ) + BC POPqq, ( A ) + ROUTINE P CALLnn, + DE PUSHqq, ( B ) + HL PUSHqq, ( C ) + BC PUSHqq, ( A ) +;CODE + +