# System dictionary

This is the list of words provided by the kernel and its boot sequence,
organized by topics.

You should read doc/usage first to understand concepts mentioned here.

## Glossary

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
that the Return Stack is modified.

Some words have a variable stack signature, most often in pair with a flag.
These are indicated with "?" to tell that the argument might not be there. For
example, "-- n? f" means that "n" might or might not be there.

Some elements are surrounded in "" quotes, for example ""name" --". This
indicates an element that isn't read from PS, but from the input stream (with
"word").

Letters have consistent meanings

a -- address
n -- number
f -- flag
s -- string
w -- word
e -- dictionary entry
u -- count
c -- character, a 8-bit value
a b c -- anything, but order will change

The description of the words can contain letters in between "*" characters,
indicating a special attribute:

*I* indicates an immediate word.
*C* indicates a compileonly word.
*B* indicates that it is "binary modulable", that is, comes in 3 words. The
    straight word is the 32-bit version. The one with a "w" prefix is the 16-bit
    version and the one with a "c" prefix is the 8-bit version.
*A* Preserves A register

## Symbols

Across words, different symbols are used in different contexts,
but we try to be consistent in their use. Here's their definitions:

! - Store
@ - Fetch
$ - Initialize
( - Lower boundary
) - Upper boundary
[ - Beginning of a mode
] - End of a mode
' - Address of
? - As a suffix, means "Is it ...?". As a prefix, "do ... if flag"
[...] - Indicates immediateness

## Maybe a HAL word?

If the word you're looking for ends with ")" or "," and isn't in here, it might
be a HAL word and is documented in [doc/hal].

## System variables

### Raw SYSVARS

The words below yield addresses in the SYSVARS area [doc/data]. Those words
yield an address to the cells containing the associated value. All those values
can be written to at run time, but some are dangerous to change.

COMPILING  Whether the system is currently compiling a word
CURALLOC   Pointer to the current allocator [doc/usage/mem]
SYSALLOC   System allocator [doc/usage/mem]
SYSDICT    Pointer to system dictionary's latest entry
[rcnt]     Current "R level" which is changed when compiling RS-related words
           during compilation [doc/usage/rs]
FINDMASK   Mask to apply to "length" fields during "findentry". Defaults to $7f
           and is reset to that value each time "findentry is called.
INPTR      Input stream pointer
PARSEPTR   Pointer to active "parse ( str -- n? f )" word
PSTOP      Address where PS begins and is reinitialized at "abort"
PSSZ       Size of PS in bytes
RSTOP      Address where RS begins and is reinitialized at "quit"
HBANKIDX   Current "HAL number bank" index [doc/hal]
HBANK      HAL number bank [doc/hal]

Those values are indirect values depending on CURALLOC [doc/usage/mem]:

HERE       The value of "here"
HEREMAX    Upper bound for "here"
NEWHERE    "OOM recovery" word pointer ( -- a u )

Those variables below aren't in SYSVARS, but they behave similarly:

NEXTIN<    Pointer to ( -- ?a u ) word for input stream rotation [doc/usage/io]
EMIT       Address for the "emit ( c -- )" word
ABORTPTR   Word where "abort" points [doc/usage/flow]
QUITHOOK   Called by "quit" right before it calls "main" [doc/usage/flow]
curword    Address of last read word (with "word" word). Is a string.
NEXTWORD   Contains the "next word override": When nonzero, the next "word" call
           will yield (and clear NEXTWORD) this word instead of getting it from
           the input stream.
PREVWS     Value of the whitespace that "ended" the last word to be read
lastanon   Address of the latest anonymous word [doc/usage/word]
echoin     Controls input stream echo [doc/usage/io]
insz       Input stream buffer size in bytes
showprogress If nonzero, "?progress>" spits "." to console. You might want to
             set this during early boot of a system with big drivers as they
             take a little while to compile.
SCRYING    Global flag controlled by "scry" and "scrying?" [doc/usage/word]
PREVRESERVE Value of "HERE" at the time of the last "reserve" call

### Constants

SPC         $20
CR          $0d
LF          $0a
BS          $08
ESC         $1b
EOF         -1
STR_MAXSZ   $100 Maximum size of strings, including size byte.
KERNELSTART Address of the first dictionary entry living in the kernel
KERNELEND   Address of the last entry living in the kernel
HERESTART   Address of ':', the first word in xcomp/boot
SYSTEMUNIT  Pointer to the "system" unit

## Stack

drop       a --            *A*
2drop      a b --          *A*
dup        a -- a a        *A*
?dup       a -- a? a       *A* dup if a is nonzero.
2dup       a b -- a b a b  *A*
swap       a b -- b a      *A*
over       a b -- a b a    *A*
nip        a b -- b        *A*
tuck       a b -- b a b    *A*
rot        a b c -- b c a  *A*
rot>       a b c -- c a b  *A*

rdrop      --          *AI* Compile a RS shrink of 4 bytes.
2rdrop     --          *AI* Compile a RS shrink of 8 bytes.
r@         --          *AI* Compile a push of current RS top to PS.
r!         --          *AI* Compile a push of current PS top to RS.
r>         --          *AI* Equivalent to r@ rdrop.
>r         --          *AI* Equivalent to r! drop.
scnt       -- n        Number of elements in PS, excluding "n".
rcnt       -- n        Number of elementS in RS, excluding this call.
stack?     --          Error out if scnt < 0.

[]>r       a u --      *I* see [usage/mem]
r>[]       dst -- a u  *I* see [usage/mem]
str>r      str --      *I* see [usage/mem]
r>str      dst -- str  *I* see [usage/mem]

## Memory

@          a -- n        *AB* Fetch n at address a.
w@
c@
!          n a --        *AB* Store n at address a.
w!
c!
+!         n a --        Add n to value stored at address a.
@!         n1 a -- n2    *A* Fetch n2 at address a, then store n1 at address a.
@+         a -- a+4 n    Fetch n at address a and increase a.
c@+        a -- a+1 n    Fetch n at address a and increase a.
!+         n a -- a+4    Store n at address a and increase a.
c!+        n a -- a+1    Store n at address a and increase a.
,          n --          *AB* Write n to HERE and increase HERE by number of
w,                       bytes written.
c,

align2     n -- n        Add 0 or 1 to "a" so that it's divisible by 2.
align4     n -- n        Add 0, 1, 2 or 3 to "a" so that it's divisible by 4.
align2#    n --          Abort with error if a is not divisible by 2
align4#    n --          Abort with error if a is not divisible by 4
alignhere  --            Align HERE to 4b.
allot      u --          *A* Increase HERE by u
allot0     u --          *A* Allot u and fill this space with zeroes
allot@     u -- a        Same as "allot", but yield alloted address
cell       -- 4          Return native memory width.
cells      n -- n*4      Multiply by native memory width (4).
cells/     n -- n/4      Divide by native memory width (4).
here       -- a          *A* Equivalent to "HERE @"
here#      -- a          *A* Align HERE to 4b and return it.
newhere    -- a u        Equivalent to "NEWHERE @ execute"
move       src dst u --  *B* Copy u units from address src to address dst,
wmove                    moving upwards. Register A contains the "dst" address
cmove                    following the last written byte.
cmoveallot src u --      Allocate u bytes then copy that many bytes there from
                         src.
fill       a u n --      *B* Fill range [a, a+u] with n.
wfill
cfill
nc,        n --          Parse n numbers from input stream and write them as
                         8-bit values.
idx        n a u -- ?i f *B* Search for n in range [a, a+u] and yield its index.
widx                     If found, f=1 and i is present. Otherwise, f=0 and i is
cidx                     absent.

strmove ( str dst -- )
  Copy "str"'s content (including length byte) to "dst", which must have enough
  place to accomodate it.

s, ( str -- )
  Write "str" to here.

## Arithmetics

1+         a -- a+1    *A*
1-         a -- a-1    *A*
+          a b -- a+b  *A*
-          a b -- a-b  *A*
swap-      a b -- b-a  *A*
*          a b -- a*b  *A*
/          a b -- a/b  *A*
/+         a b -- a/b  *A* and add 1 if remainder is nonzero
/mod       a b -- r q  *A* r is remainder, q is quotient
mod        a b -- n    *A* n is the remainder of a divided by b
neg        a -- -a     *A*
and        a b -- n    *A* n is the result of a binary "and" of a and b
or         a b -- n    *A* binary "or"
xor        a b -- n    *A* binary "exclusive or"
^          a -- n      *A* n is the result of "a -1 xor", flipping all bits.
lshift     n u -- n    *A* Shift n by u bits to the left
rshift     n u -- n    *A* Shift n by u bits to the right
<<         n -- n      *A* Shift n left by 1
>>         n -- n      *A* Shift n right by 1

## Logic

"f" is a flag that can be either 0 or 1. We describe conditions for "f" to be 1.
Other conditions yield f=0.

<          a b -- f     *A* f=1 if a is lower than b
>          a b -- f     *A* f=1 if a is higher than b
=          a b -- f     *A* f=1 if a equals b
<>         a b -- f     *A* f=1 if a is not equal to b
>=         a b -- f     *A* f=1 if a is higher than or equal to b
<=         a b -- f     *A* f=1 if a is lower than or equal to b
0<         a -- f       *A* f=1 if a is negative
0>=        a -- f       *A* f=1 if a is not negative
not        a -- f       *A* f=1 if a is zero
bool       a -- f       *A* f=1 if a is nonzero
and?       a b -- f     boolean "and"
or?        a b -- f     boolean "or"
min        a b -- n     *A* n is the lowest number between a and b.
max        a b -- n     *A* n is the highest number between a and b.
max0       n -- n       *A* Return n if n is positive, otherwise 0.
=><=       n l h -- f   *A* f=1 if n >= l and n <= h.
?swap      a b -- l h   *A* Sort a and b, putting the highest number on TOS.
[]=        r1 r2 u -- f *B* f=1 if memory ranges [r1, r1+u] and [r2, r2+u] have
w[]=                    the same content.
c[]=
s=         s1 s2 -- f   f=1 if s1 and s2 have the same length and content.

## Flow

See [doc/usage/flow] for details.

noop        --        Do nothing
idle        --        If sys/loop is active, run the loop. Otherwise, noop.
bye         --        Halt the machine.
quit        --        Reset RS and return to the "main" loop.
abort       --        Reset PS and quit.
execute     a --      Call address a.
exit        --        *I* Compile a return from call.
main        --        The mainloop. Repeatedly call "run1".
break       --        *I* Break from an iterator early.
[if]        f --      If f=0, skip all following words until a "[then]" word is
                      reached.
[then]      --        No-op.
:quithook   --        Create a new "quit" hook.
addquithook w --      Add "w" to the "quit hooks" chain.

Control structure patterns (All words below are *A*):

if .. else .. then
n case .. of condition .. endof .. endcase
begin .. again
begin .. until
begin .. while .. repeat
begin .. while .. while .. repeat .. else .. then
n for .. next
lo hi for2 .. next

## Linked list

Dusk's core contains the words:

  llnext ll>data llend llappend lladd llprepend llfind

But because of their thematic affinity, these words are documented in [mem/ll].

## Dictionary

'          "x" -- w  Find x in system dictionary and error out if not found.
[']        "x" --    *I* Find x and compile its address as a literal.
w>e        w -- e    Yield an entry (linked list pointer) from a word reference.
e>w        e -- w    Yield a word reference (executable) from an entry.
e>wlen     e -- a    Yield the address of "e"'s length field.
entrylen   e -- n    Yield the length of the name of "e".
code       "x" --    Create entry with name from input stream in sysdict.
samecode   --        Create code word with the same name as latest word.
current    -- w      Yield the last word to be added to the system dictionary.

entryname[] ( entry -- a u )
  Yields the range containing "entry"'s name.

entry[] ( 'dict a u -- )
  Create entry with name corresponding to range "a u" in dictionary 'dict.

entry ( 'dict str -- )
  Shortcut for "c@+ entry[]".

findentry ( name 'dict -- entry-or-0 )
  Find name in dictionary pointer 'dict and yield the found entry or 0 if no
  match was found.

find ( name 'dict -- word-or-0 )
  Call "findentry" and yield the word associated with the found entry.

compileonly! ( -- )
  Unset b6 of FINDMASK so that the next "findentry" only looks up for words
  with the "compileonly" flag set.

## Unit

unit       "x" --    Create a new unit "x". The resulting word is not executable
                     and must be referenced with "find".
endunit    --        Stop associating further entries with the current unit.
needs      "..." --  See [doc/usage/unit]
unitalias  "..." --  See [doc/usage/unit]
system     --        *I* Unit containing all kernel and xcomp/boot words

unittop ( unit -- a )
  Yields a pointer to the "top entry" field of the unit

currentunit (variable)
  Points to the last unit to have been defined. Because Units are a LL, you can
  access all units through that.

bubbleup ( bottomenttry topentry -- )
  If "topentry" exists in sysdict, we assume that "bottomentry" also does, and
  that it's below "topentry" (catastrophic corruption occurs if not).

  We then take the chain that these two entries represent and move them up on
  top of sysdict. The entry that previously linked to "topentry" will now link
  to the entry that "bottomentry" pointed to.

  If "topentry" doesn't exist in sysdict, or if it is 0, nothing happens.

## I/O

in<         -- c     Read next character from system input source.
in<#        -- c     Like in<, but aborts on EOF.
emit        c --     Emit character to system output destination.
nl>         --       Emit CR then LF.
spc>        --       Emit SPC.
rtype       a u --   Emit characters in range [a, a+u].
stype       s --     Emit all characters in s.
ztype       z --     Emit all characters at z until a 0 is met.
."          "x" --   *I* Compiles: "x" stype
abort"      "x" --   *IC* Compiles: ."x" abort
?abort"     "x" --   *IC* Compiles: if abort"x" then
word        -- s     Try to read a word and error out if EOF is reached.
wordorquote -- s     Read either a word or a quote from input. [doc/usage/io]
\           --       Skip input stream until end of line (LF is the mark).
(           --       Skip input stream until " ) " is read.
.           n --     Emit "n" as decimal.
.x          n --     Emit "n" as hexadecimal
.x2         n --     Emit "n"'s lower half as hexadecimal
.x1         n --     Emit "n"'s lower byte as hexadecimal
?progress>  --       If "showprogress" is nonzero, spits ".". Called during
                     "f<<" and "cc<<".

stepback ( -- )
  Go back one character in the input stream. Next "in<" call will re-yield the
  same character. To avoid memory corruption, this should never be called more
  than once for a given "in<" call.

str< ( -- ?c f )
  Read from in< and apply string literal escapes. Yield char as c and f=1. If
  we've reached the end of string with an unescaped double quote, f=0 and c is
  absent.

toword? ( -- f )
  Advance input stream to next non-whitespace character then yield f=1. If EOF
  is reached, yield f=0. If inptr already points to a non-whitespace, f=1 and we
  don't advance.

## Parsing and interpreting

parse ( s -- n? f )
  Try to parse string s as a number. f=1 and n exists if parsing was successful.

parsedec ( a u -- n? f )
  Parse range "a u" as decimal.

parsechar ( a u -- n? f )
  Parse range "a u" as a character.

parsehex ( a u -- n? f )
  Parse range "a u", *without* the "$" prefix", as a hexadecimal.

run1 ( -- )
  Accumulate a word from input stream (that is, call "word") and then run that
  word according to the general interpretation logic, that is:

  1. If it's a number literal, put it on PS.
  2. Otherwise, try to find it in the dictionary.
  3. If found, execute. Otherwise, "word not found" error.

interpret ( -- )
  Call "run1" as long a "in<" doesn't yield EOF.

exec[] ( w a u -- )
  Execute "w" with range "a u" as an input stream. Previous input stream is
  restored after "w" returns.

interpret[] ( a u -- )
  Interpret the range of characters "a u" as Forth source code.

## Compiling

Compiling words operate on a higher plane: they write native code to "here",
which can then be executed to have the desired effect. Words from the HAL
(doc/hal) are omitted from the list below but are of course very useful on the
subject of compiling.

[          --       *I* Stop compiling. The following words will be interpreted.
]          --       Begin compiling. The following words will be compiled.
:          "x" --   Create entry x and begin compiling.
:realias   --       Compile a word while at the same time "realias"ing the word
                    it shadows to this new word.
:~         --       Create an anonymous word and begin compiling.
:override  --       Define a new word while at the same time making the address
                    of the word it replaces the "latest anonymous word".
:compiling --       Add "compile time" behavior to the current word.
;          --       *I* Compile a return from call and then stop compiling.
~          --       *I* Compile a call to the latest anonymous word.
'~         --       *I* Yield or compile address of latest anonymous word.
litn       n --     Compile a literal with value n.
compile    "x" --   *I* Find word x and compile a compilation of a call to it.
[rcompile]  "x" --  *I* Find word x and compile a call to it. Can be used to
                    compile immediate words instead of executing them.
[compile] "x" --    *I* Like [rcompile], but use "compileonly!".
compile]   --       Begin compiling as if we called ":", but without the
                    dictionary entry part. That is: wordmark, pushret, ]
immediate  --       Make last added entry into an immediate entry.
compileonly --      Make last added entry into a "compile only" entry.
coimm      --       Same as "compileonly immediate"
create     "x" --   Create a new entry named "x" of type "cell".
variable   "x" --   Equivalent to "create 0 ,".
const      n "x" -- Create a new constant named "x" of value n.
consts     n ... -- Create a series of n constants. Reads input n*2 times, the
                    first word being expected to have a ( -- n ) signature, the
                    second word being the name of the constant.
adder      n "x" -- Create a word that adds "n" to PS top. All adders are *A*.
does>      n --     Compile a word that binds "n" to the contents following this
                    very word.
immdoes>   n --     Like "does>", but make the word immediate.
scry       --       Set the SCRYING flag to 1. See [doc/usage/word]
scrying?   -- f     Yields SCRYING flag and reset it.
scrying?,  --       Compile a "scrying check" to Z flag.
alias      "x y" -- Find word "x" in system dictionary and create entry "y" of
                    type "alias" pointing to it.
@alias     a "x" -- Create word "x" that calls 4b address contained at "a".
realias    w t --   Make target word "t" into an alias to word "w".

bind ( w n "x" -- )
  Compile a word that pushes "n" to PS then calls "w".

## Strings

See [doc/usage/lit]

"        x" --    *I* Yield string literal with contents "x".
,"       x" --    Read from in< until " is reached and write it to here
strallot n -- a   Allocate n bytes in the string pool and yield it
newstr   -- a     Begin building a new string
endstr   a -- str Finalize string

## Templates

See [doc/usage/imm]

bi X | Y      --> dup X swap Y        a -- x y
bi+ X | Y     --> dup X over Y        a -- a x y
tri X | Y | Z --> dup X over Y rot Z  a -- x y z
dip X |       --> >r X r>             a b -- x b
