Overview of RetroForth and the Handbook

RetroForth is a compact, easy to learn, and quite useful implementation of the Forth programming language. It is based on the Rx Core, which provides most of the essential Forth words, but not an interactive environment. The RetroForth code extends the Rx Core to provide an interpreter, editor, and many additional words.

This Handbook is an expanded version of the Rx Handbook, Revision 1.1.

What are word classes?

The concept of a
word class
was derived from HelFORTH. Traditionally, RetroForth followed the cmForth model of having two dictionaries, one for immediate words and one for normal words. With version 8.2, released in the later months of 2005, the internal model shifted to support word classes.

Word classes are a way to group related words, based on their compilation and execution behaviors. A special word, called a
class handler,
is defined to handle an execution token passed to it on the stack. The compiler uses a variable named
class
to set the default class when compiling a word. We'll take a closer look at
class
later. The Rx Core provides RetroForth with several primary classes with differing behaviors:

.class

( xt -- )
The class handler for the provided classes. During compilation, a call to the xt is
compiled. During interpretation, the xt is executed.

.data

( xt -- )
.data is used for data structures and numbers. It will leave the xt on the stack
during interpretation and compile the xt as a literal during compilation.

.forth

( xt -- )
The class handler for forth words. Semantics are the same as with
.class


.self

( xt -- )
Words using this class are always called, unless used in a definition with a
prefixing word immediately before them. Generally words using this class will
need to be state-smart.

.macro

( xt -- )
Used for compiler macros, words with a class of
.macro
are always called during
compilation (unless preceded by a prefixing word). They are ignored during
interpretation.

.inline

( xt -- )
This is a special class used for certain primitives. It will copy the machine
code
(up to a hex value of $C3, the ret instruction)
into the current definition
during compilation. During interpretation the word is called normally. Only words
that do not use $c3 and have no calls to other words can use the
.inline
class.




Historical note: the naming and behaviour of the
.forth
and
.macro
classes was chosen to be fairly compatible with the model used in older versions of RetroForth. Many simple applications from releases 4 through 8 can be ported to the current release (9) with little effort. Larger applications will run into difficulty elsewhere as they may depend on certain internal aspects of earlier versions.


The class mechanism is not limited to these classes. You can write custom classes at any time. On entry the custom handler should take the XT passed on the stack and do something with it. Generally the handler should also check the compiler state to determine what to do in either interpretation or compilation. As a simple example, let's take a look at the
.data
hander, written in Forth:

( handler for data types )
( This compiles the XT as a literal in a definition. If interpreting, just leave )
( the XT on the data stack. )
( For constants, we make use of a clever trick. We set the XT to the value of the )
( constant and give it a class of .data allowing it to work without requiring any )
( special code or create/does> constructs )
: .data ( xt -- xt | ) state @ if literal, then ;


When writing programs in RetroForth, you will need to set the class of each word you write. There are numerous ways to do this. A look at the example below will show how each method appears in source. We'll use the
.forth
class in the examples, but the same approach applies to all of the classes.

' .forth class ! ( set the default class to .forth )
' .forth reclass ( set the class of the last defined word to .forth )
reclass: wordname ( set wordname to the current default class )


Because changing the default class will be done regularly, the main classes also have wrapper functions provided. These wrappers are given a class of
.forth
and are listed below. In general, it's recommended to use these wrappers to set the default class for readability reasons.

forth

( -- )
Set .forth as the active class

macro

( -- )
Set .macro as the active class

self

( -- )
Set .self as the active class

inline

( -- )
Set .inline as the active class




Dictionary Headers

There is a single dictionary consisting of a linked list of headers. The current form of a header is shown in the chart below. Pay special attention to the accessors. Each of these words corresponds to a field in the dictionary header. When dealing with dictionary headers, it is recommended that you use the accessors to access the fields since it is expected that the exact structure of the header will change over time.

Technical Note: With the move to the Rx Core in version 9, the internal dictionary model has changed slightly. Applications depending on the physical structure of the dictionary will need to be modified to work with Release 9.


accessor:
:link

offset: 0
Use:
Link to the previous header. If set to 0, no prior entries are visible.


accessor:
:xt

offset: 4
Use:
Link to the compiled code definition. For constants, the value of the constant is stored in this field.


accessor:
:class

offset: 8
Use:
Link to the class handler.


accessor:
:name

offset: 12
Use:
The name of the word, stored as a packed string.


Word names are stored in the dictionary as packed strings. Use
count
to obtain a counted string from it. In memory, a packed string looks like:

count (one byte)
actual string (a sequence of bytes)


Because of the use of a single byte for the count, word names are limited to 255 characters.

The most recently created header can be accessed by using a variable named
last
. To access a specific header, you can use
>entry
to get the header that points to a specified XT. A special variable named
which
is also updated by find each time a word is looked up in the dictionary. This variable always points to the entry of the most recently found word.

Given only a single dictionary, it might seem limiting, however both vocabularies and lexical scope have been implemented on top of it. We will look at each of these now.

Lexical Scope

There will be often be times you would like to "hide" some words that you only use once or twice to build other words. In Rx, the ability to do this is provided by the lexical scoping words
loc:
and
;loc


A typical use looks like:

loc: ( start a lexically scoped sequence )
loc: ( start a lexically scoped sequence )
10 constant ten
20 constant twenty
10 20 * constant two-hundred
:: two-hundred ; ( anonymous definition )
words ( debugging; show words in dictionary )
;loc is value ( close the lexically scoped sequence and reveal )
( the anonymous definition as "value" )
words ( debugging; show words in dictionary )
: a value 10 / ;
: b value 20 / ;
:: a b * . cr ; ( anonymous definition )
;loc is foo ( close the lexically scoped sequence and reveal )
( the anonymous definition as "foo" )
words ( debugging; show words in dictionary )


As you can see in the example above, when a lexically scoped definition is closed, the headers for all words inside the scoped region are removed. Only words explicitly revealed are kept in in the global dictionary. Also, as is shown in the example, you can nest lexically scoped sequences. Nesting is possible up to four levels deep.

Lexical scopes are used frequently by the developers of Rx and RetroForth and allow a good level of factoring without polluting the actual dictionary with words that are not needed outside a select few definitions.

Vocabularies

We recognize that there will be times when you want to be able to hide words from the main dictionary, but still access them later. This is where vocabularies come into the picture. RetroForth provides a single word which can be used to create a named vocabulary. A simple example of a vocabulary in use is shown below.

voc: foo ( create a vocabulary named "foo" )
foo 10 constant a ( create a constant in the "foo" vocabulary )
foo 20 constant b
: bar foo a b * . cr ; ( access the words in "foo" inside a definition in )
( the global dictionary )


Notice that you need specify the vocabulary once per line when you wish to create a new word inside the vocabulary. When you wish to use words from inside a vocabulary in a definition, specify the vocabulary inside the definition. When you use a vocabulary in a definition, words defined in the main dictionary after the vocabulary was defined are not visible. To close the vocabulary (and reveal words defined after it), use a word called
~
. This is shown in the example below.

voc: foo ( create a vocabulary named "foo" )
foo 10 constant a ( create a constant in the "foo" vocabulary )
foo 20 constant b
: times * ; ( a simple definition in the main dictionary )
: bar foo a b ~ times . cr ;
( above is a definition using words in "foo" and in the main dictionary. Note ~ which )
( closes the "foo" vocabulary. )


Handling Syntax Errors

By default, the RetroForth reports syntax errors with a short error message. This behavior can be changed at any time by replacing a special vectored word named
word?
.
word?
is the "last chance" error handler. When the interpreter obtains a token it tries to process it in a defined order:

1. Look up the token in the dictionary. If found, execute its class handler
2. Attempt to convert the token to a number. If valid, execute the .data
class handler to determine the course of action to take
3. Call word? to hopefully resolve the problem.


You can extend this or simply report errors by changing
word?
to point to a new definition. As a vectored word, this is easily done. A simple example is shown below.

( Example of a custom error handler. )
:: ( addr count -- flag ) ." could not identify: " type cr false ; is word?


This replaces the default definition of
word?
with a new handler that displays a nice error message.

Of course it is possible to write significantly more flexible error handlers. For instance, you could write a handler for entering floating point numbers, say
>float
, and hook that into
word?
to allow the user to enter floating point numbers directly at the interpreter.

Vectored Words

Many words in RetroForth are defined as vectored words. These are similar to, but more flexible than, deferred words in ANS Forth. A vectored word will have
vector
as the first word in its definition. This is the only step necessary to make a word vectorable. Two words are provided for altering and resetting vectored definitions. These are
is
and
devector
.

The
is
word is used to set a vector to a new XT. In use it takes the new XT on the stack and parses for the name of the vectored word to change. If the specified name does not exist, it creates a new vectored word with a default definition calling the specified XT. To reset a changed vector to the original definition, the word
devector
is used. This parses ahead for the name of the word to remove the vector from. What these three words allow is the changing of existing definitions. As a simple example, try the code below.

( simple example of vectors )
: a vector 12 ;
: b vector 23 ;
: c vector a b * ;
: d a . ." times " b . ." equals " c . cr ;
d
:: 23 ; is a
d
:: 13 ; is b
d
:: a b + ; is c
d
devector a
d
devector b
d
devector c
d


While a bit simplistic, this does show how to use vectors. Vectors can also be used with lexically scoped sequences to make revealing multiple words cleaner:

( another example )
: a vector ;
: b vector ;

loc:
10 constant ten
20 constant twenty

: * later prior * ;
: + later prior + ;

10 + 20 constant thirty
20 * 20 constant four-hundred

:: ." 10+20=" thirty . cr ; is a
:: ." 20*20=" four-hundred . cr ; is b
;loc


Learning to use vectored words will allow you to explore new options in your programming.

Appendix 1: Glossary of Words

Most of the words provided by RetroForth are listed here. Some, which are platform specific, are not.

.class ( xt -- ) .class
Class handler for the provided classes

.forth ( xt -- ) .class
Class handler for Forth words

.macro ( xt -- ) .class
Class handler for compiler macros

.inline ( xt -- ) .class
Class handler for Forth words that can be inlined

.data ( xt -- ) .class
Class handler for basic data structures

forth ( -- ) .forth
Set the .forth class as the active class

macro ( -- ) .forth
Set the .macro class as the active class

self ( -- ) .forth
Set the .self class as the active class

inline ( -- ) .forth
Set the .inline class as the active class

entry ( addr count -- ) .forth
Create a dictionary entry pointing to here. A string containing the name
of the word to be created is passed on the stack. The word is created with
the current class.

find ( addr count --- xt flag | addr count flag ) .forth
Attempt to find a word in the dictionary. If successful, it returns the
address of the word and the true value. If unsuccessful, it leaves the
string containing the word to search for on the stack and returns a value
of false.

>number ( addr count -- n flag | addr count flag ) .forth
Attempt to convert a string to a number in the current base. If successful,
leave the number and a value of true on the stack. If unsuccessful, leaves
the string containing the number on the stack and a return value of false.

] ( -- ) .forth
Turn the compiler mode on. This sets state to -1.

compile ( xt -- ) .forth
Compile a call to the specified xt.
Technical note:
The call instruction is relative. To obtain the address of a word
from a compiled definition, try something like:
: foo words ; ' foo 1+ @ here + 5 +
This will adjust the first call (1+ skips over the call opcode) to
point to the valid address for the function used. The "5 +" is the
adjustment, based on the size of a call. Call opcodes on x86 are
5 bytes long.

, ( n -- ) .forth
Place a cell (4 byte) value into the heap

1, ( n -- ) .forth
Place a single byte into the heap

2, ( n -- ) .forth
Place a word (2 bytes) into the heap

3, ( n -- ) .forth
Place three bytes into the heap

eval ( addr count -- ) .forth
Evaluate (using the current compiler state) the string. The results
of the evaluation are left on the stack.

parse ( delimiter -- addr count ) .forth
Search ahead in the tib for the delimiter. This skips leading whitespace
and, if not found, will return a string containing the rest of the
input line.

reset ( ... -- ) .forth
Remove all values from the stack

last ( -- addr ) .forth
Get the address of a pointer to the most recent dictionary entry

tib ( -- addr ) .forth
Get the address of the text input buffer

word? ( addr count -- flag ) .forth
Last-chance error handler for syntax errors. Receives a string
pointing to a token that was not located in the dictionary or converted
to a number. The returned flag should tell whether it was handled
successfully or not.

not ( n -- n ) .forth
Perform a bitwise NOT operation on the TOS

@ ( addr -- n ) .forth
Fetch a cell-sized value from the address specified

! ( n addr -- ) .forth
Store a cell-sized value into the address specified

w@ ( addr -- n ) .forth
Fetch a word-sized value from the address specified

w! ( n addr -- ) .forth
Store a word-sized value into the address specified

c@ ( addr -- n ) .forth
Fetch a single byte from the address specified

c! ( n addr -- ) .forth
Store a single byte into the address specified

+ ( x y -- z ) .forth
- ( x y -- z ) .forth
* ( x y -- z ) .forth
/ ( x y -- z ) .forth
mod ( x y -- z ) .forth
These are the basic math words. Insert the operation between the "x" and
"y" in the stack comment to convert to infix notation.

/mod ( x y -- z n ) .forth
Divide, returning the result and the remainder (x/y)

wsparse ( "word" -- addr count ) .forth
Parse ahead in the TIB until either a space or end of line is reached.

lnparse ( "line" -- addr count ) .forth
Parse ahead in the tib until the end of the line is reached

' ( "name" -- xt | addr count ) .forth
Return the address of a function in the dictionary or the name of
the word if it doesn't exist.
Technical note:
This word will set which to the dictionary entry of the word
if found.

'? ( "name" -- xt flag | addr count flag ) .forth
Return the address of a function in the dictionary or the name of
the word if it doesn't exist. Also returns a flag that can be used
with the conditional words.

>> ( x y -- z ) .forth
Perform a bitwise right shift

<< ( x y -- z ) .forth
Perform a bitwise left shift

here ( -- addr ) .forth
Return the value on the top of the stack

:link ( dt -- addr ) .forth
Return the address of the link field in a dictionary entry

:xt ( dt -- addr ) .forth
Return the address of the xt field in a dictionary entry

:class ( dt -- addr ) .forth
Return the address of the class field in a dictionary entry

:name ( dt -- addr ) .forth
Return the address of the name field in a dictionary entry.
Use count to convert this to a string.

reclass ( class -- ) .forth
Change the class of the most recently defined word to the specified
class.

reclass: ( "name" -- ) .forth
Change the class of the word specified by name to the current class

cells ( x -- y ) .forth
Return x multiplied by the size of a cell (4 bytes)

cell+ ( x -- y ) .forth
Add the size of a cell (4 bytes) to x

cell- ( x -- y ) .forth
Subtract the size of a cell (4 bytes) from x

word+ ( x -- y ) .forth
Add the size of a word (2 bytes) to x

word- ( x -- y ) .forth
Subtract the size of a word (2 bytes) from x

rot ( x y z -- y z x ) .forth
Rotate the stack so that the third element is on top.

-rot ( x y z -- z x y ) .forth
Rotate the stack twice. Basically the same as doing rot rot.

over ( x y -- x y x ) .forth
Place a copy of NOS above TOS

tuck ( x y -- y x y ) .forth
Place a copy of TOS under NOS

2dup ( x y -- x y x y ) .forth
Duplicate the top two items on the stack

later ( rs:x rs:y -- rs:y rs:x ) .forth
Defer execution of the rest of the definition until the caller
finishes executing.
General note:
This word plays with the return stack. It can be difficult to
master the effects, but allows some interesting options for
controlling the flow of execution through multiple words.
It is worth taking some time to experiment with and
learning to use it well.

0; ( n -- n | ) .forth
Exit a word if the TOS is equal to 0. Drops TOS before exiting.
Leave it alone if non-zero.

execute ( xt -- ) .forth
Call the code that xt points to. This word ignores the class!
Technical note:
This treats the passed xt as a .self word.
General note:
You can use get-class to obtain the class handler for the
specified xt.

create: ( "name" -- ) .forth
Create a new word with a name of name and the current class

literal, ( n -- ) .forth
Compile a literal into a definition. This is different from the
macro word literal which is called at compile time. This word
is called at runtime.

create ( "name" -- ) .forth
Create a new word named name using the .data class.

variable ( "name" -- ) .forth
Create a variable named name with an initial value of 0

variable: ( n "name" -- ) .forth
Create a variable named name with an initial value of n.

constant ( n "name" -- ) .forth
Create a constant named name with a value of n.

+! ( n addr -- ) .forth
Add n to the contents of the memory location specified by addr.

-! ( n addr -- ) .forth
Subtract n from the contents of the memory location specified
by addr.

allot ( n -- ) .forth
Allocate n bytes in the heap

:: ( -- xt ) .forth
Start an anonymous definition. Leaves the xt of the anonymous
definition on the stack.

alias ( xt "name" -- ) .forth
Create an alternate dictionary entry for xt with a name of name

loc: ( -- ) .forth
Start a lexically scoped section of code. These can be nested up to
four deep.

;loc ( -- ) .forth
End the current lexically scoped section of code.

fill ( addr count value -- ) .forth
Fill the memory location specified by addr with count bytes of the
specified value.

move ( source dest count -- ) .forth
Move count bytes from the memory address specified by source to dest.

place ( source count dest -- dest ) .forth
Place a string into the address specified by dest.

pad ( -- addr ) .forth
Return the address of the pad, a floating buffer above here which is
used for temporary strings and other structures.

>pad ( addr count -- addr count ) .forth
Move a string to the pad.

" ( "string" -- addr count ) .forth
Parse ahead until a " is encountered. Place this string in the PAD and
return a pointer to it.

$, ( addr count -- ) .forth
Compile a string into the current definition.

hex ( -- ) .forth
Switch to base 16

decimal ( -- ) .forth
Switch to base 10

binary ( -- ) .forth
Switch to base 2

octal ( -- ) .forth
Switch to base 8

>entry ( xt -- dt ) .forth
Given an xt, obtain the corresponding dictionary entry.

count ( addr -- addr count ) .forth
Convert a packed string to a counted string (useful with dictionary
entries)

is ( xt "name" -- ) .forth
Change the vectored word name to point to xt. If name does not exist,
create a new vectored word with a default definition of xt.

devector ( "name" -- ) .forth
Remove the vector of name, restoring the original definition.

zt ( addr count -- addr ) .forth
Convert a counted string to a zero-terminated string. Cleanup of
allocated space is done automatically.

voc: ( "name" -- ) .forth
Create a named vocabulary.

get-class ( xt -- xt class ) .forth
Obtain the class for a specified xt; if the xt is not found in the
dictionary, assumes a default class of .forth

cr ( -- ) .forth
Move the cursor to the next line, scrolling up if necessary.

space ( -- ) .forth
Display a space

spaces ( n -- ) .forth
Display "n" spaces

accept ( addr count -- count ) .forth
Accept "count" bytes of input from the keyboard (using "key") to the
specified address. Returns the number of bytes read.

# ( n -- ) .forth
. ( n -- ) .forth
Display a signed number. # does not leave a trailing space, . does.

words ( -- ) .forth
Display a list of all named words.

? ( -- ) .forth
Display the version number of RetroForth being run.

| ( "string" -- ) .forth
Everything following | is a comment.

ui ( -- ) .forth
A vectored word called after each line of input is processed

how ( addr count -- ) .forth
This word is called to process a line of input. By default, it
does eval on the passed string.

exit ( -- ) .forth
Resets the ui word to the default, empty state.

key ( -- c ) .forth
Read a single character of input from the keyboard; uses the default
line buffering settings of the host OS.
Technical Note: Reads from stdin

emit ( c -- ) .forth
Display a single byte on the screen
Technical Note: Writes to stdout

type ( addr count -- ) .forth
Display a string on the screen
Technical Note: Writes to stdout

." ( "string" -- ) .self
Display a string or compile a string into a definition, followed by
a call to type. Action depends on state.

( ( "comment" -- ) .self
Parse ahead until the ) character or end of line is found. Ignore
everything that was parsed.

: ( "name" -- ) .self
Create a new word with a name of "name" and start the compiler. The
word is created in the current class.

; ( -- ) .macro
End the current definition
See also: ;;

[ ( -- ) .macro
Switch to interpretation mode. Sets state to 0.

;; ( -- ) .macro
Compile an exit to the current word without ending the definition
Technical note:
This word will compile either a "ret" ($c3 opcode) or change
the last compiled call opcode to a jump opcode. This provides
inherent tail call elimination and allows for safe recursion.
As ; uses this, the same applies to it.

literal ( n -- ) .macro
Compile a literal from the stack into the current definition

x: ( "name" -- ) .macro
Compile a call to name ignoring the action defined by the word's class.
This treats the "name" as a .self word.

['] ( "name" -- ) .macro
Compile the xt of name into the current word

c: ( "name" -- ) .macro
Compile the code needed to compile a call to name into the current
definition. Does the same thing as: ['] name compile

as ( "class" -- ) .macro
Change the class of the most recently created word to the specified
class

>r ( n -- rs:n ) .macro
Move TOS onto the return stack.

r> ( rs:n -- n ) .macro
Move TORS to the data stack

r ( -- n ) .macro
Get a copy of TORS on the data stack.
Historical Note:
In most Forths this word is called r@. In RetroForth, it has
traditionally been called r. We chose to continue with the
RetroForth naming in this case.
Technical Note:
This has the same effect as: r> dup >r

rdrop ( rs:n -- ) .macro
Drop the TORS.
Technical Note:
Has the same effect as: r> drop

repeat ( -- ) .macro
Start an unconditional loop

again ( -- ) .macro
Close an unconditional loop, branching back to the most recent repeat
Technical Note:
Loops constructed using repeat and again are properly
tail recursive.

for ( n -- rs:counter ) .macro
Start a counted loop. The counter is put on the return stack

next ( rs:counter -- rs:counter | ) .macro
Close a loop that starts with for. Decrements the counter. If 0, exit,
otherwise branch back to for.

(if) ( n -- ) .macro
Factor of most of the forms of if
General Note:
There is seldom any reason to call this word in normal code. Its
sole purpose is to factor out a chunk of code shared between the
other conditionals.

<>if ( x y -- ) .macro
=if ( x y -- ) .macro
<if ( x y -- ) .macro
>if ( x y -- ) .macro
We describe these together since they are very similar in functionality.
To visualize the action, insert the symbol between "x" and "y" in the
stack diagram. "<>" denotes inequality.
These words start a conditional, ending with then.

if ( flag -- ) .macro
If the flag is true, execute the code between if and then. If not
true, skips ahead to then.

then ( -- ) .macro
Terminate any of the if constructs. This patches the conditional jump
to point to the proper offset in the compiled definition.

;then ( -- ) .macro
Compile an exit to the word and then terminate any if construct.
Technical Note:
The same as: ;; then

if; ( flag -- ) .macro
If true, then exit the word. Otherwise continue execution of the word.
Technical Note:
The same as: if ;; then

prior ( "word" -- ) .macro
Compile a call to an earlier definition of word.
General Note:
In general this isn't used very much, but it can be invaluable
if you are extending the functionality of an existing word.

vector ( -- ) .macro
Make the word a vectored word. This must be the first word in the
definition if you use it.
Technical Note:
Inlines to 5 nop opcodes. is will change these to a jump
opcode; devector will reset them back to nop opcodes.

s" ( "string" -- addr count ) .macro
Compile a string into the definition
Technical Note:
The current implementation of this word compiles into two
literals (the address and length), a jump, the actual string,
and a nop opcode (target of the jump).
General Note:
We use a different word for strings inside definitions than we
do for the interpreter. This is done to eliminate the need for
using the x: prefix word when we want to postpone parsing until
the word is called.

{ ( "}" -- ) .macro
Compile a sequence of code to be evaluated at runtime. The compiler
state is not altered by this.

~ ( rs:x rs:y -- rs:y rs:x ) .macro
Call LATER at compile time. Useful with VOC:

class> ( -- ) .macro
When used with "create", this allows an anonymous class to be assigned
to a word. A simple example, using a varient of the ".data" class:
: foo create: , class> @ state @ if literal, then ;

case: ( n -- ) .macro
Compare (case) to "n"; perform action up to break if the values
are identical.

break ( -- ) .macro
Break out of a case: construct

switch ( n -- ) .macro
Start a case: construct.

dup ( n -- n n ) .inline
Duplicate the top item on the stack

1+ ( x -- y ) .inline
Increment the TOS

1- ( x -- y ) .inline
Decrement the TOS

swap ( x y -- y x ) .inline
Exchange the location of the top two items on the stack

drop ( x -- ) .inline
Discard the top element of the stack

nip ( x y -- y ) .inline
Discard the second element on the stack

2drop ( x y -- ) .inline
Discard the top two elements on the stack

and ( x y -- z ) .inline
Perform a bitwise AND operation

or ( x y -- z ) .inline
Perform a bitwise OR operation

xor ( x y -- z ) .inline
Perform a bitwise XOR operation

h0 ( -- addr ) .data
Variable pointing to the top of the heap

base ( -- addr ) .data
Variable holding the current base

>in ( -- addr ) .data
Variable that holds the current address in the sequence being
evaluated.

class ( -- addr ) .data
Variable that stores the address of the current class handler for
creating new words.

state ( -- addr ) .data
Variable which contains the state of the compiler. true is compiling,
false is interpreting.

which ( -- addr ) .data
Variable pointing to the dictionary entry of the most recently
found word.

whitespace ( -- addr ) .data
Variable which contains a character used as the whitespace to search
for.

list ( -- addr ) .data
Variable pointing to a data structure used to implement lexical scoping.

true ( -- addr ) .data
Constant returning the value -1

false ( -- addr ) .data
Constant returning the value 0

(case) ( -- addr ) .data
Variable containing the current value used by the "case" words.

version ( -- value ) .data
The version number of RetroForth. This is stored as a single integer;
you can get major/minor versions by doing "100 /mod" on it.




Appendix 2: Assembly Listings

Many words in the bootstrap code for Rx are written using raw opcodes for various instructions. This has been a cause of much confusion, so we'd like to take the time now to document what each word actually compiles to. Due to the little-endian nature of the x86, the opcodes in the source are in reverse order. These listings have the normal order.

dup
8946FC mov [esi-4], eax
8D76FC lea esi, [esi-4]

drop
AD lodsd

swap
8706 xchg eax, [esi]

nip
83C604 add esi, 4

1+
40 inc eax

1-
48 dec eax

2drop
AD lodsd
AD lodsd

and
2306 and eax, [esi]
83C604 add esi, 4

or
0B06 or eax, [esi]
83C604 add esi, 4

xor
3306 xor eax, [esi]
83C604 add esi, 4

r>
<call to dup>
58 pop eax

>r
50 push eax
AD lodsd

r
<call to dup>
58 pop eax
50 push eax

rdrop
5B pop ebx

=if
0F85 jnz

<if
0F8E jng

>if
0F8D jnl

<>if
0F84 jz

(if)
3B06 cmp eax, [esi]
AD lodsd
AD lodsd
???? <conditional, passed on stack>
?????? <offset for jump, dword>

then
<patch the last jump>
$90 nop

vector
90 nop
90 nop
90 nop
90 nop
90 nop

+
0603 add eax, [esi]
83C604 add esi, 4

-
2906 sub [esi], eax
AD lodsd

*
F726 mul dword [esi]
83C604 add esi, 4

/mod
89C3 mov ebx, eax
AD lodsd
99 cdq
F7FB idiv ebx
8946FC mov [esi-4], eax
8D76FC lea esi, [esi-4]
89D0 mov eax, edx
8706 xchg eax, [esi]

@
8B00 mov eax, [eax]

!
89C2 mov edx, eax
AD lodsd
8902 mov [edx], eax
AD drop

c!
89C2 mov edx, eax
AD lodsd
8802 mov [edx], al
AD drop

w!
89C2 mov edx, eax
AD lodsd
668902 mov [edx], ax
AD drop

next
<call to dup>
58 pop eax
48 dec eax
0F8F jg near
???? <offset to jump to, dword>
AD lodsd

>>
89C1 mov ecx, eax
AD lodsd
D3E8 shr eax, cl

<<
89C1 mov ecx, eax
AD lodsd
D3E0 shl eax, cl

Valid HTML 4.01 Strict

Valid CSS!