From 1efb2821e31a720b815901d3c9cbe67371a077c8 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 14 Apr 2020 10:04:09 -0400 Subject: [PATCH] Make usage and dictionary documentation in-system --- README.md | 10 ++- blk/001 | 1 + blk/002 | 4 + blk/003 | 12 +++ blk/004 | 15 ++++ blk/006 | 16 ++++ blk/008 | 16 ++++ blk/009 | 14 ++++ blk/011 | 16 ++++ blk/012 | 16 ++++ blk/014 | 16 ++++ blk/015 | 16 ++++ blk/016 | 4 + blk/030 | 11 +++ blk/031 | 16 ++++ blk/032 | 10 +++ blk/034 | 16 ++++ blk/035 | 3 + blk/037 | 16 ++++ blk/038 | 5 ++ blk/040 | 16 ++++ blk/042 | 16 ++++ blk/043 | 16 ++++ blk/044 | 3 + blk/046 | 11 +++ blk/048 | 7 ++ blk/050 | 15 ++++ blk/052 | 10 +++ blk/054 | 12 +++ blk/056 | 8 ++ blk/058 | 10 +++ blk/060 | 16 ++++ blk/061 | 16 ++++ blk/062 | 9 +++ blk/064 | 4 + dictionary.txt | 213 ------------------------------------------------- usage.txt | 124 ---------------------------- 37 files changed, 401 insertions(+), 338 deletions(-) create mode 100644 blk/002 create mode 100644 blk/003 create mode 100644 blk/004 create mode 100644 blk/006 create mode 100644 blk/008 create mode 100644 blk/009 create mode 100644 blk/011 create mode 100644 blk/012 create mode 100644 blk/014 create mode 100644 blk/015 create mode 100644 blk/016 create mode 100644 blk/030 create mode 100644 blk/031 create mode 100644 blk/032 create mode 100644 blk/034 create mode 100644 blk/035 create mode 100644 blk/037 create mode 100644 blk/038 create mode 100644 blk/040 create mode 100644 blk/042 create mode 100644 blk/043 create mode 100644 blk/044 create mode 100644 blk/046 create mode 100644 blk/048 create mode 100644 blk/050 create mode 100644 blk/052 create mode 100644 blk/054 create mode 100644 blk/056 create mode 100644 blk/058 create mode 100644 blk/060 create mode 100644 blk/061 create mode 100644 blk/062 create mode 100644 blk/064 delete mode 100644 dictionary.txt delete mode 100644 usage.txt diff --git a/README.md b/README.md index 9dafa31..0c2caa4 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,15 @@ run Collapse OS in different contexts][jsemul]. Using those while following along with the [User Guide](doc/) is your quickest path to giving Collapse OS a try. +## Documentation + +Usage documentation is in-system. Run `0 LIST` for an introduction. You can +also open `blk/000` in a modern text editor. + +See `/emul/README.md` for getting an emulated system running. + +There is also `/notes.txt` for implementation notes. + ## Organisation of this repository * `forth`: Forth is slowly taking over this project (see issue #4). It comes @@ -66,4 +75,3 @@ A more traditional [mailing list][listserv] and IRC (#collapseos on freenode) ch [discussion]: https://www.reddit.com/r/collapseos [listserv]: http://lists.sonic.net/mailman/listinfo/collapseos [forth-issue]: https://github.com/hsoft/collapseos/issues/4 - diff --git a/blk/001 b/blk/001 index 0013ce2..adc91c7 100644 --- a/blk/001 +++ b/blk/001 @@ -1,2 +1,3 @@ MASTER INDEX + 2 Documentation diff --git a/blk/002 b/blk/002 new file mode 100644 index 0000000..dc7823c --- /dev/null +++ b/blk/002 @@ -0,0 +1,4 @@ +Documentation index + + 3 Usage + 30 Dictionary diff --git a/blk/003 b/blk/003 new file mode 100644 index 0000000..c6a9b93 --- /dev/null +++ b/blk/003 @@ -0,0 +1,12 @@ +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 for a word +reference. + +Contents + + 4 DOES> 6 Compilation vs meta-comp. + 8 I/O 11 Chained comparisons +14 Addressed devices diff --git a/blk/004 b/blk/004 new file mode 100644 index 0000000..55765dc --- /dev/null +++ b/blk/004 @@ -0,0 +1,15 @@ +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> @ ;" diff --git a/blk/006 b/blk/006 new file mode 100644 index 0000000..7c19250 --- /dev/null +++ b/blk/006 @@ -0,0 +1,16 @@ +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. diff --git a/blk/008 b/blk/008 new file mode 100644 index 0000000..89381a5 --- /dev/null +++ b/blk/008 @@ -0,0 +1,16 @@ +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. (cont.) diff --git a/blk/009 b/blk/009 new file mode 100644 index 0000000..1100574 --- /dev/null +++ b/blk/009 @@ -0,0 +1,14 @@ +(cont.) 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. diff --git a/blk/011 b/blk/011 new file mode 100644 index 0000000..b4e33da --- /dev/null +++ b/blk/011 @@ -0,0 +1,16 @@ +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. + + + + (cont.) diff --git a/blk/012 b/blk/012 new file mode 100644 index 0000000..781cffd --- /dev/null +++ b/blk/012 @@ -0,0 +1,16 @@ +(cont.) 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 + &< <>} + diff --git a/blk/014 b/blk/014 new file mode 100644 index 0000000..27bde4b --- /dev/null +++ b/blk/014 @@ -0,0 +1,16 @@ +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: + + (cont.) diff --git a/blk/015 b/blk/015 new file mode 100644 index 0000000..898fa32 --- /dev/null +++ b/blk/015 @@ -0,0 +1,16 @@ +(cont.) + > : 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 ........ + > + (cont.) diff --git a/blk/016 b/blk/016 new file mode 100644 index 0000000..dada9c1 --- /dev/null +++ b/blk/016 @@ -0,0 +1,4 @@ +(cont.) 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. diff --git a/blk/030 b/blk/030 new file mode 100644 index 0000000..1f63268 --- /dev/null +++ b/blk/030 @@ -0,0 +1,11 @@ +Dictionary + +Be sure to read usage guide (B3) first. + +31 Glossary 34 Symbols +37 Entry management 40 Defining words +42 Flow 46 Parameter stack +48 Return stack 50 Memory +52 Addressed devices 54 Arithmetic / Bits +56 Logic 58 Strings +60 I/O 64 Disk diff --git a/blk/031 b/blk/031 new file mode 100644 index 0000000..d1bac52 --- /dev/null +++ b/blk/031 @@ -0,0 +1,16 @@ +Glossary + +Stack notation: " -- ". Rightmost is +top of stack (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 this +stack transformation is made at compile time. + +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 word reference. Why not +refer to the beginning of the word struct? Because we actually +seldom refer to the name and prev link, except during +compilation, so defining "word reference" this way makes the +code easier to understand. + (cont.) diff --git a/blk/032 b/blk/032 new file mode 100644 index 0000000..c074364 --- /dev/null +++ b/blk/032 @@ -0,0 +1,10 @@ +(cont.) +Atom: A word of the type compiledWord contains, in its PF, a +list of what we call "atoms". Those atoms are most of the time +word references, but they can also be references to NUMBER and +LIT. + +Words between "()" are "support words" that aren't really meant +to be used directly, but as part of another word. + +"*I*" in description indicates an IMMEDIATE word. diff --git a/blk/034 b/blk/034 new file mode 100644 index 0000000..b35fd05 --- /dev/null +++ b/blk/034 @@ -0,0 +1,16 @@ +Symbols + +Throughout words, different symbols are used in different +contexts, but we try to been consistent in their use. Here's +their definitions: + +! - Store +@ - Fetch +$ - Initialize +^ - Arguments in their opposite order +< - Input +> - 1. Pointer in a buffer 2. Opposite of "<". +( - Lower boundary +) - Upper boundary +* - Word indirection (pointer to word) + (cont.) diff --git a/blk/035 b/blk/035 new file mode 100644 index 0000000..66c0e67 --- /dev/null +++ b/blk/035 @@ -0,0 +1,3 @@ +(cont.) +~ - Container for native code. Usually not an executable word. +? - Is it ...? (example: IMMED?) diff --git a/blk/037 b/blk/037 new file mode 100644 index 0000000..5670c62 --- /dev/null +++ b/blk/037 @@ -0,0 +1,16 @@ +Entry management + +(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 -- a Push addr of word x to a. If not found, + aborts. +['] x -- *I* Like "'", but spits the addr as a number + literal. If not found, aborts. +, n -- Write n in HERE and advance it. +ALLOT n -- Move HERE by n bytes +C, b -- Write byte b in HERE and advance it. +DELW a -- Delete wordref at a. If it shadows another + definition, that definition is unshadowed. +FORGET x -- Rewind the dictionary (both CURRENT and HERE) up to + x's previous entry. (cont.) diff --git a/blk/038 b/blk/038 new file mode 100644 index 0000000..22e0144 --- /dev/null +++ b/blk/038 @@ -0,0 +1,5 @@ +(cont.) +PREV a -- a Return a wordref's previous entry. +WHLEN a -- n Get word header length from + wordref. That is, name length + 3. + a is a wordref diff --git a/blk/040 b/blk/040 new file mode 100644 index 0000000..e116f4a --- /dev/null +++ b/blk/040 @@ -0,0 +1,16 @@ +Defining words + +: x ... -- Define a new word +; R:I -- Exit a colon definition +CREATE x -- Create cell named x. Doesn't allocate a PF. +[COMPILE] x -- Compile word x and write it to HERE. + IMMEDIATE words are *not* executed. +COMPILE x -- Meta compiles. See B6. +CONSTANT x n -- Creates cell x that when called pushes its + value. +DOES> -- See B4. +IMMED? a -- f Checks whether wordref at a is immediate. +IMMEDIATE -- Flag the latest defined word as immediate. +LITA n -- Write address n as a literal. +LITN n -- Write number n as a literal. +VARIABLE c -- Creates cell x with 2 bytes allocation. diff --git a/blk/042 b/blk/042 new file mode 100644 index 0000000..36dd48e --- /dev/null +++ b/blk/042 @@ -0,0 +1,16 @@ +Flow + +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 input stream is executed +immediately. In this context, branching doesn't work. + +(br) -- Branches by the number specified in the 2 following + bytes. Can be negative. +(?br) f -- Branch if f is false. +( -- *I* Comment. Ignore rest of line until ")" is read. +[ -- Begin interpretative mode. In a definition, words + between here and "]" will be executed instead of + compiled. +] -- End interpretative mode. +ABORT -- Resets PS and RS and returns to interpreter. (cont.) diff --git a/blk/043 b/blk/043 new file mode 100644 index 0000000..b9ef2bc --- /dev/null +++ b/blk/043 @@ -0,0 +1,16 @@ +(cont.) +ABORT" x" -- *I* Compiles a ." followed by a ABORT. +AGAIN I:a -- *I* Jump backwards to preceeding BEGIN. +BEGIN -- I:a *I* Marker for backward branching with + AGAIN. +ELSE I:a -- *I* Compiles a (fbr) and set branching + cell at a. +EXECUTE a -- Execute wordref at addr a +IF -- I:a *I* Compiles a (fbr?) and pushes its + cell's addr +INTERPRET -- Get a line from stdin, compile it in tmp + memory, then execute the compiled + contents. +QUIT R:drop -- Return to interpreter prompt immediately +RECURSE R:I -- R:I-2 Run the current word again. +THEN I:a -- *I* Set branching cell at a. (cont.) diff --git a/blk/044 b/blk/044 new file mode 100644 index 0000000..095e0b4 --- /dev/null +++ b/blk/044 @@ -0,0 +1,3 @@ +(cont.) +UNTIL f -- *I* Jump backwards to BEGIN if f is + false. diff --git a/blk/046 b/blk/046 new file mode 100644 index 0000000..6a0c0c0 --- /dev/null +++ b/blk/046 @@ -0,0 +1,11 @@ +Parameter Stack + +DROP a -- +DUP a -- a a +OVER a b -- a b a +ROT a b c -- b c a +SWAP a b -- b a +2DROP a a -- +2DUP a b -- a b a b +2OVER a b c d -- a b c d a b +2SWAP a b c d -- c d a b diff --git a/blk/048 b/blk/048 new file mode 100644 index 0000000..31ef056 --- /dev/null +++ b/blk/048 @@ -0,0 +1,7 @@ +Return Stack + +>R n -- R:n Pops PS and push to RS +R> R:n -- n Pops RS and push to PS +I -- n Copy RS TOS to PS +I' -- n Copy RS second item to PS +J -- n Copy RS third item to PS diff --git a/blk/050 b/blk/050 new file mode 100644 index 0000000..215c842 --- /dev/null +++ b/blk/050 @@ -0,0 +1,15 @@ +Memory + +@ a -- n Set n to value at address a +! n a -- Store n in address a +? a -- Print value of addr a ++! n a -- Increase value of addr a by n +C@ a -- c Set c to byte at address a +C! c a -- Store byte c in address a +CURRENT -- a Set a to wordref of last added entry. +CURRENT* -- a A pointer to active CURRENT*. Useful + when we have multiple active dicts. +HERE -- a Push HERE's address +H@ -- a HERE @ +MOVE a1 a2 u -- Copy u bytes from a1 to a2, starting + with a1, going up. diff --git a/blk/052 b/blk/052 new file mode 100644 index 0000000..d92bf82 --- /dev/null +++ b/blk/052 @@ -0,0 +1,10 @@ +Addressed devices + +See B14 for details. + +ADEV$ -- Initialize adev subsystem +A@ a -- c Indirect C@ +A! c a -- Indirect C! +A@* -- a Address for A@ word +A!* -- a Address for A! word +AMOVE src dst u -- Same as MOVE, but with A@ and A! diff --git a/blk/054 b/blk/054 new file mode 100644 index 0000000..70c17ec --- /dev/null +++ b/blk/054 @@ -0,0 +1,12 @@ +Arithmetic / Bits + ++ a b -- c a + b -> c +- a b -- c a - b -> c +-^ a b -- c b - a -> c +* a b -- c a * b -> c +/ a b -- c a / b -> c +MOD a b -- c a % b -> c +/MOD a b -- r q r:remainder q:quotient +AND a b -- c a & b -> c +OR a b -- c a | b -> c +XOR a b -- c a ^ b -> c diff --git a/blk/056 b/blk/056 new file mode 100644 index 0000000..c6eeddc --- /dev/null +++ b/blk/056 @@ -0,0 +1,8 @@ +Logic + += n1 n2 -- f Push true if n1 == n2 +< n1 n2 -- f Push true if n1 < n2 +> n1 n2 -- f Push true if n1 > n2 +CMP n1 n2 -- n Compare n1 and n2 and set n to -1, 0, or 1. + n=0: a1=a2. n=1: a1>a2. n=-1: a1 ffff + (cont.) diff --git a/blk/061 b/blk/061 new file mode 100644 index 0000000..3968fee --- /dev/null +++ b/blk/061 @@ -0,0 +1,16 @@ +(cont.) +." xxx" -- *I* Compiles string literal xxx followed by a + call to (print). +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 +IN> -- a Address of variable containing current pos in + input buffer. +KEY -- c Get char c from direct input +PC! c a -- Spit c to port a +PC@ a -- c Fetch c from port a +WORD -- a Read one word from buffered input and push its + addr. + (cont.) diff --git a/blk/062 b/blk/062 new file mode 100644 index 0000000..108ad8a --- /dev/null +++ b/blk/062 @@ -0,0 +1,9 @@ +(cont.) +There are also ascii const emitters: +BS +CR +LF +SPC + + + diff --git a/blk/064 b/blk/064 new file mode 100644 index 0000000..502c52e --- /dev/null +++ b/blk/064 @@ -0,0 +1,4 @@ +Disk + +LIST n -- Prints the contents of the block n on screen in the + form of 16 lines of 64 columns. diff --git a/dictionary.txt b/dictionary.txt deleted file mode 100644 index a8f9a86..0000000 --- a/dictionary.txt +++ /dev/null @@ -1,213 +0,0 @@ -Be sure to read "usage.txt" for a guide to Collapse OS' Forth. - -*** Glossary *** -Stack notation: " -- ". Rightmost is top of stack -(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 -this stack transformation is made at compile time. - -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 -word reference. Why not refer to the beginning of the word struct? Because we -actually seldom refer to the name and prev link, except during compilation, so -defining "word reference" this way makes the code easier to understand. - -Atom: A word of the type compiledWord contains, in its PF, a list of what we -call "atoms". Those atoms are most of the time word references, but they can -also be references to NUMBER and LIT. - -Words between "()" are "support words" that aren't really meant to be used -directly, but as part of another word. - -"*I*" in description indicates an IMMEDIATE word. - -*** Symbols *** - -Throughout words, different symbols are used in different contexts, but we try -to been consistent in their use. Here's their definitions: - -! - Store -@ - Fetch -$ - Initialize -^ - Arguments in their opposite order -< - Input -> - 1. Pointer in a buffer 2. Opposite of "<". -( - Lower boundary -) - Upper boundary -* - Word indirection (pointer to word) -~ - Container for native code. Usually not an executable word. -? - Is it ...? (example: IMMED?) - -*** Entry management *** -(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 -- a Push addr of word x to a. If not found, aborts -['] x -- *I* Like "'", but spits the addr as a number - literal. If not found, aborts. -, n -- Write n in HERE and advance it. -ALLOT n -- Move HERE by n bytes -C, b -- Write byte b in HERE and advance it. -DELW a -- Delete wordref at a. If it shadows another - definition, that definition is unshadowed. -FORGET x -- Rewind the dictionary (both CURRENT and HERE) up to - x's previous entry. -PREV a -- a Return a wordref's previous entry. -WHLEN a -- n Get word header length from wordref. That is, name - length + 3. a is a wordref - -*** Defining words *** -: x ... -- Define a new word -; R:I -- Exit a colon definition -CREATE x -- Create cell named x. Doesn't allocate a PF. -[COMPILE] x -- Compile word x and write it to HERE. IMMEDIATE - words are *not* executed. -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 in usage.txt -IMMED? a -- f Checks whether wordref at a is immediate. -IMMEDIATE -- Flag the latest defined word as immediate. -LITA n -- Write address n as a literal. -LITN n -- Write number n as a literal. -VARIABLE c -- Creates cell x with 2 bytes allocation. - -*** Flow *** -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 -input stream is executed immediately. In this context, branching doesn't work. - -(br) -- Branches by the number specified in the 2 following - bytes. Can be negative. -(?br) f -- Branch if f is false. -( -- *I* Comment. Ignore rest of line until ")" is read. -[ -- Begin interetative mode. In a definition, words - between here and "]" will be executed instead of - compiled. -] -- End interpretative mode. -ABORT -- Resets PS and RS and returns to interpreter -ABORT" x" -- *I* Compiles a ." followed by a ABORT. -AGAIN I:a -- *I* Jump backwards to preceeding BEGIN. -BEGIN -- I:a *I* Marker for backward branching with AGAIN. -ELSE I:a -- *I* Compiles a (fbr) and set branching cell at a. -EXECUTE a -- Execute wordref at addr a -IF -- I:a *I* Compiles a (fbr?) and pushes its cell's addr -INTERPRET -- Get a line from stdin, compile it in tmp memory, - then execute the compiled contents. -QUIT R:drop -- Return to interpreter prompt immediately -RECURSE R:I -- R:I-2 Run the current word again. -THEN I:a -- *I* Set branching cell at a. -UNTIL f -- *I* Jump backwards to BEGIN if f is *false*. - -*** Parameter Stack *** -DROP a -- -DUP a -- a a -OVER a b -- a b a -ROT a b c -- b c a -SWAP a b -- b a -2DROP a a -- -2DUP a b -- a b a b -2OVER a b c d -- a b c d a b -2SWAP a b c d -- c d a b - -*** Return Stack *** ->R n -- R:n Pops PS and push to RS -R> R:n -- n Pops RS and push to PS -I -- n Copy RS TOS to PS -I' -- n Copy RS second item to PS -J -- n Copy RS third item to PS - -*** Memory *** -@ a -- n Set n to value at address a -! n a -- Store n in address a -? a -- Print value of addr a -+! n a -- Increase value of addr a by n -C@ a -- c Set c to byte at address a -C! c a -- Store byte c in address a -CURRENT -- a Set a to wordref of last added entry. -CURRENT* -- a A pointer to active CURRENT*. Useful when we have - multiple active dicts. -HERE -- a Push HERE's address -H@ -- a HERE @ -MOVE a1 a2 u -- Copy u bytes from a1 to a2, starting with a1, going - up. -*** Addressed devices *** - -See usage.txt for details. - -ADEV$ -- Initialize adev subsystem -A@ a -- c Indirect C@ -A! c a -- Indirect C! -A@* -- a Address for A@ word -A!* -- a Address for A! word -AMOVE src dst u -- Same as MOVE, but with A@ and A! - -*** Arithmetic / Bits *** - -+ a b -- c a + b -> c -- a b -- c a - b -> c --^ a b -- c b - a -> c -* a b -- c a * b -> c -/ a b -- c a / b -> c -MOD a b -- c a % b -> c -/MOD a b -- r q r:remainder q:quotient -AND a b -- c a & b -> c -OR a b -- c a | b -> c -XOR a b -- c a ^ b -> c - -*** Logic *** -= n1 n2 -- f Push true if n1 == n2 -< n1 n2 -- f Push true if n1 < n2 -> n1 n2 -- f Push true if n1 > n2 -CMP n1 n2 -- n Compare n1 and n2 and set n to -1, 0, or 1. - n=0: a1=a2. n=1: a1>a2. n=-1: a1 ffff -." xxx" -- *I* Compiles string literal xxx followed by a call - to (print) -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 -IN> -- a Address of variable containing current pos in input - buffer. -KEY -- c Get char c from direct input -PC! c a -- Spit c to port a -PC@ a -- c Fetch c from port a -WORD -- a Read one word from buffered input and push its addr - -There are also ascii const emitters: -BS -CR -LF -SPC - -*** Disk *** - -LIST n -- Prints the contents of the block n on screen in the - form of 16 lines of 64 columns. diff --git a/usage.txt b/usage.txt deleted file mode 100644 index 8a2c434..0000000 --- a/usage.txt +++ /dev/null @@ -1,124 +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 + &< <>} - -*** 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.