1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-26 13:38:05 +11:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Virgil Dupras
98191d0b0d recipes/rc2014/sdcard: now works under Forth! 2020-04-19 16:56:37 -04:00
Virgil Dupras
dfaa1dc101 link: add "offset" argument to RLDICT
As long as our target was the first word of the "user" dict, using
target's prev to compute offset was fine, but when the target is not
the first word, this system breaks down.

This is the case when, instead of including source code in our boot
binary, we paste it in Collapse OS' prompt.

Also, adjust RC2014 recipe to include stage 3 assembling instructions
with the "paste into prompt" method.
2020-04-19 16:28:40 -04:00
8 changed files with 86 additions and 266 deletions

View File

@ -110,11 +110,11 @@
( TODO implement RLCELL ) ( TODO implement RLCELL )
( Copy dict from target wordref, including header, up to HERE. ( Copy dict from target wordref, including header, up to HERE.
We're going to compact the space between that word and its We're going relocate those words by specified offset. To do
prev word. To do this, we're copying this whole memory area this, we're copying this whole memory area in HERE and then
in HERE and then iterate through that copied area and call iterate through that copied area and call RLWORD on each
RLWORD on each word. That results in a dict that can be word. That results in a dict that can be concatenated to
concatenated to target's prev entry in a more compact way. target's prev entry in a more compact way.
This copy of data doesn't allocate anything, so H@ doesn't This copy of data doesn't allocate anything, so H@ doesn't
move. Moreover, we reserve 4 bytes at H@ to write our target move. Moreover, we reserve 4 bytes at H@ to write our target
@ -129,19 +129,16 @@
possible to reliably detect its end. If you need that last possible to reliably detect its end. If you need that last
word, define a dummy word before calling RLDICT. word, define a dummy word before calling RLDICT.
) )
( target -- ) ( target offset -- )
: RLDICT : RLDICT
( First of all, let's get our offset. It's easy, it's ( First of all, let's get our offset. It's easy, it's
target's prev field, which is already an offset, minus target's prev field, which is already an offset, minus
its name length. We expect, in RLDICT that a target's its name length. We expect, in RLDICT that a target's
prev word is a "hook word", that is, an empty word. ) prev word is a "hook word", that is, an empty word. )
( H@ == target )
DUP H@ !
DUP 1- C@ 0x7f AND ( t namelen )
SWAP 3 - @ ( namelen po )
-^ ( o )
( H@+2 == offset ) ( H@+2 == offset )
H@ 2+ ! ( ) H@ 2+ ! ( target )
( H@ == target )
H@ ! ( )
( We have our offset, now let's copy our memory chunk ) ( We have our offset, now let's copy our memory chunk )
H@ @ WORD( ( src ) H@ @ WORD( ( src )
DUP H@ -^ ( src u ) DUP H@ -^ ( src u )
@ -180,5 +177,9 @@
( Relink a regular Forth full interpreter. ) ( Relink a regular Forth full interpreter. )
: RLCORE : RLCORE
LIT< H@ (find) DROP RLDICT LIT< H@ (find) DROP ( target )
DUP 3 - @ ( t prevoff )
( subtract H@ name length )
2- ( t o )
RLDICT
; ;

View File

@ -210,22 +210,49 @@ That's it! our binary is ready to run!
../../emul/hw/rc2014/classic stage2r.bin ../../emul/hw/rc2014/classic stage2r.bin
And there you have it, a stage2 binary that you've assembled yourself. Now, And there you have it, a stage2 binary that you've assembled yourself.
here's for your homework: use the same technique to add the contents of
`readln.fs` and `adev.fs` to stage2 so that you have a full-featured
interpreter.
Name it `stage3.bin` (the version without any source code appended and no ### Assembling stage 3
`INIT` word defined), you'll need this binary for sub-recipes written for the
RC2014.
Here's a little cheatsheet, but seriously, you should figure most of it Stage 2 gives you a useable prompt, but bare. Because 8K isn't a lot of space
yourself. Tough love they call it. to cram source code, we're limited in what we can include for this stage.
* `cat stage2.bin ../../forth/readln.fs ../../forth/adev.fs run.fs > stage2r.bin` However, now that we have a usable prompt, we can do a lot (be cautious though:
* Don't forget `RDLN$` and `ADEV$`. there is no `readln` yet, so you have no backspace), for example, build a
* `RLDICT` is like `RLCORE` but with a chosen target. stage 3 with `readln`.
* `stripfc` can help you deal with size constraints.
Copy the unit's source
cat ../../forth/readln.fs | ../../tools/stripfc | xclip
and just paste it in your terminal. 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.
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 `' INBUFSZ`. `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 `INBUFSZ` entry (that
is, `' INBUFSZ 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:
' INBUFSZ 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
initializes `RDLN$` instead of creating a minimal `(c<)`.
Keep that `stage3.bin` around, you will need it for further recipes.
[rc2014]: https://rc2014.co.uk [rc2014]: https://rc2014.co.uk
[romwrite]: https://github.com/hsoft/romwrite [romwrite]: https://github.com/hsoft/romwrite

View File

@ -33,24 +33,17 @@ 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.
## Using the at28 driver ## Building your stage 4
The AT28 driver is at `drv/at28.fs` and is a pure forth source file so it's Using the same technique as you used for building your stage 3, you can append
rather easy to set up from the base Stage 3 binary: required words to your boot binary. Required units are `forth/adev.fs` and
`drv/at28.fs`.
cat ../stage3.bin ../pre.fs ../../../drv/at28.fs ../run.fs > os.bin
../../../emul/hw/rc2014/classic os.bin
## Writing contents to the AT28 ## Writing contents to the AT28
The driver provides `AT28!` which can be plugged in adev's `A!*`. The driver provides `AT28!` which can be plugged in adev's `A!*`.
It's not in the Stage 3 binary, but because it's a small piece of Forth code, First, upload your binary to some place in memory, for example `a000`. To do so,
let's just run its definition code:
cat ../../../drv/at28.fs | ./stripfc | ./exec <tty device>
Then, upload your binary to some place in memory, for example `a000`. To do so,
run this from your modern computer: run this from your modern computer:
./upload <tty device> a000 <filename> ./upload <tty device> a000 <filename>

View File

@ -1,7 +1,7 @@
: (c<) KEY DUP EMIT ; : (c<) KEY DUP EMIT ;
: INIT : INIT
ACIA$ ACIA$
." Collapse OS" CR LF ." Collapse OS" CRLF
( 0c == CINPTR ) ( 0c == CINPTR )
['] (c<) 0x0c RAM+ ! ['] (c<) 0x0c RAM+ !
; ;

View File

@ -1,19 +0,0 @@
TARGETS = os.bin cfsin/helo
BASEDIR = ../../..
ZASM = $(BASEDIR)/emul/zasm/zasm
KERNEL = $(BASEDIR)/kernel
APPS = $(BASEDIR)/apps
CFSPACK = $(BASEDIR)/tools/cfspack/cfspack
.PHONY: all
all: $(TARGETS) sdcard.cfs
os.bin: glue.asm
cfsin/helo: helo.asm
$(TARGETS):
$(ZASM) $(KERNEL) $(APPS) < glue.asm > $@
$(CFSPACK):
make -C $(BASEDIR)/tools/cfspack
sdcard.cfs: cfsin $(CFSPACK)
$(CFSPACK) cfsin > $@

View File

@ -8,7 +8,7 @@ You can't really keep pins high and low on an IO line. You need some kind of
intermediary between z80 IOs and SPI. intermediary between z80 IOs and SPI.
There are many ways to achieve this. This recipe explains how to build your own There are many ways to achieve this. This recipe explains how to build your own
hacked off SPI relay for the RC2014. It can then be used with `sdc.asm` to hacked off SPI relay for the RC2014. It can then be used with `sdc.fs` to
drive a SD card. drive a SD card.
## Goal ## Goal
@ -18,10 +18,8 @@ design.
## Gathering parts ## Gathering parts
* A RC2014 with Collapse OS with these features: * A RC2014 Classic
* shell * `stage3.bin` from the base recipe
* blockdev
* sdc
* A MicroSD breakout board. I use Adafruit's. * A MicroSD breakout board. I use Adafruit's.
* A proto board + header pins with 39 positions so we can make a RC2014 card. * A proto board + header pins with 39 positions so we can make a RC2014 card.
* Diodes, resistors and stuff * Diodes, resistors and stuff
@ -34,7 +32,7 @@ design.
## Building the SPI relay ## Building the SPI relay
The [schematic][schematic] supplied with this recipe works well with `sdc.asm`. The [schematic][schematic] supplied with this recipe works well with `sdc.fs`.
Of course, it's not the only possible design that works, but I think it's one Of course, it's not the only possible design that works, but I think it's one
of the most straighforwards. of the most straighforwards.
@ -71,95 +69,41 @@ matter. However, it *does* matter for the `SELECT` line, so I don't follow my
own schematic with regards to the `M1` and `A2` lines and use two inverters own schematic with regards to the `M1` and `A2` lines and use two inverters
instead. instead.
## Building the kernel ## Building your stage 4
To be able to work with your SPI relay and communicate with the card, you Using the same technique as you used for building your stage 3, you can append
should have [glue code that looks like this](glue.asm). required words to your boot binary. Required units are `forth/blk.fs` and
`drv/sdc.fs`. You also need `drv/sdc.z80` but to save you the troubles of
rebuilding from stage 1 for this recipe, we took the liberty of already having
included it in the base recipe.
Initially, when you don't know if things work well yet, you should comment out ## Testing in the emulator
the block creation part.
## Reading from the SD card The RC2014 emulator includes SDC emulation. You can attach a SD card image to
it by invoking it with a second argument:
The first thing we'll do is fill the SD card's first 12 bytes with "Hello ../../../emul/hw/rc2014/classic stage4.bin ../../../emul/blkfs
World!":
echo "Hello World!" > /dev/sdX You will then run with a SD card having the contents from `/blk`.
Then, insert your SD card in your SPI relay and boot the RC2014. ## Usage
Run the `sdci` command which will initialize the card. The blockdev 0 is First, the SD card needs to be initialized
already selected at initialization, but you could, to be sure, run `bsel 0` to
select the first blockdev, which is configured to be the sd card.
Set your memory pointer to somewhere you can write to with `mptr 9000` and then SDC$
you're ready to load your contents with `load d` (load the 13 bytes that you
wrote to your sd card earlier. You can then `peek d` and see that your
"Hello World!\n" got loaded in memory!
## Mounting a filesystem from the SD card If there is no error message, we're fine. Then, we need to hook `BLK@*` and
`BLK!*` into the SDC driver:
The Makefile compiles `helo.asm` in `cfsin` and then packs `cfsin` into a CFS ' SDC@ BLK@* !
filesystem into the `sdcard.cfs` file. That can be mounted by Collapse OS! ' SDC! BLK!* !
$ cat sdcard.cfs > /dev/sdX And thats it! You have full access to disk block mechanism:
Then, you insert your SD card in your SPI relay and go: 102 LOAD
BROWSE
Collapse OS (at this moment, the driver is a bit slow though...)
> sdci
> fson
> fls
helo
hello.txt
> helo
Hello!
>
The `helo` command is a bit magical and is due to the hook implemented in
`pgm.asm`: when an unknown command is typed, it looks in the currently mounted
filesystem for a file with the same name. If it finds it, it loads it in memory
at a predefined place (in our case, `0x9000`) and executes it.
Now let that sink in for a minute. You've just mounted a filesystem on a SD
card, loaded a file from it in memory and executed that file, all that on a
kernel that weights less than 3 kilobytes!
## Writing to a file in the SD card
Now what we're going to do is to write back to a file on the SD card. From a
system with the SD card initialized and the FS mounted, do:
> fopn 0 hello.txt
> bsel 1
> mptr 9000
9000
> load d
> peek d
48656C6C6F20576F726C64210A
Now that we have our "Hello World!\n" loaded in memory, let's modify it and make
it start with "XXX" and save it to the file. `sdcf` flushes the current SD card
buffer to the card. It's automatically ran whenever we change sector during a
read/write/seek, but was can also explicitly call it with `sdcf`.
> poke 3
[type "XXX"]
> peek d
5858586C6F20576F726C64210A
> seek 00 0000
0000
> save d
> sdcf
The new "XXXlo World!\n" is now written to the card, at its proper place in CFS!
You can verify this by pulling out the card (no need to unmount it from Collapse
OS, but if you insert it again, you'll need to run `sdci` again), insert it in
your modern system and run:
$ head -c 512 /dev/sdX | xxd
You'll see your "XXXlo World!\n" somewhere, normally at offset `0x120`!
[schematic]: spirelay/spirelay.pdf [schematic]: spirelay/spirelay.pdf
[inspiration]: https://www.ecstaticlyrics.com/electronics/SPI/fast_z80_interface.html [inspiration]: https://www.ecstaticlyrics.com/electronics/SPI/fast_z80_interface.html

View File

@ -1,114 +0,0 @@
; classic RC2014 setup (8K ROM + 32K RAM) and a stock Serial I/O module
; The RAM module is selected on A15, so it has the range 0x8000-0xffff
.equ RAMSTART 0x8000
.equ RAMEND 0xffff
.equ ACIA_CTL 0x80 ; Control and status. RS off.
.equ ACIA_IO 0x81 ; Transmit. RS on.
.equ USER_CODE 0xa000
jp init ; 3 bytes
; *** Jump Table ***
jp printstr
jp sdcWaitResp
jp sdcCmd
jp sdcCmdR1
jp sdcCmdR7
jp sdcSendRecv
; interrupt hook
.fill 0x38-$
jp aciaInt
.inc "err.h"
.inc "ascii.h"
.inc "blkdev.h"
.inc "fs.h"
.inc "core.asm"
.inc "str.asm"
.equ ACIA_RAMSTART RAMSTART
.inc "acia.asm"
.equ BLOCKDEV_RAMSTART ACIA_RAMEND
.equ BLOCKDEV_COUNT 2
.inc "blockdev.asm"
; List of devices
.dw sdcGetB, sdcPutB
.dw blk2GetB, blk2PutB
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
.equ STDIO_GETC aciaGetC
.equ STDIO_PUTC aciaPutC
.inc "stdio.asm"
.equ FS_RAMSTART STDIO_RAMEND
.equ FS_HANDLE_COUNT 1
.inc "fs.asm"
; *** BASIC ***
; RAM space used in different routines for short term processing.
.equ SCRATCHPAD_SIZE STDIO_BUFSIZE
.equ SCRATCHPAD FS_RAMEND
.inc "lib/util.asm"
.inc "lib/ari.asm"
.inc "lib/parse.asm"
.inc "lib/fmt.asm"
.equ EXPR_PARSE parseLiteralOrVar
.inc "lib/expr.asm"
.inc "basic/util.asm"
.inc "basic/parse.asm"
.inc "basic/tok.asm"
.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE
.inc "basic/var.asm"
.equ BUF_RAMSTART VAR_RAMEND
.inc "basic/buf.asm"
.inc "basic/blk.asm"
.inc "basic/sdc.asm"
.equ BFS_RAMSTART BUF_RAMEND
.inc "basic/fs.asm"
.equ BAS_RAMSTART BFS_RAMEND
.inc "basic/main.asm"
.equ SDC_RAMSTART BAS_RAMEND
.equ SDC_PORT_CSHIGH 6
.equ SDC_PORT_CSLOW 5
.equ SDC_PORT_SPI 4
.inc "sdc.asm"
init:
di
ld sp, RAMEND
im 1
call aciaInit
call fsInit
call basInit
ld hl, basFindCmdExtra
ld (BAS_FINDHOOK), hl
xor a
ld de, BLOCKDEV_SEL
call blkSel
ei
jp basStart
basFindCmdExtra:
ld hl, basFSCmds
call basFindCmd
ret z
ld hl, basBLKCmds
call basFindCmd
ret z
ld hl, basSDCCmds
jp basFindCmd
; *** blkdev 2: file handle 0 ***
blk2GetB:
ld ix, FS_HANDLES
jp fsGetB
blk2PutB:
ld ix, FS_HANDLES
jp fsPutB

View File

@ -1,12 +0,0 @@
; prints "Hello!" on screen
.equ printstr 0x03
.org 0x9000
ld hl, sHello
call printstr
xor a ; success
ret
sHello:
.db "Hello!", 0x0d, 0x0a, 0