# 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 -- entry
m -- entry metadata
r -- range
u -- count
c -- character, a 8-bit value
a b c -- order of elements matter

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

*I* indicates an immediate word.
*B* indicates that it is "binary modulable".
*C* indicates that the word can be compiled. Its description is its behavior in
    interpret mode, but when in compile mode, it will "do the right thing" to
    compile the same behvior.
*A* Preserves the 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
^ - Opposite
( - Lower boundary
) - Upper boundary
' - 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/arch]. Those are not
direct values, but addresses to cells containing the 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.
HERE       The value of "here".
CURCONTEXT Currently active context [doc/arch].
HBANKIDX   Current "HAL number bank" index [doc/hal].
[rcnt]     Current "R level" which is changed when compiling RS-related words
           during compilation [doc/usage/rs].
nextmeta   Pointer to the metadata that will be assigned to the next entry to
           be created. Reset to 0 after each entry. Is a linked list.
MOD        Current "binary modulator" ID [doc/usage/bwm].
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.
INPTR      Input stream pointer.
HEREMAX    Upper bound for "here".
SYSCONTEXT System context [doc/arch]. $14b in length.
ABORTPTR   Word where "abort" points. Generally "(abort)".
PARSEPTR   Pointer to active "parse ( str -- n? f )" word
PSTOP      Address where PS begins and is reinitialized at "abort"
RSTOP      Address where RS begins and is reinitialized at "quit"
curword    Address of last read word (with "word" word). Is a string.
HBANK      HAL number bank [doc/hal].


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

NEXTIN<    Pointer to ( -- ?a u ) word for input stream rotation
EMIT       Address for the "emit ( c -- )" word.
ABORTPTR   Address for the "abort" word.

### Constants

SPC        $20
CR         $0d
LF         $0a
BS         $08
ESC        $1b
EOF        -1
MAXWORDSZ  $20 max length in bytes that "word" can yield.

### Values

Obey "to" semantics

here         Address where the "write" words write.
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.
inptr        Pointer to current input stream buffer. Proxy value to INPTR.
insz         Input stream buffer size in bytes

## Stack

Stack words move elements around in PS and RS.

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

rdrop      --              *I* Compile a RS shrink of 4 bytes.
2rdrop     --              *I* Compile a RS shrink of 8 bytes.
r@         --              *I* Compile a push of current RS top to PS.
r!         --              *I* Compile a push of current PS top to RS.
r>         --              *I* Equivalent to r@ rdrop.
>r         --              *I* Equivalent to r! drop.
[rallot]   "n" -- n        *I* Parse a number and compile an negated rs+, of
                           that size as well as code that pushes current RSP
                           value to PS. n is automatically 32-bit aligned.
[rfree]    n --            *I* Compiles a rs+, of "n". "n" must be 32-bit
                           aligned.
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.

## Memory

Memory words fetch, store or write to an address in memory. "bw" means "binary
width", a value that can be 1, 2 or 4 depending of the width of the operation.

@          a -- n        *B* Fetch n at address a.
!          n a --        *B* Store n at address a.
+!         n a --        *B* Add n to value stored at address a.
1+!        a --          *B* Same a "1 +!" but faster
1-!        a --          *B* Same a "-1 +!" but faster
and!       n a --        *B* Apply "and n" to value stored at address a.
or!        n a --        *B* Apply "or n" to value stored at address a.
xor!       n a --        *B* Apply "xor n" to value stored at address a.
@!         n1 a -- n2    *B* Fetch n2 at address a, then store n1 at address a.
@+         a -- a+bw n   *B* Fetch n at address a and increase a.
!+         n a -- a+bw   *B* Store n at address a and increase a.
@@+        a -- n        *B* Do an indirect fetch from a and then increase the
                             pointer contained in a by "bw".
@!+        n a --        *B* Do an indirect store to a and then increase the
                             pointer contained in a by "bw".
,          n --          *B* Write n to here and increase here by "bw".
be@        a -- n        *B* Big-endian fetch
be!        n a --        *B* Big-endian store
be,        n --          *B* Big-endian write
le@        a -- n        *B* Little-endian fetch
le!        n a --        *B* Little-endian store
le,        n --          *B* Little-endian write
8b         --            *I* Set binary width to 8-bit
[8b]       --            Same, but not immediate
16b        --            *I* Set binary width to 16-bit
[16b]      --            Same, but not immediate

align2     a -- a        Add 0 or 1 to "a" so that it's divisible by 2.
align4     a -- a        Add 0, 1, 2 or 3 to "a" so that it's divisible by 4.
align2#    a --          Abort with error if a is not divisible by 2
align4#    a --          Abort with error if a is not divisible by 4
alignhere  --            Align here to 4b.
allot      u --          Increase here by u.
allot0     u --          Allot u and fill this space with zeroes.
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          Align here to 4b and return it.
move       src dst u --  *B* Copy u units from address src to address dst,
                         moving upwards.
cmove,     src u --      Copy u bytes to "here" and increase "here" by u.
-cmove,    src u --      Rewind "here" by u, then call "cmove,".
fill       a u n --      *B* Fill range [a, a+u] with n.
nc,        n --          Parse n numbers from input stream and write them as
                         8-bit values.
nabort,    n --          Write address of word "abort" n times. Used for method
                         placeholders.
idx        n a u -- ?i f *B* Search for n in range [a, a+u] and yield its index.
                         If found, f=1 and i is present. Otherwise, f=0 and i is
                         absent.

cmove" ( dst "x" -- dst+ )
  Copy bytes "x" within quotes to address "dst" and yield the address directly
  following the copied contents.

Convenience shortcuts:

c@  --> 8b @
c!  --> 8b !
c,  --> 8b ,
w@  --> 16b @
w!  --> 16b !
c@+ --> 8b @+
c!+ --> 8b !+
cmove --> 8b move
cfill --> 8b fill
c[]=  --> 8b []=
cidx  --> 8b idx

## Arithmetics

1+         a -- a+1
1-         a -- a-1
+          a b -- a+b
-          a b -- a-b
-^         a b -- b-a
*          a b -- a*b
/          a b -- a/b
/+         a b -- a/b  and add 1 if remainder is nonzero
/mod       a b -- r q  r is remainder, q is quotient
mod        a b -- n    n is the remainder of a divided by b
neg        a -- -a
and        a b -- n    n is the result of a binary "and" of a and b
or         a b -- n    n is the result of a binary "or" of a and b
xor        a b -- n    n is the result of a binary "exclusive or" of a and b
^          a -- n      n is the result of "a -1 xor", flipping all bits.
lshift     n u -- n    Shift n by u bits to the left
rshift     n u -- n    Shift n by u bits to the right
<<         n -- n      Shift n left by 1
>>         n -- n      Shift n right by 1
<<c        n -- n c    Shift n left by 1, yielding c as the 32-bit carry bit
>>c        n -- n c    Shift n right by 1, yielding c as the 32-bit carry bit
upcase     c -- c      If c is between "a" and "z", yield their upcase version.
                       Otherwise, yield c.

## 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     f=1 if a is lower than b
>          a b -- f     f=1 if a is higher than b
=          a b -- f     f=1 if a equals b
<>         a b -- f     f=1 if a is not equal to b
>=         a b -- f     f=1 if a is higher than or equal to b
<=         a b -- f     f=1 if a is lower than or equal to b
0<         a -- f       f=1 if a is negative
0>=        a -- f       f=1 if a is not negative
not        a -- f       f=1 if a is zero
bool       a -- f       f=1 if a is nonzero
and?       a b -- f     boolean "and"
or?        a b -- f     boolean "or"
min        a b -- n     n is the lowest number between a and b.
max        a b -- n     n is the highest number between a and b.
max0       n -- n       Return n if n is positive, otherwise 0.
=><=       n l h -- f   f=1 if n >= l and n <= h.
?swap      a b -- l h   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
                        the same content.
s=         s1 s2 -- f   f=1 if s1 and s2 have the same length and content.

## Flow

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 "word" and "runword".
break      --        *I* Break from an iteraor early.
[if]       f --      If f=0, skip all following words until a "[then]" word is
                     reached.
[then]     --        No-op.

Structured flow words are better explained in their interaction together rather
than their individual effect and this is done in doc/usage. Below is a list of
builtin flow words for reference. All these words are immediate.

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

llnext    ll -- ll   Yield next LL element.
ll>data   ll -- a    Yield the data associated to a LL element (add 4).
llend     ll -- ll   Iterate LL until we reach the last element.
llappend  elem ll -- Append "elem" at the end of "ll".
lladd     ll --      Write a new LL element to here then write its address to
                     the last element of "ll".
llprepend ll --      Write a new LL element that points to "ll".

For more, see 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.
'"         "x" --    *I* Compile x as a string followed by a call to '.
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.
e>meta     e -- a    Yield the address of "e"'s metadata link.
meta>data  m -- a    Yield the address of "m"'s payload.
entry      'dict s --
                     Create entry with name s in dictionary 'dict.
code       "x" --    Create entry with name from input stream in sysdict.
code8b     --        Create a 8b variant to the latest word.
code16b    --        Create a 16b variant to the latest word.
current    -- w      Yield the last word to be added to the system dictionary.
system     --        Set CURCONTEXT to SYSCONTEXT.

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, applying
  binary width modulation logic.
findmeta ( id ll -- ll-or-0 )
  Search for metadata entry with ID "id" in linked list "ll". If found, return
  the entry. Otherwise, zero.
findmod ( e -- w )
  Yield the executable word associated with entry "e", taking MOD into account.
  If MOD is zero, we just apply "e>w". Otherwise, look in "w"'s metadata for a
  binary width modulator with the same ID as MOD. If found, set MOD to zero and
  make "w" point to that modulator. If not found, leave both "w" and MOD as-is.
metaadd ( id entry -- )
  Write a new metadata entry of type "id" to "here" and add it to "entry"'s
  metadata list.

## Fields

See [doc/usage/bind].

fieldsoff (value)
  Offset that the next field to be defined will have.
fields$ ( -- )
  Reset fieldsoff to 0.
fallot ( n -- )
  Increase fieldsoff by n.
field ( "name" -- ) *B*
  Create a new field named "name".
fieldbe ( "name" -- ) *B*
  Create a big-endian field. Supports only "to" and "to'"
fieldle ( "name" -- ) *B*
  Create a little-endian field. Supports only "to" and "to'"
field' ( sz "name" -- )
  Create a new field reference named "name". Instead of accessing the address as
  a value, calling the field will yield the address itself. Upon creation,
  increases fieldsoff by "sz".
method ( "name" -- )
  Create a method named "name".
smethod ( "name" -- )
  Create a static method, that is, a method that doesn't duplicate its PS top
  before calling the associated word (in other words, no "self").
fstructbind ( "fieldname" "structname" "bindname" -- )
  Create a new "field structbind" which binds the field "fieldname" to the
  struct "structname". This new structbind has the name "bindname".

## Namespaces and structures

ns[ ( "name" -- bkpctx )
  Creates a new namespace "name" and enters it.
struct[ ( "name" -- bkpctx )
  Creates a new structure "name" and enters it. "fieldsoff" is initialized.
]ns ( bkpctx -- )
  Restores the context that was active before the previous "enter".
]struct ( bkpctx -- )
  Creates a "SZ" constant from "fieldsoff" and restores the context that was
  active before the previous "enter".
extends ( "name" -- )
  The next "ns[" call will fork namespace "name" instead of forking the current
  context.
intons[ ( "name" -- bkpctx )
  Enter a namespace.
nsfind ( 'ns "name" -- w )
  Find a word "name" in namespace "'ns". Aborts if not found.
ns' ( "nsname" "name" -- w )
  Find a word "name" in namespace "nsname". Aborts if not found.
[ns'] ( "nsname" "name" -- ) *I*
  Find a word "name" in namespace "nsname" and then compile the address.
  Aborts if not found.
[ns] ( "nsname" -- ) *I*
  Find namespace "nsname" and compile its address.
[nsdict] ( "nsname" -- ) *I*
  Find namespace "nsname" and compile the address of its dictionary.
nscompile ( "nsname" "name" -- ) *I*
  Compiles a call to word "name" from namespace "nsname".
structbind ( "struct" "name" 'data -- )
  Create a new struct bind named "name" binding "'data" to the struct "struct".
rebind' ( 'data "bindname" -- )
  Rebind struct "bindname" to data "'data".
[rebind'] ( "bindname" -- )
  Compiles a "rebind'" to "bindname".

## I/O

in<        -- c        Read next character from system input source.
in<#       -- c        Like in<, but aborts on EOF.
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.
"<         -- c        Read from in< and apply literal escapes. c=-1 when " is
                       read (end of string).
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.
litrepl    c -- c      If "c" is one of the "\" literal escapes (n, r, 0),
                       replace it with the associated char. Preserve otherwise.
litrepl?   c -- ?c f   If c is '\', f=1 and c is absent. Otherwise, f=0 and c is
                       preserved.
,"         x" --       Read from in< until " is reached and write it to here.
."         x" --       *IC* Emit string x.
abort"     x" --       *IC* Emit string x then abort.
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.
word       -- s        Try to read a word and error out if EOF is reached.
\          --          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<<".

## Parsing

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.
compword ( s -- )
  Compile "s", that is: try to parse as a number. Write a literal if it's a
  number. Other-wise, find word in system dict and check if it's an immediate.
  If yes, execute, otherwise, write a call to its address.
runword ( s -- )
  Try to parse "s" as a number. If it is one, push in to PS. Otherwise find in
  system dict and then execute.
interpret ( -- )
  As long a "in<" doesn't yield EOF, run a "word/runword" loop.

## 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.
:8b        --       Create a 8b variant to the latest word.
:16b       --       Create a 16b variant to the latest word.
: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.
'~         -- a     Yields the address of the latest anonymous word.
litn       n --     Compile a literal with value n.
execute,   a --      Compile a call to address "a".
compile    "x" --   *I* Find word x and compile a compilation of a call to it.
[compile]  "x" --   *I* Find immediate word x and instead of executing it
                    immediately as we would normally do, compile it as if it
                    wasn't immediate.
[rcompile] "x" --   *I* Like [compile], but select the runtime behavior.
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.
create     "x" --   Create a new entry named "x" of type "cell".
const      n "x" -- Create a new constant named "x" of value n.
adder      n "x" -- Create a word that adds "n" to PS top.
bind       w n "x" --
  Compile a word that pushes "n" to PS then calls "w".
immbind    wi w n "x" --
  Like "bind", but grafts a ":compiling" metadata with "n" and "wi".
does>      n --     Compile a word that binds "n" to the contents following this
                    very word.
immdoes>   n --     Like "does>", but make the word immediate.
value      n "x" -- Create a new entry of type "value" with n as its initial
                    value
@value     a "x" -- Creates a value pointing to address "a" rather than its own
                    cell.
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".
,S"        x" -- a  Write the typed string to here and yield its address.
S"         x" --    *IC* Yield string literal with contents "x".
,[         --       *IC* Begin immediate writing mode. See doc/usage
],         --       Finish immediate writing.
addr,      hal --   Compiles code that pushes the address of "hal" to PS.

For these words below, see [doc/usage/bmw]:
MOD)           hal -- hal
MODsz          -- sz
MOD$           --
modulablecode  "name" --

## Mirror compilers

There are also some compiler words that all have the same ( -- ) signature and
that compile the same operation as their name, without the "," part. You can use
them to create a "code" word or to inline an operation instead of calling it.

dup, swap, nip, drop, 2drop, rot, over,

## "to" words

to    --> !
to+   --> +!
to1+  --> 1+!
to1-  --> 1-!
to&   --> and!
to|   --> or!
to^   --> xor!
to'   --> noop
to@!  --> @!
to@+  --> @@+
to!+  --> @!+

totbl ( "name" ... -- )
  Create a new "to words" table for use with "toexec".

toexec ( ? totbl -- ? )
  Executes or compiles "to" word according to table "totbl". This table can be
  either a "execute" table or "compile" table.

toexecwrap ( halop totbl -- ? )
  Given an "execute time" "totbl", wrap a compilation of it from the given
  "halop". It will compile "addr,", followed by the correct "to" word. This
  results in slower code, but saves you from defining a separate compilation
  table.

totblexec ( -- tbl )
  Default table for "execution time" to semantics.

totblcmp ( -- tbl )
  Default table for "compile time" to semantics.

toerr ( -- )
  Raise a "unsupported to operation" error

## Templates

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
