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
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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!
|
||||
|
Loading…
Reference in New Issue
Block a user