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 = ../..
|
BASEDIR = ../..
|
||||||
FDIR = $(BASEDIR)/forth
|
FDIR = $(BASEDIR)/forth
|
||||||
EDIR = $(BASEDIR)/emul
|
EDIR = $(BASEDIR)/emul
|
||||||
|
@ -40,7 +40,7 @@ device I use in this recipe.
|
|||||||
### Gathering parts
|
### Gathering parts
|
||||||
|
|
||||||
* A "classic" RC2014 with Serial I/O
|
* A "classic" RC2014 with Serial I/O
|
||||||
* [Forth's stage 2 binary][stage2]
|
* [Forth's stage binary][stage]
|
||||||
* [romwrite][romwrite] and its specified dependencies
|
* [romwrite][romwrite] and its specified dependencies
|
||||||
* [GNU screen][screen]
|
* [GNU screen][screen]
|
||||||
* A FTDI-to-TTL cable to connect to the Serial I/O module
|
* 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
|
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.
|
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
|
Building the binary is as simple as running `make`. This will yield `os.bin`
|
||||||
cross-compiling a bit tricky. It's usually much easier to bootstrap a Forth
|
which can then be written to EEPROM.
|
||||||
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.
|
|
||||||
|
|
||||||
### Emulate
|
### Emulate
|
||||||
|
|
||||||
@ -100,131 +86,9 @@ identify the tty bound to it (in my case, `/dev/ttyUSB0`). Then:
|
|||||||
|
|
||||||
screen /dev/ttyUSB0 115200
|
screen /dev/ttyUSB0 115200
|
||||||
|
|
||||||
Press the reset button on the RC2014 to have Forth begin its bootstrap process.
|
Press the reset button on the RC2014 and the "ok" prompt should appear.
|
||||||
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.
|
|
||||||
|
|
||||||
[rc2014]: https://rc2014.co.uk
|
[rc2014]: https://rc2014.co.uk
|
||||||
[romwrite]: https://github.com/hsoft/romwrite
|
[romwrite]: https://github.com/hsoft/romwrite
|
||||||
[stage2]: ../../emul
|
[stage]: ../../emul
|
||||||
[screen]: https://www.gnu.org/software/screen/
|
[screen]: https://www.gnu.org/software/screen/
|
||||||
|
@ -8,7 +8,6 @@ itself.
|
|||||||
## Gathering parts
|
## Gathering parts
|
||||||
|
|
||||||
* A RC2014 Classic
|
* A RC2014 Classic
|
||||||
* `stage2.bin` from the base recipe
|
|
||||||
* An extra AT28C64B
|
* An extra AT28C64B
|
||||||
* 1x 40106 inverter gates
|
* 1x 40106 inverter gates
|
||||||
* Proto board, RC2014 header pins, wires, IC sockets, etc.
|
* 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.
|
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"
|
You build the binary by modifying the base recipe's `xcomp` unit. This binary
|
||||||
module and the AT28 driver. We'll need to assemble a stage 3.
|
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
|
Addressed devices are at B140. If you read that block, you'll see that it tells
|
||||||
directly `LOAD` them, but for this recipe, we can't assume you have, so what
|
you to load block 142. Open the `xcomp` unit and locate the ACIA driver loading
|
||||||
you'll have to do is to manually paste the code from the appropriate blocks.
|
line. Insert your new load line after that one.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Do the same thing with the AT28 driver (B590)
|
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
|
You also have to modify the initialization sequence at the end of the `xcomp`
|
||||||
at once might freeze up the RC2014, so it is recommended that you use
|
unit to include `ADEV$`.
|
||||||
`/tools/exec` that let the other side enough time to breathe.
|
|
||||||
|
|
||||||
After your pasting, you'll have a compiled dict of that code in memory. You'll
|
Build again, write `os.com` to EEPROM.
|
||||||
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$`.
|
|
||||||
|
|
||||||
## Writing contents to the AT28
|
## Writing contents to the AT28
|
||||||
|
|
||||||
|
@ -70,35 +70,23 @@ instead.
|
|||||||
|
|
||||||
## Building your binary
|
## Building your binary
|
||||||
|
|
||||||
Your Collapse OS binary needs the SDC drivers which need to be inserted during
|
The binary built in the base recipe doesn't have SDC drivers. Using the same
|
||||||
Cross Compilation, which needs you need to recompile it from stage 1. First,
|
instructions as in the `eeprom` recipe, you'll need to insert those drivers.
|
||||||
look at B600. You'll see that it indicates a block range for the driver. That
|
The SDC driver is at B600. It gives you a load range. This means that what
|
||||||
needs to be loaded.
|
you need to insert in `xcomp` will look like:
|
||||||
|
|
||||||
Open xcomp.fs from base recipe and locate acia loading. You'll insert a line
|
|
||||||
right after that that will look like:
|
|
||||||
|
|
||||||
602 616 LOADR ( sdc )
|
602 616 LOADR ( sdc )
|
||||||
|
|
||||||
Normally, that's all you need to do. However, you have a little problem: You're
|
You also need to add `BLK$` to the init sequence.
|
||||||
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.
|
|
||||||
|
|
||||||
Build it and write it to EEPROM.
|
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
|
## Testing in the emulator
|
||||||
|
|
||||||
The RC2014 emulator includes SDC emulation. You can attach a SD card image to
|
The RC2014 emulator includes SDC emulation. You can attach a SD card image to
|
||||||
it by invoking it with a second argument:
|
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`.
|
You will then run with a SD card having the contents from `/blk`.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user