mirror of
https://github.com/hsoft/collapseos.git
synced 2024-12-25 05:18:06 +11:00
125 lines
4.5 KiB
Plaintext
125 lines
4.5 KiB
Plaintext
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 + &< <>}
|
|
|
|
*** Addressed devices
|
|
|
|
The adev unit provides a simple but powerful abstraction over C@ and C!: A@ and
|
|
A!. These work the same way as C@ and C! (but for performance reasons, aren't
|
|
used in core words), but are indirect calls.
|
|
|
|
Upon initialization, the default to C@ and C!, but can be set to any word
|
|
through A@* and A!*.
|
|
|
|
On top of that, it provides a few core-like words such as AMOVE.
|
|
|
|
Let's demonstrate its use through a toy example:
|
|
|
|
> : F! SWAP 1 + SWAP C! ;
|
|
> 8 H@ DUMP
|
|
|
|
:54 0000 0000 0000 0000 ........
|
|
> 9 H@ A!
|
|
> 8 H@ DUMP
|
|
|
|
:54 0900 0000 0000 0000 ........
|
|
> ' F! A!* !
|
|
> 9 H@ 1 + A!
|
|
> 8 H@ DUMP
|
|
|
|
:54 090a 0000 0000 0000 ........
|
|
> H@ H@ 2 + 2 AMOVE
|
|
> 8 H@ DUMP
|
|
|
|
:54 090a 0a0b 0000 0000 ........
|
|
>
|
|
|
|
Of course, you might want to end up using adev in this kind of ad-hoc way to
|
|
have some kind of mapping function, but what you'll mostly want to to is to
|
|
plug device drivers into those words.
|