rc2014: adapt recipe to single stage xcomp
It's now much easier...
This commit is contained in:
parent
b0258f5bba
commit
0703da928e
@ -1,4 +1,4 @@
|
||||
TARGET = stage1.bin
|
||||
TARGET = os.bin
|
||||
BASEDIR = ../..
|
||||
FDIR = $(BASEDIR)/forth
|
||||
EDIR = $(BASEDIR)/emul
|
||||
|
@ -40,7 +40,7 @@ device I use in this recipe.
|
||||
### Gathering parts
|
||||
|
||||
* A "classic" RC2014 with Serial I/O
|
||||
* [Forth's stage 2 binary][stage2]
|
||||
* [Forth's stage binary][stage]
|
||||
* [romwrite][romwrite] and its specified dependencies
|
||||
* [GNU screen][screen]
|
||||
* A FTDI-to-TTL cable to connect to the Serial I/O module
|
||||
@ -50,24 +50,10 @@ device I use in this recipe.
|
||||
Modules used in this build are configured through the `conf.fs` file in this
|
||||
folder. There isn't much to configure, but it's there.
|
||||
|
||||
### Build stage 1
|
||||
### Build the binary
|
||||
|
||||
Self-bootstrapping is in Forth's DNA, which is really nice, but it makes
|
||||
cross-compiling a bit tricky. It's usually much easier to bootstrap a Forth
|
||||
from itself than trying to compile it from a foreign host.
|
||||
|
||||
This makes us adopt a 2 stages strategy. A tiny core is built from a foreign
|
||||
host, and then we run that tiny core on the target machine and let it bootstrap
|
||||
itself, then write our full interpreter binary.
|
||||
|
||||
We could have this recipe automate that 2 stage build process all automatically,
|
||||
but that would rob you of all your fun, right? Instead, we'll run that 2nd
|
||||
stage on the RC2014 itself!
|
||||
|
||||
To build your stage 1, run `make` in this folder, this will yield `stage1.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 a full Forth interpreter.
|
||||
Building the binary is as simple as running `make`. This will yield `os.bin`
|
||||
which can then be written to EEPROM.
|
||||
|
||||
### Emulate
|
||||
|
||||
@ -100,131 +86,9 @@ identify the tty bound to it (in my case, `/dev/ttyUSB0`). Then:
|
||||
|
||||
screen /dev/ttyUSB0 115200
|
||||
|
||||
Press the reset button on the RC2014 to have Forth begin its bootstrap process.
|
||||
Note that it has to build more than half of itself from source. It takes about
|
||||
30 seconds to complete.
|
||||
|
||||
Once bootstrapping is done you should see the Collapse OS prompt. That's a full
|
||||
Forth interpreter. You can have fun right now.
|
||||
|
||||
However, that long boot time is kinda annoying. Moreover, that bootstrap code
|
||||
being in source form takes precious space from our 8K ROM. That brings us to
|
||||
building stage 2.
|
||||
|
||||
### Building stage 2
|
||||
|
||||
You're about to learn a lot about this platform and its self-bootstrapping
|
||||
nature, but its a bumpy ride. Grab something. Why not a beer?
|
||||
|
||||
Our stage 1 prompt is the result of Forth's inner core interpreting the source
|
||||
code of the Full Forth, which was appended to the binary inner core in ROM.
|
||||
This results in a compiled dictionary, in RAM, at address 0x8000+system RAM.
|
||||
|
||||
Wouldn't it be great if we could save that compiled binary in ROM and save the
|
||||
system the trouble of recompiling itself on boot?
|
||||
|
||||
Unfortunately, this compiled dictionary isn't usable as-is. Offsets compiled in
|
||||
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 the Linker (B120)
|
||||
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:
|
||||
|
||||
RLCORE
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
### Assembling the stage 2 binary
|
||||
|
||||
At that point, we have a fully relocated binary in memory. Depending on our
|
||||
situations, the next steps differ.
|
||||
|
||||
* 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.
|
||||
|
||||
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 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.
|
||||
|
||||
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 `0042`, the address for
|
||||
`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,
|
||||
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`.
|
||||
|
||||
So, the end of our compiled dict is actually `99de`. Alright, let's extract it:
|
||||
|
||||
dd if=memdump bs=1 skip=36192 count=3198 > dict.bin
|
||||
|
||||
`36192` is `8d60` and `3198` is `99de-8d60`. This needs to be prepended by the
|
||||
boot binary. We already have `stage1.bin`, but this binary contains bootstrap
|
||||
source code we don't need any more. To strip it, we'll need to `dd` it out to
|
||||
`LATEST`, in my case `098b`:
|
||||
|
||||
dd if=stage1.bin bs=1 count=2443 > s1pre.bin
|
||||
|
||||
Now we can combine our binaries:
|
||||
|
||||
cat s1pre.bin dict.bin > stage2.bin
|
||||
|
||||
Is it ready to run yet? no. There are 3 adjustments we need to manually make
|
||||
using our hex editor.
|
||||
|
||||
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`.
|
||||
|
||||
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 initialization code. We've stripped it from our stage1 earlier,
|
||||
we need to re-add it.
|
||||
|
||||
Look at `xcomp.fs`. You see that `," bla bla bla"` line? That's initialization
|
||||
code. Copy it to a file like `run.fs` (without the `,"`) and build your final
|
||||
binary:
|
||||
|
||||
cat stage2.bin run.fs > stage2r.bin
|
||||
|
||||
That's it! our binary is ready to run!
|
||||
|
||||
../../emul/hw/rc2014/classic stage2r.bin
|
||||
|
||||
And there you have it, a stage2 binary that you've assembled yourself.
|
||||
Press the reset button on the RC2014 and the "ok" prompt should appear.
|
||||
|
||||
[rc2014]: https://rc2014.co.uk
|
||||
[romwrite]: https://github.com/hsoft/romwrite
|
||||
[stage2]: ../../emul
|
||||
[stage]: ../../emul
|
||||
[screen]: https://www.gnu.org/software/screen/
|
||||
|
@ -8,7 +8,6 @@ itself.
|
||||
## Gathering parts
|
||||
|
||||
* A RC2014 Classic
|
||||
* `stage2.bin` from the base recipe
|
||||
* An extra AT28C64B
|
||||
* 1x 40106 inverter gates
|
||||
* Proto board, RC2014 header pins, wires, IC sockets, etc.
|
||||
@ -33,46 +32,21 @@ in write protection mode, but I preferred building my own module.
|
||||
|
||||
I don't think you need a schematic. It's really simple.
|
||||
|
||||
### Assembling stage 3
|
||||
### Building the binary
|
||||
|
||||
Stage 2 gives you a full interpreter, but it's missing the "Addressed devices"
|
||||
module and the AT28 driver. We'll need to assemble a stage 3.
|
||||
You build the binary by modifying the base recipe's `xcomp` unit. This binary
|
||||
is missing 2 things: Addressed devices and the AT28 Driver.
|
||||
|
||||
When you'll have a system with function disk block system, you'll be able to
|
||||
directly `LOAD` them, but for this recipe, we can't assume you have, so what
|
||||
you'll have to do is to manually paste the code from the appropriate blocks.
|
||||
|
||||
Addressed devices are at B140. To know what you have to paste, open the loader
|
||||
block (B142) and see what blocks it loads. For each of the blocks, copy/paste
|
||||
the code in your interpreter.
|
||||
Addressed devices are at B140. If you read that block, you'll see that it tells
|
||||
you to load block 142. Open the `xcomp` unit and locate the ACIA driver loading
|
||||
line. Insert your new load line after that one.
|
||||
|
||||
Do the same thing with the AT28 driver (B590)
|
||||
|
||||
If you're doing the real thing and not using the emulator, pasting so much code
|
||||
at once might freeze up the RC2014, so it is recommended that you use
|
||||
`/tools/exec` that let the other side enough time to breathe.
|
||||
You also have to modify the initialization sequence at the end of the `xcomp`
|
||||
unit to include `ADEV$`.
|
||||
|
||||
After your pasting, you'll have a compiled dict of that code in memory. You'll
|
||||
need to relocate it in the same way you did for stage 2, but instead of using
|
||||
`RLCORE`, which is a convenience word hardcoded for stage 1, we'll parametrize
|
||||
`RLDICT`, the word doing the real work.
|
||||
|
||||
`RLDICT` takes 2 arguments, `target` and `offset`. `target` is the first word
|
||||
of your relocated dict. In our case, it's going to be `' ADEVMEM+`. `offset` is
|
||||
the offset we'll apply to every eligible word references in our dict. In our
|
||||
case, that offset is the offset of the *beginning* of the `ADEVMEM+` entry (that
|
||||
is, `' ADEVMEM+ WORD(` minus the offset of the last word (which should be a hook
|
||||
word) in the ROM binary.
|
||||
|
||||
That offset can be conveniently fetched from code because it is the value of
|
||||
the `LATEST` constant in stable ABI, which is at offset `0x08`. Therefore, our
|
||||
offset value is:
|
||||
|
||||
' ADEVMEM+ WORD( 0x08 @ -
|
||||
|
||||
You can now run `RLDICT` and proceed with concatenation (and manual adjustments
|
||||
of course) as you did with stage 2. Don't forget to adjust `run.fs` so that it
|
||||
runs `ADEV$`.
|
||||
Build again, write `os.com` to EEPROM.
|
||||
|
||||
## Writing contents to the AT28
|
||||
|
||||
|
@ -70,35 +70,23 @@ instead.
|
||||
|
||||
## Building your binary
|
||||
|
||||
Your Collapse OS binary needs the SDC drivers which need to be inserted during
|
||||
Cross Compilation, which needs you need to recompile it from stage 1. First,
|
||||
look at B600. You'll see that it indicates a block range for the driver. That
|
||||
needs to be loaded.
|
||||
|
||||
Open xcomp.fs from base recipe and locate acia loading. You'll insert a line
|
||||
right after that that will look like:
|
||||
The binary built in the base recipe doesn't have SDC drivers. Using the same
|
||||
instructions as in the `eeprom` recipe, you'll need to insert those drivers.
|
||||
The SDC driver is at B600. It gives you a load range. This means that what
|
||||
you need to insert in `xcomp` will look like:
|
||||
|
||||
602 616 LOADR ( sdc )
|
||||
|
||||
Normally, that's all you need to do. However, you have a little problem: You're
|
||||
busting the 8K ROM limit. But it's ok, you can remove the linker's XPACKing
|
||||
line: because you'll have access to the blkfs from SD card, you can load it
|
||||
from there!
|
||||
|
||||
Removing the linker from XPACKing will free enough space for your binary to fit
|
||||
in 8K. You also have to add `BLK$` to initialization routine.
|
||||
You also need to add `BLK$` to the init sequence.
|
||||
|
||||
Build it and write it to EEPROM.
|
||||
|
||||
If you want, once you're all set with the SD card, you can relink core words
|
||||
like you did in the base recipe for optimal resource usage.
|
||||
|
||||
## Testing in the emulator
|
||||
|
||||
The RC2014 emulator includes SDC emulation. You can attach a SD card image to
|
||||
it by invoking it with a second argument:
|
||||
|
||||
../../../emul/hw/rc2014/classic stage3.bin ../../../emul/blkfs
|
||||
../../../emul/hw/rc2014/classic os.bin ../../../emul/blkfs
|
||||
|
||||
You will then run with a SD card having the contents from `/blk`.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user