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:
parent
dee7eea497
commit
7c692c1111
@ -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<? )
|
|
||||||
;
|
|
||||||
|
|
@ -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
|
but that would rob you of all your fun, right? Instead, we'll run that 2nd
|
||||||
stage on the RC2014 itself!
|
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
|
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
|
||||||
|
|
||||||
@ -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.
|
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.
|
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
|
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
|
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
|
Fortunately, inside the compiled source is the contents of the Linker (B120)
|
||||||
allow us to relink our compiled dictionary so that in can be relocated in ROM,
|
which will allow us to relink our compiled dictionary so that in can be
|
||||||
next to our boot binary. I won't go into relinking details. Look at the source.
|
relocated in ROM, next to our boot binary. I won't go into relinking details.
|
||||||
For now, let's just use it:
|
Look at the source. For now, let's just use it:
|
||||||
|
|
||||||
RLCORE
|
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.
|
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
|
[rc2014]: https://rc2014.co.uk
|
||||||
[romwrite]: https://github.com/hsoft/romwrite
|
[romwrite]: https://github.com/hsoft/romwrite
|
||||||
[stage2]: ../../emul
|
[stage2]: ../../emul
|
||||||
|
@ -8,7 +8,7 @@ itself.
|
|||||||
## Gathering parts
|
## Gathering parts
|
||||||
|
|
||||||
* A RC2014 Classic
|
* A RC2014 Classic
|
||||||
* `stage3.bin` from the base recipe
|
* `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,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.
|
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
|
Stage 2 gives you a full interpreter, but it's missing the "Addressed devices"
|
||||||
required words to your boot binary. Required units are `forth/adev.fs` and
|
module and the AT28 driver. We'll need to assemble a stage 3.
|
||||||
`drv/at28.fs`.
|
|
||||||
|
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
|
## Writing contents to the AT28
|
||||||
|
|
||||||
|
@ -28,8 +28,7 @@ H@ XOFF @ - XOFF @ 8 + !
|
|||||||
446 452 XPACKR ( parse )
|
446 452 XPACKR ( parse )
|
||||||
358 360 XPACKR ( acia.fs )
|
358 360 XPACKR ( acia.fs )
|
||||||
442 445 XPACKR ( print )
|
442 445 XPACKR ( print )
|
||||||
459 463 XPACKR ( fmt )
|
453 463 XPACKR ( readln fmt )
|
||||||
123 132 XPACKR ( linker )
|
123 132 XPACKR ( linker )
|
||||||
," : x KEY DUP EMIT ; "
|
," : _ ACIA$ RDLN$ (ok) ; _ "
|
||||||
," : _ ACIA$ (ok) ['] x 0x0c RAM+ ! ; _ "
|
|
||||||
H@ 256 /MOD 2 PC! 2 PC!
|
H@ 256 /MOD 2 PC! 2 PC!
|
||||||
|
Loading…
Reference in New Issue
Block a user