1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-02 10:20:55 +11:00

Compare commits

..

No commits in common. "d6c9ab3f32e6e54f23b32b59b32eddff29d814bd" and "9390479d491e0d4f500f2279b78f7a9d70ab817a" have entirely different histories.

3 changed files with 144 additions and 85 deletions

View File

@ -57,19 +57,17 @@
( a o ol -- a+n )
: RLATOM
ROT ( o ol a )
DUP @ ( o ol a n )
DUP 0x24 = IF
( 0x24 is an addrWord, which should be offsetted in
the same way that a regular word would. To achieve
this, we skip ASKIP and instead of skipping 4 bytes
like a numberWord, we skip only 2, which means that
our number will be treated like a regular wordref.
)
DROP
DUP @ 0x24 = IF
( addrWord? we need to offset it )
2 + ( o ol a+2 )
ROT ROT 2DROP ( a )
EXIT
ROT OVER ( ol a o a )
@ -^ ( ol a n-o )
OVER ! ( ol a )
SWAP DROP ( a )
2 + ( a+2 )
EXIT ( no need for ASKIP )
THEN
DUP @ ( o ol a n )
ROT ( o a n ol )
< IF ( under limit, do nothing )
SWAP DROP ( a )

View File

@ -67,9 +67,7 @@ stage on the RC2014 itself!
To build your stage 1, run `make` in this folder, this will yield `os.bin`.
This will contain that tiny core and, appended to it, the Forth source code it
needs to run to bootstrap itself. When it's finished bootstrapping, you will
get a prompt to an almost-full Forth interpreter (there's not enough space in
8K to fit both link.fs and readln.fs, so we ditch readln. Our prompt is raw. No
backspace no buffer. Hardcore mode.)
get a prompt to a full Forth interpreter.
### Emulate
@ -127,92 +125,157 @@ there are compiled based on a 0x8000-or-so base offset. What we need is a
0xa00-or-so base offset, that is, something suitable to be appended to the boot
binary, in ROM, in binary form.
Fortunately, inside the compiled source is the contents of link.fs which will
allow us to relink our compiled dictionary so that in can be relocated in ROM,
next to our boot binary. I won't go into relinking details. Look at the source.
For now, let's just use it:
We can't simply adjust offsets. For complicated reasons, that can't be reliably
done. We have to re-interpret that same source code, but from a ROM offset. But
how are we going to do that? After all, ROM is called ROM for a reason.
RLCORE
Memory maps.
That command will take the dict from `' H@` up to `CURRENT`, copy it in free
memory and then relocate it. It will print 3 addresses during its processing.
What we're going to do is to set up a memory map targeting our ROM and point it
to our RAM. Then we can recompile the source as if we were in ROM, right after
our boot binary. Forth won't ever notice it's actually in RAM.
The first address is the top copied address. The process didn't touch memory
above this point. The second address is the wordref of the last copied entry.
The 3rd is the bottom address of the copied dict. When that last address is
printed, the processing is over (because we don't have a `>` prompt, we don't
have any other indicator that the process is over).
Alright, let's do this. First, let's have a look around. Where is the end of
our boot binary? To know, find the word ";", which is the last word of icore:
### Assembling the stage 2 binary
> ' ; .X
097d>
> 64 0x0970 DUMP
:70 0035 0958 00da ff43 .5.X...C
:78 003b 3500 810e 0020 .;5....
:80 0043 0093 07f4 03ef .C......
:88 0143 005f 0f00 0131 .C._...1
:90 3132 2052 414d 2b20 12 RAM+
:98 4845 5245 2021 0a20 HERE !.
:a0 3a20 4840 2048 4552 : H@ HER
:a8 4520 4020 3b0a 203a E @ ;. :
At that point, we have a fully relocated binary in memory. Depending on our
situations, the next steps differ.
See that `_` at 0x98b? That's the name of our hook word. 4 bytes later is its
wordref. That's the end of our boot binary. 0x98f, that's an address to write
down.
* If we're on a RC2014 that has writing capabilities to permanent storage,
we'll want to assemble that binary directly on the RC2014 and write it to
permanent storage.
* If we're on a RC2014 that doesn't have those capabilities, we'll want to dump
memory on our modern environment using `/tools/memdump` and then assemble that
binary there.
* If we're in the emulator, we'll want to dump our memory using `CTRL+E` and
then assemble our stage 2 binary from that dump.
Right after that is our appended source code. The first part is `pre.fs` and
can be ignored. What we want starts at the definition of the `H@` word, which
is at 0x9a0. Another address to write down.
In these instructions, we assume an emulated environment. I'll use actual
offsets of an actual assembling session, but these of course are only examples.
It is very likely that these will not be the same offsets for you.
So our memory map will target 0x98f. Where will we place it? It doesn't matter
much, we have plenty of RAM. Where's `HERE`?
So you've pressed `CTRL+E` and you have a `memdump` file. Open it with a hex
editor (I like `hexedit`) to have a look around and to decide what we'll extract
from that memdump. `RLCORE` already gave you important offsets (in my case,
`9a3c`, `99f6` and `8d60`), but although the beginning of will always be the
same (`8d60`), the end offset depends on the situation.
> H@ .X
8c3f>
If you look at data between `99f6` and `9a3c`, you'll see that this data is not
100% dictionary entry material. Some of it is buffer data allocated at
initialization. To locate the end of a word, look for `0043`, the address for
`EXIT`. In my case, it's at `9a1a` and it's the end of the `INIT` word.
Alright, let's go wide and use 0xa000 as our map destination. But before we do,
let's copy the content of our ROM into RAM because there's our source code
there and if we don't copy it before setting up the memory map, we'll shadow it.
Moreover, the `INIT` routine that is in there is not quite what we want,
because it doesn't contain the `HERE` adjustment that we find in `pre.fs`.
We'll want to exclude it from our binary, so let's go a bit further, at `99cf`,
ending at `99de`.
Let's be lazy and don't even check where the source stop. Let's assume it stops
at 0x1fff, the end of the ROM.
So, the end of our compiled dict is actually `99de`. Alright, let's extract it:
> 0x98f 0xa000 0x2000 0x98f - MOVE
> 64 0xa000 DUMP
:00 3131 3220 5241 4d2b 112 RAM+
:08 2048 4552 4520 210a HERE !.
:10 203a 2048 4020 4845 : H@ HE
:18 5245 2040 203b 0a20 RE @ ;.
:20 3a20 2d5e 2053 5741 : -^ SWA
:28 5020 2d20 3b0a 203a P - ;. :
:30 205b 2049 4e54 4552 [ INTER
:38 5052 4554 2031 2046 PRET 1 F
dd if=memdump bs=1 skip=36192 count=3198 > dict.bin
Looks fine. Now, let's create a memory map. A memory map word is rather simple.
It is called before each `@/C@/!/C!` operation and is given the opportunity to
tweak the address on PSP's TOS. Let's go with our map:
`36192` is `8d60` and `3198` is `99de-8d60`. This needs to be prepended by the
boot binary. But that one, we already have. It's `z80c.bin`
> : MMAP
DUP 0x98f < IF EXIT THEN
DUP 0x1fff > IF EXIT THEN
[ 0xa000 0x98f - LITN ] +
;
> 0x98e MMAP .X
098e> 0x98f MMAP .X
a000> 0xabc MMAP .X
a12b> 0x1fff MMAP .X
b66e> 0x2000 MMAP .X
2000>
cat z80c.bin dict.bin > stage2.bin
This looks good. Let's apply it for real:
Is it ready to run yet? no. There are 3 adjustments we need to manually make
using our hex editor.
> ' MMAP (mmap*) !
> 64 0x980 DUMP
1. We need to link `H@` to the hook word of the boot binary. In my case, it's
a matter of writing `02` at `08ec` and `00` at `08ed`, `H@`'s prev field.
2. We need to end our binary with a hook word. It can have a zero-length name
and the prev field needs to properly point to the previous wordref. In my
case, that was `RLCORE` at offset `1559` for a `stage2.bin` size of `1568`,
which means that I appended `0F 00 00` at the end of the file.
3. Finally, we need to adjust `LATEST` which is at offset `08`. This needs to
point to the last wordref of the file, which is equal to the length of
`stage2.bin` because we've just added a hook word. This means that we write
`6B` at offset `08` and `15` at offset `09`.
:80 0043 0093 07f4 03ef .C......
:88 0143 005f 0f00 0131 .C._...1
:90 3132 2052 414d 2b20 12 RAM+
:98 4845 5245 2021 0a20 HERE !.
:a0 3a20 4840 2048 4552 : H@ HER
:a8 4520 4020 3b0a 203a E @ ;. :
:b0 202d 5e20 5357 4150 -^ SWAP
:b8 202d 203b 0a20 3a20 - ;. :
Now are we ready yet? ALMOST! There's one last thing we need to do: add runtime
source. In our case, because we have a compiled dict, the only source we need
to include is `pre.fs` and `run.fs`:
But how do we know that it really works? Because we can write in ROM!
cat stage2.bin pre.fs run.fs > stage2r.bin
> 'X' 0x98f !
> 64 0x980 DUMP
That's it! our binary is ready to run!
:80 0043 0093 07f4 03ef .C......
:88 0143 005f 0f00 0131 .C._...X
:90 0032 2052 414d 2b20 .2 RAM+
:98 4845 5245 2021 0a20 HERE !.
:a0 3a20 4840 2048 4552 : H@ HER
:a8 4520 4020 3b0a 203a E @ ;. :
:b0 202d 5e20 5357 4150 -^ SWAP
:b8 202d 203b 0a20 3a20 - ;. :
> 64 0xa000 DUMP
../../emul/hw/rc2014/classic stage2r.bin
:00 5800 3220 5241 4d2b X.2 RAM+
:08 2048 4552 4520 210a HERE !.
:10 203a 2048 4020 4845 : H@ HE
:18 5245 2040 203b 0a20 RE @ ;.
:20 3a20 2d5e 2053 5741 : -^ SWA
:28 5020 2d20 3b0a 203a P - ;. :
:30 205b 2049 4e54 4552 [ INTER
:38 5052 4554 2031 2046 PRET 1 F
And there you have it, a stage2 binary that you've assembled yourself. Now,
here's for your homework: use the same technique to add the contents of
`readln.fs` to stage2 so that you have a full-featured interpreter.
We're now ready for a re-bootstrap. Here's what we're gonna do:
1. Bring `CURRENT` and `HERE` back to `0x98f`.
2. Set `CINPTR` to `icore`'s `(c<)`.
`(c<)` word is the main input of the interpreter. Right now, your `(c<)` comes
from the `readln` unit, which makes the main `INTERPRET` loop wait for your
keystrokes before interpreting your words.
But this can be changed. At the moment where we change `CINPTR`, the interpret
loop will start reading from it, so we'll lose control. That is why we must
prepare things carefully before that. We'll re-gain control at the end of the
bootstrap source, in `run.fs`, where `(c<)` is set to `readln`'s `(c<)`
`(c<)` word is the main input of the interpreter. Right now, your `(c<)` comes
from the `readln` unit, which makes the main `INTERPRET` loop wait for your
keystrokes before interpreting your words.
But this can be changed. At the moment where we change `CINPTR`, the interpret
loop will start reading from it, so we'll lose control. That is why we must
prepare things carefully before that. We'll re-gain control at the end of the
bootstrap source, in `run.fs`, where `(c<)` is set to `readln`'s `(c<)`.
At this moment, `icore`'s `(c<)` is shadowed by `readln`, but at the moment
`CURRENT` changes, it will be accessible again. However, this all has to change
in one shot, so we need to prepare a compiled word for it if we don't want to
lose access to our interpret loop in the middle of our operation.
> : KAWABUNGA!
( 60 == (c<) pointer )
0x9a0 0x60 RAM+ !
0x98f CURRENT !
0x98f HERE !
( 0c == CINPTR )
(find) (c<) DROP 0x0c RAM+ !
;
Ready? Set? KAWABUNGA!
TODO: make this work...
[rc2014]: https://rc2014.co.uk
[romwrite]: https://github.com/hsoft/romwrite

View File

@ -13,17 +13,15 @@ by more than once space or by a newline. Hackish, but works.
int main()
{
int spccnt = 2; // if the first char is a (, consider it a comment opener.
int spccnt = 0;
int incomment = 0;
int c;
c = getchar();
while ( c != EOF ) {
if (c == '\n') {
if (!incomment) {
// We still spit newlines whenever we see them, Forth interpreter
// doesn't like when they're not there...
putchar(c);
}
// We still spit newlines whenever we see them, Forth interpreter
// doesn't like when they're not there...
putchar(c);
spccnt += 2;
} else if (c == ' ') {
spccnt++;