1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-02 14:30: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 ) ( a o ol -- a+n )
: RLATOM : RLATOM
ROT ( o ol a ) ROT ( o ol a )
DUP @ ( o ol a n ) DUP @ 0x24 = IF
DUP 0x24 = IF ( addrWord? we need to offset it )
( 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
2 + ( o ol a+2 ) 2 + ( o ol a+2 )
ROT ROT 2DROP ( a ) ROT OVER ( ol a o a )
EXIT @ -^ ( ol a n-o )
OVER ! ( ol a )
SWAP DROP ( a )
2 + ( a+2 )
EXIT ( no need for ASKIP )
THEN THEN
DUP @ ( o ol a n )
ROT ( o a n ol ) ROT ( o a n ol )
< IF ( under limit, do nothing ) < IF ( under limit, do nothing )
SWAP DROP ( a ) 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`. 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 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 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 get a prompt to a full Forth interpreter.
8K to fit both link.fs and readln.fs, so we ditch readln. Our prompt is raw. No
backspace no buffer. Hardcore mode.)
### Emulate ### 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 0xa00-or-so base offset, that is, something suitable to be appended to the boot
binary, in ROM, in binary form. binary, in ROM, in binary form.
Fortunately, inside the compiled source is the contents of link.fs which will We can't simply adjust offsets. For complicated reasons, that can't be reliably
allow us to relink our compiled dictionary so that in can be relocated in ROM, done. We have to re-interpret that same source code, but from a ROM offset. But
next to our boot binary. I won't go into relinking details. Look at the source. how are we going to do that? After all, ROM is called ROM for a reason.
For now, let's just use it:
RLCORE Memory maps.
That command will take the dict from `' H@` up to `CURRENT`, copy it in free What we're going to do is to set up a memory map targeting our ROM and point it
memory and then relocate it. It will print 3 addresses during its processing. 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 Alright, let's do this. First, let's have a look around. Where is the end of
above this point. The second address is the wordref of the last copied entry. our boot binary? To know, find the word ";", which is the last word of icore:
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).
### 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 See that `_` at 0x98b? That's the name of our hook word. 4 bytes later is its
situations, the next steps differ. 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, Right after that is our appended source code. The first part is `pre.fs` and
we'll want to assemble that binary directly on the RC2014 and write it to can be ignored. What we want starts at the definition of the `H@` word, which
permanent storage. is at 0x9a0. Another address to write down.
* 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.
In these instructions, we assume an emulated environment. I'll use actual So our memory map will target 0x98f. Where will we place it? It doesn't matter
offsets of an actual assembling session, but these of course are only examples. much, we have plenty of RAM. Where's `HERE`?
It is very likely that these will not be the same offsets for you.
So you've pressed `CTRL+E` and you have a `memdump` file. Open it with a hex > H@ .X
editor (I like `hexedit`) to have a look around and to decide what we'll extract 8c3f>
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.
If you look at data between `99f6` and `9a3c`, you'll see that this data is not Alright, let's go wide and use 0xa000 as our map destination. But before we do,
100% dictionary entry material. Some of it is buffer data allocated at let's copy the content of our ROM into RAM because there's our source code
initialization. To locate the end of a word, look for `0043`, the address for there and if we don't copy it before setting up the memory map, we'll shadow it.
`EXIT`. In my case, it's at `9a1a` and it's the end of the `INIT` word.
Moreover, the `INIT` routine that is in there is not quite what we want, Let's be lazy and don't even check where the source stop. Let's assume it stops
because it doesn't contain the `HERE` adjustment that we find in `pre.fs`. at 0x1fff, the end of the ROM.
We'll want to exclude it from our binary, so let's go a bit further, at `99cf`,
ending at `99de`.
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 > : MMAP
boot binary. But that one, we already have. It's `z80c.bin` 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 > ' MMAP (mmap*) !
using our hex editor. > 64 0x980 DUMP
1. We need to link `H@` to the hook word of the boot binary. In my case, it's :80 0043 0093 07f4 03ef .C......
a matter of writing `02` at `08ec` and `00` at `08ed`, `H@`'s prev field. :88 0143 005f 0f00 0131 .C._...1
2. We need to end our binary with a hook word. It can have a zero-length name :90 3132 2052 414d 2b20 12 RAM+
and the prev field needs to properly point to the previous wordref. In my :98 4845 5245 2021 0a20 HERE !.
case, that was `RLCORE` at offset `1559` for a `stage2.bin` size of `1568`, :a0 3a20 4840 2048 4552 : H@ HER
which means that I appended `0F 00 00` at the end of the file. :a8 4520 4020 3b0a 203a E @ ;. :
3. Finally, we need to adjust `LATEST` which is at offset `08`. This needs to :b0 202d 5e20 5357 4150 -^ SWAP
point to the last wordref of the file, which is equal to the length of :b8 202d 203b 0a20 3a20 - ;. :
`stage2.bin` because we've just added a hook word. This means that we write
`6B` at offset `08` and `15` at offset `09`.
Now are we ready yet? ALMOST! There's one last thing we need to do: add runtime But how do we know that it really works? Because we can write in ROM!
source. In our case, because we have a compiled dict, the only source we need
to include is `pre.fs` and `run.fs`:
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, We're now ready for a re-bootstrap. Here's what we're gonna do:
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. 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 [rc2014]: https://rc2014.co.uk
[romwrite]: https://github.com/hsoft/romwrite [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 main()
{ {
int spccnt = 2; // if the first char is a (, consider it a comment opener. int spccnt = 0;
int incomment = 0; int incomment = 0;
int c; int c;
c = getchar(); c = getchar();
while ( c != EOF ) { while ( c != EOF ) {
if (c == '\n') { if (c == '\n') {
if (!incomment) { // We still spit newlines whenever we see them, Forth interpreter
// We still spit newlines whenever we see them, Forth interpreter // doesn't like when they're not there...
// doesn't like when they're not there... putchar(c);
putchar(c);
}
spccnt += 2; spccnt += 2;
} else if (c == ' ') { } else if (c == ' ') {
spccnt++; spccnt++;