Compare commits
6 Commits
d0c5d3a741
...
243c70a4b7
Author | SHA1 | Date | |
---|---|---|---|
|
243c70a4b7 | ||
|
b162ef84f5 | ||
|
e284081c79 | ||
|
941791d609 | ||
|
2481ed4b66 | ||
|
68a7be3707 |
@ -7,7 +7,7 @@ AVRABIN = zasm/avra
|
||||
SHELLAPPS = zasm ed
|
||||
SHELLTGTS = ${SHELLAPPS:%=cfsin/%}
|
||||
# Those Forth source files are in a particular order
|
||||
FORTHSRCS = core.fs str.fs parse.fs readln.fs fmt.fs z80a.fs dummy.fs
|
||||
FORTHSRCS = core.fs str.fs parse.fs readln.fs fmt.fs z80a.fs
|
||||
FORTHSRC_PATHS = ${FORTHSRCS:%=../forth/%}
|
||||
CFSIN_CONTENTS = $(SHELLTGTS) cfsin/user.h
|
||||
OBJS = emul.o libz80/libz80.o
|
||||
@ -42,7 +42,7 @@ forth/stage1dbg: forth/stage.c $(OBJS) forth/forth0-bin.h
|
||||
$(CC) -DDEBUG -DBOOT forth/stage.c $(OBJS) -o $@
|
||||
|
||||
forth/core.bin: $(FORTHSRC_PATHS) forth/stage1
|
||||
cat $(FORTHSRC_PATHS) | ./forth/stage1 | tee $@ > /dev/null
|
||||
cat $(FORTHSRC_PATHS) ./forth/stop.fs | ./forth/stage1 | tee $@ > /dev/null
|
||||
|
||||
forth/forth1.bin: forth/forth0.bin forth/core.bin
|
||||
cat forth/forth0.bin forth/core.bin > $@
|
||||
@ -104,7 +104,7 @@ updatebootstrap: $(ZASMBIN)
|
||||
.PHONY: fbootstrap
|
||||
fbootstrap: forth/stage2
|
||||
cat ./forth/conf.fs ../forth/boot.fs | ./forth/stage2 > forth/boot.bin
|
||||
cat ../forth/dummy.fs ../forth/z80c.fs forth/emul.fs ../forth/icore.fs ../forth/dummy.fs | ./forth/stage2 | tee forth/z80c.bin > /dev/null
|
||||
cat ./forth/conf.fs ../forth/z80c.fs forth/emul.fs ../forth/icore.fs | ./forth/stage2 | tee forth/z80c.bin > /dev/null
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
Binary file not shown.
2
emul/forth/stop.fs
Normal file
2
emul/forth/stop.fs
Normal file
@ -0,0 +1,2 @@
|
||||
(entry) _
|
||||
H@ 256 /MOD 2 PC! 2 PC!
|
Binary file not shown.
@ -25,17 +25,17 @@ JP(IY), NOP, ( 17, nativeWord )
|
||||
0 JPnn, ( 1d, chkPS )
|
||||
NOP, NOP, ( 20, numberWord )
|
||||
NOP, NOP, ( 22, litWord )
|
||||
RAMSTART , ( 24, INITIAL_SP )
|
||||
RAMSTART 0x0e + , ( 26, WORDBUF )
|
||||
NOP, NOP, ( 24, unused )
|
||||
NOP, NOP, ( 26, unused )
|
||||
0 JPnn, ( 28, flagsToBC )
|
||||
0 JPnn, ( 2b, doesWord )
|
||||
RS_ADDR , ( 2e, RS_ADDR )
|
||||
RAMSTART 0x0c + , ( 30, CINPTR )
|
||||
RAMSTART 0x2e + , ( 32, SYSVNXT )
|
||||
RAMSTART 0x08 + , ( 34, FLAGS )
|
||||
RAMSTART 0x0a + , ( 36, PARSEPTR )
|
||||
RAMSTART 0x04 + , ( 38, HERE )
|
||||
RAMSTART 0x02 + , ( 3a, CURRENT )
|
||||
NOP, NOP, ( 2e, unused )
|
||||
NOP, NOP, ( 30, unused )
|
||||
NOP, NOP, ( 32, unused )
|
||||
NOP, NOP, ( 34, unused )
|
||||
NOP, NOP, ( 36, unused )
|
||||
NOP, NOP, ( 38, unused )
|
||||
NOP, NOP, ( 3a, unused )
|
||||
|
||||
( BOOT DICT
|
||||
There are only 5 words in the boot dict, but these words'
|
||||
@ -116,14 +116,14 @@ PC ORG @ 1 + ! ( main )
|
||||
stack underflow.
|
||||
)
|
||||
SP 0xfffa LDddnn,
|
||||
0x24 @ SP LD(nn)dd, ( 24 == INITIAL_SP )
|
||||
RAMSTART SP LD(nn)dd, ( RAM+00 == INITIAL_SP )
|
||||
IX RS_ADDR LDddnn,
|
||||
( LATEST is a label to the latest entry of the dict. It is
|
||||
written at offset 0x08 by the process or person building
|
||||
Forth. )
|
||||
0x08 LDHL(nn),
|
||||
0x3a @ LD(nn)HL, ( 3a == CURRENT )
|
||||
0x38 @ LD(nn)HL, ( 38 == HERE )
|
||||
RAMSTART 0x02 + LD(nn)HL, ( RAM+02 == CURRENT )
|
||||
RAMSTART 0x04 + LD(nn)HL, ( RAM+04 == HERE )
|
||||
HL L1 @ LDddnn,
|
||||
0x03 CALLnn, ( 03 == find )
|
||||
DE PUSHqq,
|
||||
@ -155,7 +155,7 @@ PC ORG @ 4 + ! ( find )
|
||||
adjust. Because the compare loop pre-decrements, instead
|
||||
of DECing HL twice, we DEC it once. )
|
||||
HL DECss,
|
||||
DE 0x3a @ LDdd(nn), ( 3a == CURRENT )
|
||||
DE RAMSTART 0x02 + LDdd(nn), ( RAM+02 == CURRENT )
|
||||
L3 BSET ( inner )
|
||||
( DE is a wordref, first step, do our len correspond? )
|
||||
HL PUSHqq, ( --> lvl 1 )
|
||||
@ -249,7 +249,7 @@ L1 BSET ( abortUnderflow )
|
||||
|
||||
PC ORG @ 0x1e + ! ( chkPS )
|
||||
HL PUSHqq,
|
||||
0x24 @ LDHL(nn), ( 24 == INITIAL_SP )
|
||||
RAMSTART LDHL(nn), ( RAM+00 == INITIAL_SP )
|
||||
( We have the return address for this very call on the stack
|
||||
and protected registers. Let's compensate )
|
||||
HL DECss,
|
||||
|
@ -6,7 +6,7 @@
|
||||
: LITS LIT SCPY ;
|
||||
: LIT< WORD LITS ; IMMEDIATE
|
||||
: _err LIT< word-not-found (print) ABORT ;
|
||||
: ' WORD (find) NOT (?br) [ 4 , ] _err ;
|
||||
: ' WORD FIND NOT (?br) [ 4 , ] _err ;
|
||||
: ['] ' LITN ; IMMEDIATE
|
||||
: COMPILE ' LITN ['] , , ; IMMEDIATE
|
||||
: [COMPILE] ' , ; IMMEDIATE
|
||||
@ -106,16 +106,16 @@
|
||||
|
||||
: (sysv)
|
||||
( Get new sysv addr )
|
||||
( 50 == SYSVNXT )
|
||||
50 @ @
|
||||
( RAM+48 (30) == SYSVNXT )
|
||||
48 RAM+ @
|
||||
CONSTANT
|
||||
( increase current sysv counter )
|
||||
2 50 @ +!
|
||||
2 48 RAM+ +!
|
||||
;
|
||||
|
||||
( Set up initial SYSVNXT value, which is 2 bytes after its
|
||||
own address )
|
||||
50 @ DUP 2 + SWAP !
|
||||
48 RAM+ DUP 2 + SWAP !
|
||||
|
||||
: ."
|
||||
LIT
|
||||
|
@ -32,8 +32,6 @@ directly, but as part of another word.
|
||||
"*I*" in description indicates an IMMEDIATE word.
|
||||
|
||||
*** Defining words ***
|
||||
(find) a -- a f Read at a and find it in dict. If found, f=1 and
|
||||
a = wordref. If not found, f=0 and a = string addr.
|
||||
: x ... -- Define a new word
|
||||
; R:I -- Exit a colon definition
|
||||
, n -- Write n in HERE and advance it.
|
||||
@ -49,6 +47,8 @@ CREATE x -- Create cell named x. Doesn't allocate a PF.
|
||||
COMPILE x -- Meta compiles. Kind of blows the mind. See below.
|
||||
CONSTANT x n -- Creates cell x that when called pushes its value
|
||||
DOES> -- See description at top of file
|
||||
FIND a -- a f Read at a and find it in dict. If found, f=1 and
|
||||
a = wordref. If not found, f=0 and a = string addr.
|
||||
IMMED? a -- f Checks whether wordref at a is immediate.
|
||||
IMMEDIATE -- Flag the latest defined word as immediate.
|
||||
LITN n -- Write number n as a literal.
|
||||
|
@ -1,13 +0,0 @@
|
||||
( When building a compiled dict, always include this unit at
|
||||
the end of it so that Forth knows how to hook LATEST into
|
||||
it. We don't use the word "(entry)" to avoid messing up
|
||||
with icore setup. )
|
||||
CREATE _
|
||||
H@ 2 - HERE !
|
||||
|
||||
( After each dummy word like this, we poke IO port 2 with our
|
||||
current HERE value. The staging executable needs it to know
|
||||
what to dump. )
|
||||
|
||||
H@ 256 / 2 PC!
|
||||
H@ 2 PC!
|
@ -55,25 +55,14 @@
|
||||
, ( write! )
|
||||
; IMMEDIATE
|
||||
|
||||
: FLAGS
|
||||
( 52 == FLAGS )
|
||||
[ 52 @ LITN ]
|
||||
: RAM+
|
||||
[ RAMSTART LITN ] _c +
|
||||
;
|
||||
|
||||
: (parse*)
|
||||
( 54 == PARSEPTR )
|
||||
[ 54 @ LITN ]
|
||||
;
|
||||
|
||||
: HERE
|
||||
( 56 == HERE )
|
||||
[ 56 @ LITN ]
|
||||
;
|
||||
|
||||
: CURRENT
|
||||
( 58 == CURRENT )
|
||||
[ 58 @ LITN ]
|
||||
;
|
||||
: FLAGS 0x08 _c RAM+ ;
|
||||
: (parse*) 0x0a _c RAM+ ;
|
||||
: HERE 0x04 _c RAM+ ;
|
||||
: CURRENT 0x02 _c RAM+ ;
|
||||
|
||||
: QUIT
|
||||
0 _c FLAGS _c ! _c (resRS)
|
||||
@ -141,9 +130,14 @@
|
||||
LIT< stack-underflow _c (print) _c ABORT
|
||||
;
|
||||
|
||||
: FIND
|
||||
( 0e == FINDPTR )
|
||||
0x0e _c RAM+ _c @ EXECUTE
|
||||
;
|
||||
|
||||
: C<
|
||||
( 48 == CINPTR )
|
||||
[ 48 @ LITN ] _c @ EXECUTE
|
||||
( 0c == CINPTR )
|
||||
0x0c _c RAM+ _c @ EXECUTE
|
||||
;
|
||||
|
||||
: ,
|
||||
@ -170,8 +164,8 @@
|
||||
( Read word from C<, copy to WORDBUF, null-terminate, and
|
||||
return, make HL point to WORDBUF. )
|
||||
: WORD
|
||||
( 38 == WORDBUF )
|
||||
[ 38 @ LITN ] ( a )
|
||||
( 10 == WORDBUF )
|
||||
0x10 _c RAM+ ( a )
|
||||
_c TOWORD ( a c )
|
||||
BEGIN
|
||||
( We take advantage of the fact that char MSB is
|
||||
@ -184,7 +178,7 @@
|
||||
( a this point, PS is: a WS )
|
||||
( null-termination is already written )
|
||||
_c 2DROP
|
||||
[ 38 @ LITN ]
|
||||
0x10 _c RAM+
|
||||
;
|
||||
|
||||
: (entry)
|
||||
@ -205,7 +199,7 @@
|
||||
: INTERPRET
|
||||
BEGIN
|
||||
_c WORD
|
||||
_c (find)
|
||||
_c FIND
|
||||
IF
|
||||
1 _c FLAGS _c !
|
||||
EXECUTE
|
||||
@ -217,12 +211,16 @@
|
||||
;
|
||||
|
||||
: BOOT
|
||||
LIT< (parse) _c (find) _c DROP _c (parse*) _c !
|
||||
LIT< (c<) _c (find) _c
|
||||
NOT IF LIT< KEY _c (find) _c DROP THEN
|
||||
( 48 == CINPTR )
|
||||
[ 48 @ LITN ] _c !
|
||||
LIT< (c<$) _c (find) IF EXECUTE ELSE _c DROP THEN
|
||||
( write (find) in PARSEPTR, RAM+0e )
|
||||
( a bit wasteful, but otherwise I have bootstrap
|
||||
issues with "," )
|
||||
LIT< (find) _c (find) _c DROP 0x0e _c RAM+ _c !
|
||||
LIT< (parse) _c FIND _c DROP _c (parse*) _c !
|
||||
LIT< (c<) _c FIND _c
|
||||
NOT IF LIT< KEY _c FIND _c DROP THEN
|
||||
( 0c == CINPTR )
|
||||
0x0c _c RAM+ _c !
|
||||
LIT< (c<$) _c FIND IF EXECUTE ELSE _c DROP THEN
|
||||
_c INTERPRET
|
||||
;
|
||||
|
||||
@ -245,7 +243,7 @@
|
||||
[ 32 H@ ! 2 ALLOT 14 H@ ! 2 ALLOT ] _c ,
|
||||
BEGIN
|
||||
_c WORD
|
||||
_c (find)
|
||||
_c FIND
|
||||
( is word )
|
||||
IF _c DUP _c IMMED? IF EXECUTE ELSE _c , THEN
|
||||
( maybe number )
|
||||
@ -262,3 +260,8 @@
|
||||
':' ' X 4 - C!
|
||||
';' ' Y 4 - C!
|
||||
|
||||
( Add dummy entry. we use CREATE because (entry) is, at this
|
||||
point, broken. Adjust H@ durint port 2 ping. )
|
||||
CREATE _
|
||||
H@ 2 - 256 /MOD 2 PC! 2 PC!
|
||||
|
||||
|
@ -66,3 +66,47 @@ also "special words", for example NUMBER, LIT, FBR, that have a slightly
|
||||
different structure. They're also a pointer to an executable, but as for the
|
||||
other fields, the only one they have is the "flags" field.
|
||||
|
||||
*** System variables
|
||||
|
||||
There are some core variables in the core system that are referred to directly
|
||||
by their address in memory throughout the code. The place where they live is
|
||||
configurable by the RAMSTART constant in conf.fs, but their relative offset is
|
||||
not. In fact, they're mostlly referred to directly as their numerical offset
|
||||
along with a comment indicating what this offset refers to.
|
||||
|
||||
This system is a bit fragile because every time we change those offsets, we
|
||||
have to be careful to adjust all system variables offsets, but thankfully,
|
||||
there aren't many system variables. Here's a list of them:
|
||||
|
||||
RAMSTART INITIAL_SP
|
||||
+02 CURRENT
|
||||
+04 HERE
|
||||
+06 IP
|
||||
+08 FLAGS
|
||||
+0a PARSEPTR
|
||||
+0c CINPTR
|
||||
+0e FINDPTR
|
||||
+10 WORDBUF
|
||||
+30 SYSVNXT
|
||||
+50 RAMEND
|
||||
|
||||
INITIAL_SP holds the initial Stack Pointer value so that we know where to reset
|
||||
it on ABORT
|
||||
|
||||
CURRENT points to the last dict entry.
|
||||
|
||||
HERE points to current write offset.
|
||||
|
||||
IP is the Interpreter Pointer
|
||||
|
||||
FLAGS holds global flags. Only used for prompt output control for now.
|
||||
|
||||
PARSEPTR holds routine address called on (parse)
|
||||
|
||||
CINPTR holds routine address called on C<
|
||||
|
||||
FINDPTR holds routine address called on FIND
|
||||
|
||||
WORDBUF is the buffer used by WORD
|
||||
|
||||
SYSVNXT is the buffer+tracker used by (sysv)
|
||||
|
@ -24,6 +24,9 @@
|
||||
H@ IN( !
|
||||
INBUFSZ ALLOT
|
||||
H@ IN) !
|
||||
( We need two extra bytes. 1 for the last typed 0x0a and
|
||||
one for the following NULL. )
|
||||
2 ALLOT
|
||||
(infl)
|
||||
;
|
||||
|
||||
@ -39,24 +42,22 @@
|
||||
( read one char into input buffer and returns whether we
|
||||
should continue, that is, whether newline was not met. )
|
||||
: (rdlnc) ( -- f )
|
||||
( buffer overflow? stop now )
|
||||
IN> @ IN) @ = IF 0 EXIT THEN
|
||||
( get and echo back )
|
||||
KEY DUP ( c c )
|
||||
( 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 DROP 0x8 DUP THEN
|
||||
EMIT ( c )
|
||||
DUP 0x7f = IF DROP 0x8 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 )
|
||||
( not newline? exit now )
|
||||
DUP 0xa = NOT IF EXIT THEN ( c )
|
||||
( newline? make our result 0 and write it to indicate
|
||||
EOL )
|
||||
DROP 0
|
||||
DUP IN> @ ! ( c )
|
||||
( if newline, replace with zero to indicate EOL )
|
||||
DUP 0xa = IF DROP 0 THEN
|
||||
;
|
||||
|
||||
( Read one line in input buffer and make IN> point to it )
|
||||
@ -75,5 +76,5 @@
|
||||
( not EOL? good, inc and return )
|
||||
DUP IF 1 IN> +! EXIT THEN ( c )
|
||||
( EOL ? readline. we still return typed char though )
|
||||
(rdln) ( c )
|
||||
(rdln) (<c) ( c )
|
||||
;
|
||||
|
@ -18,8 +18,14 @@
|
||||
|
||||
Oh, also: KEY and EMIT are not defined here. There're
|
||||
expected to be defined in platform-specific code.
|
||||
|
||||
This unit expects the same conf as boot.fs.
|
||||
)
|
||||
|
||||
( dummy entry for dict hook )
|
||||
(entry) _
|
||||
H@ 256 /MOD 2 PC! 2 PC!
|
||||
|
||||
( a b c -- b c a )
|
||||
CODE ROT
|
||||
HL POPqq, ( C )
|
||||
@ -329,13 +335,12 @@ CODE BYE
|
||||
;CODE
|
||||
|
||||
CODE (resSP)
|
||||
( INITIAL_SP == 36 )
|
||||
SP 36 @ LDdd(nn),
|
||||
( INITIAL_SP == RAM+0 )
|
||||
SP RAMSTART LDdd(nn),
|
||||
;CODE
|
||||
|
||||
CODE (resRS)
|
||||
( RS_ADDR == 46 )
|
||||
IX 46 @ LDddnn,
|
||||
IX RS_ADDR LDddnn,
|
||||
;CODE
|
||||
|
||||
CODE SCMP
|
||||
|
Loading…
Reference in New Issue
Block a user