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

Compare commits

..

No commits in common. "fca0e79da371595c8f443be03c4968db466d73bb" and "7390cb18ed913edb4b76a6c513aef579dcf19266" have entirely different histories.

6 changed files with 77 additions and 185 deletions

View File

@ -1,6 +1,6 @@
TARGETS = runbin/runbin forth/forth TARGETS = runbin/runbin forth/forth
# Those Forth source files are in a particular order # Those Forth source files are in a particular order
FORTHSRCS = core.fs cmp.fs print.fs str.fs parse.fs readln.fs fmt.fs z80a.fs FORTHSRCS = core.fs print.fs str.fs parse.fs readln.fs fmt.fs z80a.fs
FORTHSRC_PATHS = ${FORTHSRCS:%=../forth/%} forth/run.fs FORTHSRC_PATHS = ${FORTHSRCS:%=../forth/%} forth/run.fs
OBJS = emul.o libz80/libz80.o OBJS = emul.o libz80/libz80.o
SLATEST = ../tools/slatest SLATEST = ../tools/slatest

View File

@ -1,38 +0,0 @@
( Words useful for complex comparison operations )
( n1 -- n1 true )
: <>{ 1 ;
( n1 f -- f )
: <>} SWAP DROP ;
: _|&
( n1 n2 cell )
>R >R DUP R> R> ( n1 n1 n2 cell )
@ EXECUTE ( n1 f )
;
( n1 f n2 -- n1 f )
: _|
CREATE , DOES>
( n1 f n2 cell )
ROT IF 2DROP 1 EXIT THEN ( n1 true )
_|&
;
: _&
CREATE , DOES>
( n1 f n2 cell )
ROT NOT IF 2DROP 0 EXIT THEN ( n1 true )
_|&
;
( All words below have this signature:
n1 f n2 -- n1 f )
' = _| |=
' = _& &=
' > _| |>
' > _& &>
' < _| |<
' < _& &<

View File

@ -1,11 +1,21 @@
Be sure to read "usage.txt" for a guide to Collapse OS' Forth.
*** Glossary ***
Stack notation: "<stack before> -- <stack after>". Rightmost is top of stack Stack notation: "<stack before> -- <stack after>". Rightmost is top of stack
(TOS). For example, in "a b -- c d", b is TOS before, d is TOS after. "R:" means (TOS). For example, in "a b -- c d", b is TOS before, d is TOS after. "R:" means
that the Return Stack is modified. "I:" prefix means "IMMEDIATE", that is, that that the Return Stack is modified. "I:" prefix means "IMMEDIATE", that is, that
this stack transformation is made at compile time. this stack transformation is made at compile time.
DOES>: Used inside a colon definition that itself uses CREATE, DOES> transforms
that newly created word into a "does cell", that is, a regular cell ( when
called, puts the cell's addr on PS), but right after that, it executes words
that appear after the DOES>.
"does cells" always allocate 4 bytes (2 for the cell, 2 for the DOES> link) and
there is no need for ALLOT in colon definition.
At compile time, colon definition stops processing words when reaching the
DOES>.
Example: ": CONSTANT CREATE HERE @ ! DOES> @ ;"
Word references (wordref): When we say we have a "word reference", it's a Word references (wordref): When we say we have a "word reference", it's a
pointer to a words *code link*. For example, the label "PLUS:" in this unit is a pointer to a words *code link*. For example, the label "PLUS:" in this unit is a
word reference. Why not refer to the beginning of the word struct? Because we word reference. Why not refer to the beginning of the word struct? Because we
@ -44,6 +54,20 @@ IMMEDIATE -- Flag the latest defined word as immediate.
LITN n -- Write number n as a literal. LITN n -- Write number n as a literal.
VARIABLE c -- Creates cell x with 2 bytes allocation. VARIABLE c -- Creates cell x with 2 bytes allocation.
Compilation vs meta-compilation. When you compile a word with "[COMPILE] foo",
its straightforward: It writes down to HERE wither the address of the word or
a number literal.
When you *meta* compile, it's a bit more mind blowing. It fetches the address
of the word specified by the caller, then writes that number as a literal,
followed by a reference to ",".
Example: ": foo [COMPILE] bar;" is the equivalent of ": foo bar ;" if bar is
not an immediate. However, ": foo COMPILE bar ;" is the equivalent of
": foo ['] bar , ;". Got it?
Meta-compile only works with real words, not number literals.
*** Flow *** *** Flow ***
Note about flow words: flow words can only be used in definitions. In the Note about flow words: flow words can only be used in definitions. In the
INTERPRET loop, they don't have the desired effect because each word from the INTERPRET loop, they don't have the desired effect because each word from the
@ -132,6 +156,32 @@ SLEN a -- n Push length of str at a.
*** I/O *** *** I/O ***
A little word about inputs. There are two kind of inputs: direct and buffered.
As a general rule, we read line in a buffer, then feed words in it to the
interpreter. That's what "WORD" does. If it's at the End Of Line, it blocks and
wait until another line is entered.
KEY input, however, is direct. Regardless of the input buffer's state, KEY will
return the next typed key.
PARSING AND BOOTSTRAP: Parsing number literal is a very "core" activity of
Forth, and therefore generally seen as having to be implemented in native code.
However, Collapse OS' Forth supports many kinds of literals: decimal, hex, char,
binary. This incurs a significant complexity penalty.
What if we could implement those parsing routines in Forth? "But it's a core
routine!" you say. Yes, but here's the deal: at its native core, only decimal
parsing is supported. It lives in the "(parsed)" word. The interpreter's main
loop is initially set to simply call that word.
However, in core.fs, "(parsex)", "(parsec)" and "(parseb)" are implemented, in
Forth, then "(parse)", which goes through them all is defined. Then, "(parsef)",
which is the variable in which the interpreter's word pointer is set, is
updated to that new "(parse)" word.
This way, we have a full-featured (and extensible) parsing with a tiny native
core.
(parse) a -- n Parses string at a as a number and push the result (parse) a -- n Parses string at a as a number and push the result
in n as well as whether parsing was a success in f in n as well as whether parsing was a success in f
(false = failure, true = success) (false = failure, true = success)
@ -143,15 +193,11 @@ SLEN a -- n Push length of str at a.
number parsing. By default, (parse). number parsing. By default, (parse).
(print) a -- Print string at addr a. (print) a -- Print string at addr a.
. n -- Print n in its decimal form . n -- Print n in its decimal form
.x n -- Print n's LSB in hex form. Always 2 characters. .X n -- Print n in its hexadecimal form. In hex, numbers
.X n -- Print n in hex form. Always 4 characters. Numbers
are never considered negative. "-1 .X" --> ffff
." xxx" -- *I* Compiles string literal xxx followed by a call ." xxx" -- *I* Compiles string literal xxx followed by a call
to (print) to (print)
are never considered negative. "-1 .X -> ffff"
C< -- c Read one char from buffered input. C< -- c Read one char from buffered input.
DUMP n a -- Prints n bytes at addr a in a hexdump format.
Prints in chunks of 8 bytes. Doesn't do partial
lines. Output is designed to fit in 32 columns.
EMIT c -- Spit char c to output stream EMIT c -- Spit char c to output stream
IN> -- a Address of variable containing current pos in input IN> -- a Address of variable containing current pos in input
buffer. buffer.

View File

@ -1,6 +1,7 @@
( requires core, parse ) ( requires core, parse )
: _ ( TODO FORGET this word )
: PUSHDGTS
999 SWAP ( stop indicator ) 999 SWAP ( stop indicator )
DUP 0 = IF '0' EXIT THEN ( 0 is a special case ) DUP 0 = IF '0' EXIT THEN ( 0 is a special case )
BEGIN BEGIN
@ -15,7 +16,7 @@
( that "0 1 -" thing is because we don't parse negative ( that "0 1 -" thing is because we don't parse negative
number correctly yet. ) number correctly yet. )
DUP 0 < IF '-' EMIT 0 1 - * THEN DUP 0 < IF '-' EMIT 0 1 - * THEN
_ PUSHDGTS
BEGIN BEGIN
DUP '9' > IF DROP EXIT THEN ( stop indicator, we're done ) DUP '9' > IF DROP EXIT THEN ( stop indicator, we're done )
EMIT EMIT
@ -24,53 +25,24 @@
: ? @ . ; : ? @ . ;
: _ : PUSHDGTS
DUP 9 > IF 10 - 'a' + 999 SWAP ( stop indicator )
ELSE '0' + THEN DUP 0 = IF '0' EXIT THEN ( 0 is a special case )
;
( For hex display, there are no negatives )
: .x
256 MOD ( ensure < 0x100 )
16 /MOD ( l h )
_ EMIT ( l )
_ EMIT
;
: .X
256 /MOD ( l h )
.x .x
;
( a -- a+8 )
: _
DUP ( save for 2nd loop )
':' EMIT DUP .x SPC
4 0 DO
DUP @
256 /MOD SWAP
.x .x
SPC
2 +
LOOP
DROP
8 0 DO
DUP C@
DUP <>{ 0x20 &< 0x7e |> <>}
IF DROP '.' THEN
EMIT
1 +
LOOP
LF
;
( n a -- )
: DUMP
LF
BEGIN BEGIN
OVER 1 < IF DROP EXIT THEN DUP 0 = IF DROP EXIT THEN
_ 16 /MOD ( r q )
SWAP 8 - SWAP SWAP ( r q )
DUP 9 > IF 10 - 'a' +
ELSE '0' + THEN ( q d )
SWAP ( d q )
AGAIN
;
: .X ( n -- )
( For hex display, there are no negatives )
PUSHDGTS
BEGIN
DUP 'f' > IF DROP EXIT THEN ( stop indicator, we're done )
EMIT
AGAIN AGAIN
; ;

View File

@ -1,88 +0,0 @@
Collapse OS usage guide
This document is not meant to be an introduction to Forth, but to instruct the
user about the peculiarities of this Forth implemenation. Be sure to refer to
dictionary.txt for a word reference.
*** DOES>
Used inside a colon definition that itself uses CREATE, DOES> transforms that
newly created word into a "does cell", that is, a regular cell ( when called,
puts the cell's addr on PS), but right after that, it executes words that appear
after the DOES>.
"does cells" always allocate 4 bytes (2 for the cell, 2 for the DOES> link) and
there is no need for ALLOT in colon definition.
At compile time, colon definition stops processing words when reaching the
DOES>.
Example: ": CONSTANT CREATE HERE @ ! DOES> @ ;"
*** Compilation vs meta-compilation
Compilation vs meta-compilation. When you compile a word with "[COMPILE] foo",
its straightforward: It writes down to HERE wither the address of the word or
a number literal.
When you *meta* compile, it's a bit more mind blowing. It fetches the address
of the word specified by the caller, then writes that number as a literal,
followed by a reference to ",".
Example: ": foo [COMPILE] bar;" is the equivalent of ": foo bar ;" if bar is
not an immediate. However, ": foo COMPILE bar ;" is the equivalent of
": foo ['] bar , ;". Got it?
Meta-compile only works with real words, not number literals.
*** I/O
A little word about inputs. There are two kind of inputs: direct and buffered.
As a general rule, we read line in a buffer, then feed words in it to the
interpreter. That's what "WORD" does. If it's at the End Of Line, it blocks and
wait until another line is entered.
KEY input, however, is direct. Regardless of the input buffer's state, KEY will
return the next typed key.
PARSING AND BOOTSTRAP: Parsing number literal is a very "core" activity of
Forth, and therefore generally seen as having to be implemented in native code.
However, Collapse OS' Forth supports many kinds of literals: decimal, hex, char,
binary. This incurs a significant complexity penalty.
What if we could implement those parsing routines in Forth? "But it's a core
routine!" you say. Yes, but here's the deal: at its native core, only decimal
parsing is supported. It lives in the "(parsed)" word. The interpreter's main
loop is initially set to simply call that word.
However, in core.fs, "(parsex)", "(parsec)" and "(parseb)" are implemented, in
Forth, then "(parse)", which goes through them all is defined. Then, "(parsef)",
which is the variable in which the interpreter's word pointer is set, is
updated to that new "(parse)" word.
This way, we have a full-featured (and extensible) parsing with a tiny native
core.
*** Chained comparisons
The unit "cmp.fs" contains words to facilitate chained comparisons with a single
reference number. This allows, for example, to easily express "a == b or a == c"
or "a > b and a < c".
The way those chained comparison words work is that, unlike single comparison
operators, they don't have a "n1 n2 -- f" signature, but rather a "n1 f n2 -- n1
f" signature. That is, each operator "carries over" the reference number in
addition to the latest flag.
You open a chain with "<>{" and you close a chain with "<>}". Then, in between
those words, you can chain operators. For example, to check whether A == B or A
== C, you would write:
A <>{ B &= C |= <>}
The first operator must be of the "&" type because the chain starts with its
flag to true. For example, "<>{ <>}" yields true.
To check whether A is in between B and C inclusively, you would write:
A <>{ B 1 - &> C 1 + &< <>}