recipes/rc2014: include readln directly in stage 1

XPACK is more efficient than stripfc was, we can pack more stuff in 8K.
We now have enough space to fit readln.
This commit is contained in:
Virgil Dupras 2020-04-26 14:52:55 -04:00
parent dee7eea497
commit 7c692c1111
4 changed files with 54 additions and 145 deletions

View File

@ -1,87 +0,0 @@
( requires core, parse, print )
( Managing variables in a core module is tricky. Sure, we
have (sysv), but here we need to allocate a big buffer, and
that cannot be done through (sysv). What we do is that we
allocate that buffer at runtime and use (sysv) to point to
it, a pointer that is set during the initialization
routine. )
64 CONSTANT INBUFSZ
: RDLNMEM+ 0x53 RAM+ @ + ;
( current position in INBUF )
: IN> 0 RDLNMEM+ ;
( points to INBUF )
: IN( 2 RDLNMEM+ ;
( points to INBUF's end )
: IN) INBUFSZ 2+ RDLNMEM+ ;
( flush input buffer )
( set IN> to IN( and set IN> @ to null )
: (infl) 0 IN( DUP IN> ! ! ;
( handle backspace: go back one char in IN>, if possible, then
emit SPC + BS )
: (inbs)
( already at IN( ? )
IN> @ IN( = IF EXIT THEN
IN> @ 1- IN> !
SPC BS
;
( read one char into input buffer and returns whether we
should continue, that is, whether CR was not met. )
: (rdlnc) ( -- f )
( buffer overflow? same as if we typed a newline )
IN> @ IN) = IF 0x0a ELSE KEY THEN ( c )
( del? same as backspace )
DUP 0x7f = IF DROP 0x8 THEN
( lf? same as cr )
DUP 0x0a = IF DROP 0xd THEN
( echo back )
DUP EMIT ( c )
( bacspace? handle and exit )
DUP 0x8 = IF (inbs) EXIT THEN
( write and advance )
DUP ( keep as result ) ( c c )
( Here, we take advantage of the fact that c's MSB is
always zero and thus ! automatically null-terminates
our string )
IN> @ ! 1 IN> +! ( c )
( if newline, replace with zero to indicate EOL )
DUP 0xd = IF DROP 0 THEN
;
( Read one line in input buffer and make IN> point to it )
: (rdln)
(infl)
BEGIN (rdlnc) NOT UNTIL
LF IN( IN> !
;
( And finally, implement C<* )
: RDLN<
IN> @ C@
DUP IF
( not EOL? good, inc and return )
1 IN> +!
ELSE
( EOL ? readline. we still return null though )
(rdln)
THEN
( update C<? flag )
IN> @ C@ 0 > 0x06 RAM+ ! ( 06 == C<? )
;
( Initializes the readln subsystem )
: RDLN$
( 53 == rdln's memory )
H@ 0x53 RAM+ !
( 2 for IN>, plus 2 for extra bytes after buffer: 1 for
the last typed 0x0a and one for the following NULL. )
INBUFSZ 4 + ALLOT
(infl)
['] RDLN< 0x0c RAM+ !
1 0x06 RAM+ ! ( 06 == C<? )
;

View File

@ -64,12 +64,10 @@ 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 `os.bin`.
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 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
@ -122,15 +120,18 @@ 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 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:
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
@ -212,48 +213,6 @@ That's it! our binary is ready to run!
And there you have it, a stage2 binary that you've assembled yourself.
### Assembling stage 3
Stage 2 gives you a useable prompt, but bare. Because 8K isn't a lot of space
to cram source code, we're limited in what we can include for this stage.
However, now that we have a usable prompt, we can do a lot (be cautious though:
there is no `readln` yet, so you have no backspace), for example, build a
stage 3 with `readln`.
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
[romwrite]: https://github.com/hsoft/romwrite
[stage2]: ../../emul

View File

@ -8,7 +8,7 @@ itself.
## Gathering parts
* A RC2014 Classic
* `stage3.bin` from the base recipe
* `stage2.bin` from the base recipe
* An extra AT28C64B
* 1x 40106 inverter gates
* Proto board, RC2014 header pins, wires, IC sockets, etc.
@ -33,11 +33,49 @@ in write protection mode, but I preferred building my own module.
I don't think you need a schematic. It's really simple.
## Building your stage 4
### Assembling stage 3
Using the same technique as you used for building your stage 3, you can append
required words to your boot binary. Required units are `forth/adev.fs` and
`drv/at28.fs`.
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.
TODO: fix these instructions. They are broken.
However, now that we have a usable prompt, we can do a lot (be cautious though:
there is no `readln` yet, so you have no backspace), for example, build a
stage 3 with `readln`.
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.
## Writing contents to the AT28

View File

@ -28,8 +28,7 @@ H@ XOFF @ - XOFF @ 8 + !
446 452 XPACKR ( parse )
358 360 XPACKR ( acia.fs )
442 445 XPACKR ( print )
459 463 XPACKR ( fmt )
453 463 XPACKR ( readln fmt )
123 132 XPACKR ( linker )
," : x KEY DUP EMIT ; "
," : _ ACIA$ (ok) ['] x 0x0c RAM+ ! ; _ "
," : _ ACIA$ RDLN$ (ok) ; _ "
H@ 256 /MOD 2 PC! 2 PC!