Dec 302017
 
Forth/2 is a fully 32-bit, native Forth for OS/2 2.0.
File FORTH025.ZIP from The Programmer’s Corner in
Category OS/2 Files
Forth/2 is a fully 32-bit, native Forth for OS/2 2.0.
File Name File Size Zip Size Zip Type
ANSI.4TH 1129 551 deflated
BLOCKS.4TH 3515 1393 deflated
EXTRAS.4TH 1725 761 deflated
FORTH.DOC 55837 19003 deflated
FORTH.EXE 32793 10743 deflated
FORTH.INI 13939 4858 deflated
LICENSE.DOC 2566 1283 deflated
LOCALS.4TH 2589 1044 deflated
MIKE.4TH 1164 363 deflated
NOTES.DOC 5970 2762 deflated
STRUCT.4TH 5424 1839 deflated
THREADS.4TH 1827 834 deflated

Download File FORTH025.ZIP Here

Contents of the FORTH.DOC file




Forth/2 Preliminary Documentation
March 23, 1993


Forth/2 is a fully 32-bit, native Forth for OS/2 2.0. It requires an
80386SX or compatible microprocessor, and OS/2 2.0 or subsequent
versions.

Forth/2 was created specifically for OS/2 using MASM 6.0 Currently it is
a text-mode application which can be run either in a full screen or in a
window. It presently does not conform to any single Forth standard.
Most of the major Forth functions are included.




Table of Contents

0. Introduction

1. Using Forth/2
1.1 Installing Forth/2
1.2 Starting Forth/2
1.3 Basic information about Forth/2
1.4 Quitting Forth/2
1.5 Executing OS/2 Commands from Forth/2
1.6 Creating and Loading Forth Source Code
1.7 Error Detection

2. For Beginners

3. Special Functions IN Forth/2
3.1 Case Statements
3.2 TO Variables (or VALUE's)
3.3 String Handling Words
3.4 Threads
3.5 Arrays and Structures
3.6 Vocabularies
3.7 Local Variables

4. List of Words
4.1 Parameters
4.2 Parameter Naming Conventions
4.3 List of Word Definitions (INCOMPLETE)

5. Technical Description
5.1 Internals
5.2 OS/2 Interface
5.3 USER Variables and Multi-Tasking
5.4 TO Variables (VALUE's)
5.5 Vocabularies
5.6 Specifications and Limits


6. Compatibility Issues
7. Where to send comments/suggestions



0. INTRODUCTION

Forth is an interactive, hierarchical programming language. Forth is a
collection of functions which can be combined to create new functions,
which in turn can be used in other functions. A "program" in Forth is
simply a word which performs a series of these functions.

For example, most books on programming show you how to create a minimal
program in a particular language, one which simply types the words
"Hello, world!" on the screen. In Forth, this would be:

: HELLO ." Hello, world!" ;

Then, typing the word HELLO and tapping the Enter key will print the
message. The word : defines the new word HELLO, and defines its
function to be all the words following up to the ; . In this case, the
only function it performs is ." (pronounced "dot-quote"), which
simply types the text to the screen up to the ending " .

All the words : ." and " are functions which reside in Forth's
vocabulary, the list of defined functions. Then, HELLO was simply
added to this list of functions. To see other available functions,
type WORDS and tap the Enter key.

This documentation cannot hope to explain Forth to newcomers as well as
others have. Two very readable and highly recommended books which
explain Forth are:

Starting Forth (2nd ed.) by Leo Brodie
Thinking Forth also by Leo Brodie.

Look for them in your local library or bookstore, or obtain these and
other books by writing or calling FORTH, Inc. at:

FORTH Inc.



John D. Hall, President
Forth Interest Group
P.O. Box 2154
Oakland, CA 94621
(510) 893-6784, Fax (510) 535-1295




1. Using Forth/2

1.1 Installing Forth/2

The Forth/2 beta is currently distributed as a single .ZIP (compressed)
file called FORTHxxx.ZIP where xxx is the version number (currently at
xxx=024). Copy this file into any folder, and use PKWare's pkunzip
utility or equivalent to uncompress the files.

Forth/2 reads and compiles the file FORTH.INI upon startup. This file
helps minimize the size of the FORTH.EXE file while maximizing the
ability to customize Forth/2. Make sure this file is in the same
directory that FORTH.EXE is called from.



1.2 Starting Forth/2

To run Forth/2 from the OS/2 command line, simply change to the
directory containing FORTH.EXE and FORTH.INI and type:

FORTH

Or, create an OS/2 .CMD file to automate this process. If you have
installed Forth/2 in a directory called FORTH2 on drive C: then create
a file called FORTH.CMD as follows:

REM FORTH.CMD - Starts Forth/2
C:
CD \FORTH2
FORTH

From the WorkPlace Shell, simply open the folder containing FORTH.EXE
and FORTH.INI and double-click on FORTH.EXE. To set it up to run in a
window or in a full screen, bring up the menu for this object with Mouse
Button 2, and select Open-->Settings, click on Session, and check either
the Windowed or Full Screen buttons to select.



1.3 Basic information about Forth/2

All Forth/2 commands are case insensitive. When new definitions are
added to the dictionary, the letters are converted to uppercase first.
To see a listing of functions available in Forth/2, type Words then tap
the Enter key.

In the Words listing, you will see a fairly long list of words in the
Forth vocabulary, followed by the words available in the System
vocabulary. The System vocabulary contains the names of many OS/2
system calls which would otherwise clutter up the main Forth dictionary.

To remove these system calls from the Words listing, type Forth ONLY and
tap the Enter key. To see or use the system calls again, type System
and they will reappear.

See Section 3.6 VOCABULARIES for more information.

You may also look up individual words from the Forth prompt. If the
file FORTH2.DOC (this file) is in the same directory that FORTH.EXE
is in, type

VIEW wordname

to get a brief description of a word and what parameters it requires
and leaves on the stack. Try VIEW VIEW for example.

Use the function key F3 to recall the previous command, or F1 to recall
the previous command one letter at a time.



1.4 Quitting Forth/2

To exit from Forth/2, type

BYE

and tap the Enter key.

If Forth/2 has locked up, or is caught in an endless (or very long)
loop, use Ctrl-C to exit the program.



1.5 Executing OS/2 Commands from Forth/2

To temporarily exit to the OS/2 command line prompt, type

CommandPrompt

and tap the key. Feel free to redefine this to a shorter word.
When you have finished using OS/2 commmands, type

EXIT

and tap the key to return to Forth/2. The state of Forth/2 will
be preserved.


For frequently used system calls, you may wish to have OS/2 run the program
directly. To invoke an editor from Forth/2, and pass it the name of a
file to edit, create the following word:

: ED ( -- ) ARGS" filename.4th" SHELL" editor.exe" ;

You must give the exact path of the editor.exe program. Omit the ARGS"
if you do not want to pass the program any arguments. To create a word
called ED" " which will edit the file after it, create this
word:

: ED" ( name-- ) [COMPILE] ARGS" SHELL" editor.exe" ;


To have OS/2's command shell execute a command, such as copy, del, start,
etc., use a /C in front of the arguments passed to CMD.EXE. For example,
to create a Forth-based Copy command, do the following:

: COPY " /C COPY " Arguments "MOVE
BL Word Arguments "CAT
" " Arguments "CAT
BL Word Arguments "CAT CommandShell ;

To copy a file test.4th to a:test.4th, type:

COPY test.4th a:test.4th

See Section 3.3 on string handling for more information.



1.6 Creating and Loading Forth Source Code

Create Forth definitions directly from the command line, or create an
ASCII sequential file using your favorite text editor. To load in the
file, use the following word:

INCLUDE" FileName.TXT"

You may freely nest INCLUDE" inside other INCLUDE"ed files. However,
when compiled, the files must not alter the stack, either by requiring
numbers or by leaving extra numbers on the stack.

You may also use OS/2's cut and paste ability to copy just a few words
from your editor, paste them into a windowed Forth/2 session, and try
them out until they work correctly.

The file FORTH.INI contains basic definitions required by most Forth
programmers, as well as a few utility programs like MORE" and DIR.
You may add your own programs to this file, or make any changes you
desire. However, it is recommended that you instead use the
STARTUP.4TH file for any customizations. This way, you will avoid
having to reconcile the latest version of Forth/2 with your own
changes.

Use ECHO ON and ECHO OFF within .4TH files to echo portions of a
document to the screen while they are being loaded.

When debugging programs it is common to load and re-load a program many
times. Avoid cluttering up the dictionary by using the word FORGET.
Use the command FORGET to remove all definitions from
onward.



1.7. Error Detection

Unlike DOS, OS/2 catches any reads or writes to memory outside of the
application's allocated space. This helps keep applications protected
from one another, and notifies you that you have a bug. Almost nothing
you can do will crash anything but the one Forth/2 session.

All this protection is nice, but when things go wrong there is no way
to trace back what happened, nor examine the values of specific memory
locations. (OS/2 has system calls to do this, but they have not been
utilized in Forth/2 yet.) Fortunately, Forth makes it so easy to test
out small chunks of code that wild memory references can be narrowed
down to the culprit quickly.

Note: These exceptions will handled gracefully in a future release.



2. FOR BEGINNERS

Under construction! If you would like to know how Forth works, find a
book on programming in Forth at your local library. Or, obtain a file
from an Internet ftp site which describes how to use Forth.

The ftp site asterix.inescn.pt (192.35.246.17) contains a great deal of
information about Forth and Forth programs. Look in the directory
/pub/forth.



2.1 Forth Structure and Syntax

The following are some basic statements about Forth's stucture and
syntax:

Forth is made up of a DICTIONARY of words.

The DICTIONARY can be organized into separate VOCABULARIES.

Each word in the dictionary performs some kind of function.

All words and numbers must be separated by at least one SPACE.

To use a word or series of words, type them in and tap Enter.

Some words, like CONSTANTs, simply return a value.

Other words perform various functions.


Forth is STACK-ORIENTED, and uses Reverse Polish Notation.

The stack is used to pass PARAMETERS to words.

Many words OPERATE on a certain number of parameters on the stack,
THEN RETURN zero or more parameters on the stack.

For example, the word '+' adds two numbers from the stack, leaving one.

To see the CONTENTS OF THE STACK, use the word .S


The defining word : CREATES a new word in the dictionary.

Create this: : TEN+ 10 + ; Try: 100 TEN+ .S -10 TEN+ .S

The word : is just another word in the dictionary, which creates
a dictionary entry for the word which follows it, and defines
the word's funtion to be to perform all the words which follow
it up to the ;

Forth allows ANY ASCII characters to be used in word names.

Other words, like VARIABLE or
CONSTANT also create new words.

VARIABLE's push the address where the variable is stored onto the stack.
To FETCH the contents of the variable, use @
To STORE a number into the variable, use !
The word @ is pronounced "FETCH", and ! is pronounced "STORE".

To print and remove the number from the top of the stack, use . (DOT)

One convention in Forth is to use . in any word which prints things.


Planned sections:

2.2 Simple math, .S, ."
2.3 : routines, IF...THEN
2.4 DO...LOOP BEGIN...AGAIN
2.5 Number formatting
2.9 Making something work inside : definitions (e.g. COPY)




3. SPECIAL FUNCTIONS IN FORTH/2

This section describes special words available in Forth/2.


3.1 CASE STATEMENTS

CASE statements are for when you evaluate a number of different options,
as in typing the day of the week. Try the following word:

: DAY ( n ) CASE 1 OF ." Monday" ENDOF
2 OF ." Tuesday" ENDOF
3 OF ." Wednesday" ENDOF
4 OF ." Thursday" ENDOF
5 OF ." Friday" ENDOF
6 OF ." Saturday" ENDOF
7 OF ." Sunday" ENDOF
." Day " . ." ?" ENDCASE ;

Simply type 5 DAY and it will print "Friday".

Note that you need a DROP or a similar effect between the last ENDOF
and the ENDCASE, which differs from other implementations of CASE..ENDOF.



3.2 TO VARIABLES (or VALUE's)

TO variables help make Forth simpler to use and to read. They are most
useful when you fetch a value much more often then you store into that
variable.

To create a TO variable, simply type:

INTEGER X_LOC

and it is initialized to 0. To examine the value of X_LOC, simply type:

X_LOC .

No @ is necessary. To store a new value into X_LOC simply type

100 TO X_LOC

Then, X_LOC . will show 100. To add to a TO variable, use +TO as in:

50 +TO X_LOC

Then, X_LOC . will show 150. To create a TO variable initialized
with a certain value, use:

1020 TO INTEGER Taxes


Create arrays of integers with another word:

100 INTARRAY XP[] ( Creates an array called XP[] of 100 elements )

100 0 DO 100 RND I TO XP[] LOOP ( Store random numbers into XP )

0 100 0 DO I XP[] + LOOP ( Calculates the sum of all XP )

And, using n index +TO XP[] will add n to the indexed integer.

You can also create STRING TO variables. See section 3.3.



3.3 STRING HANDLING WORDS

All of the string words such as ." can be used outside colon definitions.
A number of words have been included with Forth/2 to facilitate string
handling.


3.3.1 Creating and Typing Strings

To create a string constant, use the word " as follows:

" April 4, 1999" "CONSTANT Deadline

Then, use ". (quote-dot) to type it out:

Deadline ".

Develop your own synonyms for ". like @Type or $. etc. Or, a
simpler way to create a string constant is like this:

Call" Bill Clinton" President

To create a zero-terminated string, such as for use with OS/2 system
calls, use 0" as follows:

: Invoice_File 0" Invoices.DAT" ;

Then, use 0". to type the string out:

Invoice_File 0".

or either type or load a file:

0" Calendar.4TH" MORE
0" Calendar.4TH" INCLUDE

Use 0"COUNT to obtain the size of a 0-terminated string. In the
example below, the 0" string is counted, then moved into TempStr as
a counted string with MOVE>"

CREATE TempStr 80 ALLOT
0" FileName.TXT" 0"COUNT TempStr MOVE>"


You may want to terminate a regular counted string with a zero
(if you have alloted room beyond the string). To do this, use

CREATE StrA 80 ALLOT
" To be zero-terminated" StrA "MOVE
StrA 0-Terminate
StrA W+ 0". \ Skip length and use 0". to print

These words all work in colon definitions as well.


3.3.2 Copying and Concatenating Strings

The word "MOVE will move a counted string to another area of memory.
To copy a string to another memory area, do the following:

( Make room for strings up to 80 characters long )
VARIABLE StringBuf 80 ALLOT

Call" Warning: " WarnMsgBeg

: InitWarning WarnMsgBeg StringBuf "MOVE ;

: TempWarning InitWarning
" Temperature over limit!" StringBuf "CAT ;

: PresWarning InitWarning
" Pressure overload!" StringBuf "CAT ;


TempWarning StringBuf ". ( Will print the warning message )


The word "CAT will add one string to the end of a second string.
It accepts two strings on the stack, ( string1 string2 -- ) which are
both counted-strings, and adds string1 to the end of string2.

Because both WORD and " return the address of a counted string, most
string handling words act upon a single address. That address points to
a 32-bit length, followed by the characters which make up the string.
However, it is often required to manipulate a string which does not have
a length at the address, but instead the length is on the stack. To use
words like "CAT or =" with a string and length, first move it to a
temporary string buffer with the word MOVE>" as follows:

0" First Second Third Word" 6 StringBuf MOVE>"

StringBuf ". \ Will type "First "

The above code copies the first six charcters from the 0" string to
StringBuf, because 0" returns the address of the first charcter of the
string.

The word MOVE>" will copy a character string of a given length to an
address and save it as a counted string.

MOVE>" ( address length dest_address -- )


3.3.3 Comparing Strings

You can check if two strings are equal with =" which accepts the
addresses of two counted strings, and returns true or false if they are
equal or not. This word is case insensitive, so


Call" AbC" String1
String1 " aBc" ="

( Note that you cannot type in from the command line two quoted strings
in a row, because the second is parsed into the same location as the
first one, overwriting it.)

Often, however, you will have to compare one string, which is given as
an address and length, with another counted string. Instead of using
MOVE>" every time you wish to compare the string with another
(counted) string, use the word =STRING as follows:

address length " Compare String" =STRING IF ... ELSE ... THEN




3.3.4 String TO Variables

String TO variables are also available. They are used in the same way
as regular TO variables:

STRING S1 \ Create string S1

" This is String 1" TO S1 \ Store string into S1

" Initial String 2" TO STRING S2 \ Create string with initial value

" Plus some extra" +TO S2 \ Adds "Plus some..." to end of S2

S1 ". S2 ". \ Type contents of strings S1 and S2





3.4 THREADS

Threads are separate processes which run concurrently with other
programs. They share memory space with the program that created them.

OS/2 supports thread creation and management through the functions
DosCreateThread, DosKillThread, DosResumeThread, DosSuspendThread, and
DosWaitThread.

To load thread support into Forth/2, issue the command

INCLUDE" THREADS.4TH"

in your STARTUP.4TH file, which should itself be INCLUDE"ed in
FORTH.INI.


3.4.1 Creating Threads and USER Variables

In Forth/2 a thread can be created to perform some action in the
background, and free up the main program for other uses. For example,
you may want to start a separate thread to load a large table of numbers
into memory and perform computations. Or, a thread can simply put
itself to sleep and wake up at hourly intervals to perform some
function.

Most self-contained words in Forth/2 can be performed as a separate
thread. When the thread starts, it is given its own stack, and its own
copies of USER variables.

USER variables are variables which multiple threads may use without
interfering with each other. For example, if you define a variable X1,
but two different threads use this variable, then problems may arise.
One thread may store into the value, only to have the intended value
overwritten by another thread.

If X1 was created as a USER variable, however, then although the code for
each thread uses a USER variable called X1, they will be in entirely
different locations and will not interfere with each other. This allows
you to create what is commonly referred to as reentrant code.

A good example is variables which hold file handles, file pointers, etc.
Without USER variables, if two threads tried to read or write files at
the same time, the handles and pointers would overwrite each other. You
would need two separate words which use different sets of variables in
order to get just two threads to do the same function at the same time.

With USER variables, when the thread is created it is given a copy of
the current USER variables. After that, can freely modify them,
while using the exact same Forth words that other threads use.


In many cases, you may want to share a variable between threads. For
example, you may want to create a "watchdog" thread which continually
examines a variable for a certain number. This is a great help in
debugging programs.

variable XV1
: WatchDog begin XV1 @ 0< IF CR ." XV1 < 0 !" 1 sleep then
100 usleep
0 until ;

To run this as a separate thread, simply type

Thread WatchDog

and test it by storing different values into XV1. The 1 sleep is
there to prevent a huge number of warning messages, and the 100 usleep
is there to prevent the thread from slowing down the system too much.



3.4.2 Exiting and Terminating Threads

When Forth/2 is exited via BYE, all threads created by it will also be
terminated. (This may be controlled by an option in an upcoming version
of Forth/2). If you wish to kill separate threads, use the ThreadID
variable to obtain the identification number of the most recently
created thread, and type

ThreadID @ KillThread

Look at the examples in the THREADS.4TH file for two other examples of
using threads. If the word, such as Timer, finishes executing and
gets to the ; then the thread will terminate itself. The thread may
also use BYE and EXIT to terminate before the ;



3.4.3 Using ABORT in Threads

The word ABORT is vectored through 'ABORT. In effect, ABORT is

: ABORT ( -- ) 'ABORT @ EXECUTE ;

If your thread calls ABORT or ABORT" you may want to redirect ABORT to
terminate the thread. If you do not, it will halt the main program from
doing whatever it is doing. To redirect ABORT do the following:

: ThreadAbort ' BYE CFA @ 'ABORT ! ;

Then, simply call ThreadAbort in the beginning of the code for the
thread. When ABORT is called from the thread, the thread will
gracefully terminate.



3.4.4 The USER Variable Area

Currently, a number of system variables such as BASE, the dictionary
pointer, STATE, et al., are not USER variables yet. (It requires some
slight changes in how things are coded: see Part 5 for details).

Consequently, threads cannot request input from the terminal (although
they may send harmless output to the screen), nor can they compile new
definitions. These limitations may change in a future release.

If you wish to initialize a thread with a different default USER
variable area other than the current thread's area, then store the
address of the new default area into the variable 'USER. The size of
the USER area is UDP @ USER0 - where UDP is the USER area free
memory pointer, and USER0 is the current thread's USER variable base.




3.5 ARRAYS AND STRUCTURES

Arrays and structures are supported in the file STRUCT.4TH which you
can load via INCLUDE" STRUCT.4TH"

Structures hold groups of various information, such as a transaction
which has an identification number, a time/date stamp, a text
description, codes, and various other parameters. A structure brings
all of this information into one convenient element which can be easily
manipulated.

<>



3.6 VOCABULARIES

Vocabularies under Forth/2 work like most other Forths. To create a
vocabulary to hold a group of words, use

VOCABULARY

In order to tell Forth which vocabulary you wish to add new colon
definitions, variables, constants, etc. to, use

DEFINITIONS

Two vocabularies are already defined, FORTH and SYSTEM. When you
use the command WORDS to list the current Forth words, you will see the
Forth vocabulary and possibly other vocabularies as well. To only show
the FORTH vocabulary, simply type

Forth ONLY

Then, WORDS will show only the definitions in the Forth vocabulary.
To see the SYSTEM vocabulary, simply type

SYSTEM WORDS

and you will see the system vocabulary, followed by the Forth
vocabulary. Then, type FORTH words and you will instead see the Forth
vocabulary first, followed by the system vocabulary.

Forth/2 keeps track of a CURRENT vocabulary, where all new definitions
are added, as well as a list of CONTEXT vocabularies. CONTEXT vocab-
ularies are where Forth searches to find a word, either to execute it or
to compile it. What you see in the WORDS listing is the same order that
Forth/2 will use to search for words when compiling.

Whenever you invoke the name of a vocabulary, if it is already in
CONTEXT then it will simply be moved into the top of the search order.
If it is not already in CONTEXT, then it will be added to the top of
the search order.

To create two vocabularies, then define the search order for the words
as Voc2 first, then Voc1, then Voc3, then System, then Forth, do the
following:

VOCABULARY Voc1 : XV1 ... ; : XV2 ... ;
VOCABULARY Voc2 : XV3 ... ; : XV4 ... ;
VOCABULARY Voc3 : XV1 ... ; : XV2 ... ;

Forth ONLY
Forth System Voc3 Voc1 Voc2 DEFINITIONS

New words will be added to Voc2. In the example above, the same word
XV1 is defined in both vocabularies Voc1 and Voc3. In this case,
because of the search order defined, if XV1 is invoked, then the XV1
from Voc1 will be executed, even though the XV1 from Voc3 was defined
later.

The Forth ONLY above clears out any other unwanted vocabularies.



3.7 LOCAL VARIABLES

Local variables enhance Forth by reducing the complexity of stack
operations, and allow you to assign descriptive labels to the numbers
passed to the word on the stack.

Local variables are supported in the file called LOCALS.4TH. Use
INCLUDE" LOCALS.4TH" in STARTUP.4TH to load local variable support every
time Forth/2 is started.

If you pass three parameters to a word, perhaps ( addr len dest -- )
and want to copy addr,len to the dest address, you could label these
parameters as:

LOCALS| addr len dest |

After that, you are free to use the words addr, len, and dest just as
if they were regular Forth words. When the word containing the LOCALS|
is executed, at the point LOCALS| appears the top three numbers on the
stack are saved into a special data area, and the numbers are dropped
off the stack. When addr gets executed, it fetches the value of the
thrid element in the locals data area, and pushes it onto the stack.

The general format of this is as follows:

: WordName LOCALS| local1 local2 local3 ... local8 |
... local1 ...
... local2 ... local3 ... etc. ;

The entire LOCALS| ... | must be on the same line.

One simple example is below:

: DIGITS LOCALS| digit1 digit2 digit3 |
digit1 100 *
digit2 10 *
digit3 + + ;

DIGITS turns 1 2 3 DIGITS into 123


In regular Forth you would need code like this to do the same thing:

: DIGITS ROT 100 * ROT 10 * + + ;

It is much less clear what the second version of DIGITS does.

LOCAL's are somewhat slower than regular Forth words, although this may
be changed if some words are turned into assembly words.




4. DESCRIPTION OF WORDS

This is an incomplete list of the words included in Forth/2. Some are
included in the FORTH.EXE file, others are in the FORTH.INI file.


4.1 Parameters

The parameters each Forth word requires, and the parameters it returns
are specified by the following convention:

( n1 n2 n3 ... -- r1 r2 r2 ... )

where n1, n2, n3, etc. are the required parameters, and r1, r2, r3,
etc. are the results. For example, the word DUP duplicates the top
number on the stack, so it has the stack definition:

DUP ( n -- n n ) Duplicates the number on top of the stack

The word ?DUP duplicates the number on the stack if it is non-zero. It
returns different numbers of parameters depending on the top of stack:

?DUP ( n -- n 1 -- 0 ) DUPlicates n if non-zero.

The word ' (tick) fetches the address of the following word, e.g. ' DUP
will return the starting address of the DUP definition header, the Link
Field Address (LFA).

' ( word-- LFA ) gets the LFA address of the following word

So, when something is right up against the --'s, it expects a word or
set of words from the input stream.



4.2 Parameter Naming Conventions

Some naming conventions for the parameters are:

addr a 32-bit address (or just 'a')
b an 8-bit byte
char an ASCII character
n a 32-bit number
" the address of a string
? the number of parameters is unknown



4.3 List of Word Definitions (INCOMPLETE)

--Begin--
! ( n addr -- ) Stores the number n into address addr
" ( word" -- a ) Returns the addr of the counted string ending in "
"CAT ( addr1 addr2 ) ConCATenates counted string at addr1 to end of addr2
"CONSTANT ( " -- ) Creates a string constant Does> ( -- a )
"MOVE ( addr1 addr2 ) Copies counted string from addr1 to addr1
#OUT ( -- addr ) Variable holding # of characters written to line
#TIB ( -- n ) Address of the current offset in the TIB
' ( word-- LFA ) Gets the LFA address of the following word
'ABORT ( -- addr ) Pointer to code to execute when ABORT is called
'USER ( -- addr ) Pointer to the default USER area for new threads
( ( -- ) Skips interpreting words up to the next )
* ( n1 n2 -- n1*n2 ) Multiplies numbers on top of the stack
*/ ( a b c -- d ) Calculates d = a * b/c i.e. mutliply by ratio
*/MOD ( a b c -- r d ) Calculates d = a * b/c r=remainder
+ ( n1 n2 -- n1+n2 ) Adds the two numbers on top of the stack
+! ( n addr -- ) Adds n to contents of addr
+LOOP ( n -- ) Adds n to loop counter and loops back
+TO ( -- ) Sets flag for adding value into integer: 1 +TO X1
, ( n -- ) Stores n at dictionary pointer, increments DP by 4
- ( n1 n2 -- n1-n2 ) Subtracts numbers on top of the stack
-ROT ( a b c - c a b ) Rotates top of stack to the third position
-TRAILING ( a n1 -- a n2 ) Truncates extra spaces at end of string at a[ddr]
. ( n -- ) Prints the value of the top number on the stack
." ( == ) Prints a string up to the first "
.( ( -- ) Prints a string up to the first )
.S ( -- ) Prints the contents of the stack non-destructively
/ ( n1 n2 -- n1/n2 ) Divides numbers on top of the stack
/MOD ( n1 n2 - n3 n4 ) Divides n1/n2, n3=remainder, n4=quotient
0" ( word" -- n ) Returns the addr of the 0-terminated string ending in "
0"COUNT ( a -- a len ) Returns address and length of the 0-terminated string
2CONSTANT ( n1 n2 name ) Creates a two-number constant: 100 0 2CONSTANT CNTDWN
2DROP ( n1 n2 -- ) Drops two numbers from the stack
2DUP ( n1 n2 -- n1 n2 n1 n2 ) Duplicates the double number on the stack
: ( -- ) Creates a new word to perform the words up to the ;
; ( -- ) Marks the end of a definition
< ( n1 n2 -- f ) True if n1 < n2
<< ( n1 n2 -- n3 ) Shifts n1 by n2 bits left, e.g. 100 5 << = 100 2^5 *
=" ( a1 a2 -- f ) True if counted strings at a1 and a2 equal. See STRING"
=STRING ( a1 len a2 -- f ) True if string at a1,len equal to counted string a2
> ( n1 n2 -- f ) True if n1 > n2
>> ( n1 n2 -- n3 ) Shifts n1 by n2 bits right, e.g. 100 3 >> = 100 2^3 /
>IN ( -- addr ) Variable holding number of bytes left in input line
>R ( n -- ) Pushes number onto return stack. Use in pairs with R>
?COMPILE ( -- ) Gives error if not called in compile mode
?CR ( -- f ) Does a CR if #OUT is greater than 64
?DUP ( n -- n 1 -- 0 ) DUPlicates n if non-zero. Eliminates an ELSE DROP THEN
?STACK ( -- ) Gives error if the stack is out of bounds
@ ( addr -- n ) Replaces address addr with it's contents
@+ ( a -- a+4 [a] ) Returns address a+4 and contents of a. See COUNT
ABORT ( -- ) Halts execution and returns to the Forth command line
ABORT" ( f -- ) If true shows an error msg ending in " and calls ABORT
ABS ( n -- |n| ) Calculates absolute value
ALLOT ( n -- ) Allocates storage space after VARIABLE or CREATE
ALONG ( a b -- a+b a ) Use before DO when you have an address and length
AND ( n1 n2 -- n3 ) Returns the bit-wise AND of n3 = n1 AND n2
ARGS" ( arguments" ) Passes arguments to the SHELL" : ARGS" FILE.TXT"
ASCII ( char-- ) Returns ASCII value of character: ASCII A
BASE ( -- addr ) Variable holding base for number conversion
BL ( -- c ) ASCII character for a blank space, 32
BYE ( -- ) Terminates current process (BYE == Leave forth)
C! ( b n -- ) Stores byte at address n
C, ( b -- ) Compiles a byte into the dictionary
[email protected] ( n -- b ) Fetches byte at address n
CALL" ( ) Creates a string constant, CALL" Clinton" President
CMOVE ( a1 a2 len -- ) Moves len bytes from address a1 to address a2
CMOVE> ( a1 a2 len - ) Moves len bytes from a a1 to a2, high memory first
CommandShell ( -- ) Temporarily exits to the OS/2 command line
COMPILE ( -- ) Postpones compilation of the following word
COMPILECALL ( adr -- ) Compiles a call to the address given
CONSTANT ( n word -- ) Creates the constant which represents n
CONTEXT ( -- addr ) Address of CONTEXT, list of vocabularies to search
COUNT ( a -- a+1 [a] ) Returns the address+1 and the byte at addr
CR ( -- ) Outputs a carriage return and line feed.
CREATE ( word-- ) Creates a word which returns its address when called
CURRENT ( -- addr ) Pointer to vocabulary where new words are added
DECIMAL ( -- ) Start entering and printing all decimal numbers
DEFINITIONS ( -- ) Sets the vocabulary where new words are created
DELAY ( n -- ) Suspends the current thread for at least n milliseconds
DEPTH ( -- n ) Number of elements on stack
DO ( n2 n1 -- ) Repeats from n1 up to n2. Use with LOOP
DOES> ( -- ) Used after CREATE to define run-time action of word
DP! ( n -- ) Sets the dictionary pointer to a new value: DP !
DPL ( -- addr ) Variable holding decimal point location from NUMBER?
DROP ( n -- ) Drops the top number from the stack
DROPS ( n -- ) Drops n words from the stack
DUMP ( addr len -- ) Shows contents of a range of memory
DumpRegisters ( -- ) Shows the current contents of the CPU registers
DUP ( n -- n n ) Duplicates the number on top of the stack
ECHO ( -- addr ) Use ECHO ON or ECHO OFF to echo the file being loading
ELSE ( -- ) Executes following words when IF condition false
EMIT ( char -- ) Outputs an ASCII character
EOF? ( -- f ) True if at the end of file read with FRead
EXECUTE ( addr -- ) Executes the code at address, ' .S CFA @ EXECUTE
EXITCODE ( -- addr ) Variable holding exit code returned to OS/2 after BYE
FALSE ( -- 0 ) Returns the constant FALSE = 0
FBUFFER ( -- addr ) Address of a 16K buffer for loading files
FENCE ( -- addr ) Sets lowest point to which you can FORGET words
FILL ( addr n b -- ) Fills n bytes at addr with byte b
FOR ( n2 n1 -- ) Repeats from n1 to n2 if n2>n1. Use with NEXT
FORGET ( word-- ) Forgets all words up to and including the word
FSEEK ( h ptr -- f ) Sets file handle h's position to ptr, f=0 successful
HERE ( -- n ) Returns dictionary pointer
HEX ( -- ) Start entering and printing all hexadecimal numbers
I ( -- n ) Current LOOP value of innermost LOOP
IF ( f -- ) Executes the following words if flag f is non-zero
IMMEDIATE ( -- ) Marks last word defined as IMMEDIATE.
INCLUDE ( " -- ) Loads and compiles the file name given at the address
INCLUDE" ( file"-- ) Loads and compiles the file name ending with a "
INTEGER ( word -- ) Creates an integer which returns it's value. See TO +TO
J ( -- n ) Current LOOP value of second nested LOOP
K ( -- n ) Current LOOP value of third nested LOOP
KEY ( -- b ) Waits for a key to be received, returns ASCII value
KEYNOWAIT ( -- n ) Returns the raw keyboard scan code: 27=no key
LAST ( -- addr ) Address containing pointer to last word defined
LEAVE ( -- ) Causes DO...LOOP to terminate at next LOOP
LITERAL ( -- ) Compiles a literal number into word: [ 100 ] LITERAL
LOCALS| ( ? -- ) Defines local variables: LOCALS| a b c | ...
LOOP ( -- ) Branches back to DO
MAX ( n1 n2 -- n3 ) Returns the greater of n1 and n2
MIN ( n1 n2 -- n3 ) Returns the lesser of n1 and n2
MOD ( n1 n2 -- n3 ) Returns n1 modulo n2 (i.e. the remainder)
MORE" ( -- ) Types the contents of a text file: MORE" THREADS.4TH"
MOVE>" ( a len a2 -- ) Moves string at address a,len to counted string at a2
NDROP ( n -- ) Drops n elements off the stack
NEGATE ( n -- -n ) Multiplies n by -1
NEXT ( -- ) Loops back to FOR
NIP ( n1 n2 -- n2 ) Removes the second number from the stack
NUMBER? ( " -- n 1 -- 0 ) Converts string to number: true if success else false
OFF ( addr -- ) Stores a FALSE at the address
ON ( addr -- ) Stores a TRUE at the address
ONLY ( -- ) Makes the most recently cited vocabulary the only one
OPEN ( 0" -- h ) Opens the 0-terminated file name and returns handle
OR ( n1 n2 -- n3 ) Returns the bit-wise OR of n3 = n1 OR n2
OUT ( -- addr ) Address of variable holding output cursor position
OVER ( a b -- a b a ) Copies the second number to the top of the stack
PAD ( -- addr ) Address of scratch-pad memory
PICK ( ? i -- ? ) Copies the i'th element on the stack to the top
QUIT ( -- ) Quits execution and returns to Forth command line
R> ( -- n ) Pops number from return stack. Used with >R
[email protected] ( -- n ) Copy number from top of return stack
READLN ( h -- a len ) Reads next line of input from file h, then try TYPE
RECURSE ( -- ) Compiles a recursive call to the definition itself
ROLL ( ? i -- ? ) Rolls the i'th element on the stack to the top
ROT ( a b c -- b c a ) Rotates third element to top of stack
SHELL" ( string" -- ) Runs the given program: SHELL" EDITOR.EXE" See ARGS"
SP! ( addr -- ) Sets stack pointer to address. SP0 SP! to clear stack
SP0 ( -- addr ) Base address of the stack
[email protected] ( -- addr ) Stack pointer address
SPACE ( -- ) Outputs a space
SPAN ( -- n ) Address of the current offset in the TIB (same as #TIB)
STATE ( -- addr ) Address of variable holding compilation state
STRING ( -- ) Creates a TO variable string: STRING S1 " YES" TO S1
SWAP ( a b -- b a ) Swaps the two numbers on the stack
SYSCALL ( ? sys$ -- ? ) Calls a system routine starting with SYS$
THEN ( -- ) Marks the end of an IF .. THEN or IF .. ELSE .. THEN
THREAD ( -- ) Starts a thread to execute : THREAD Bunny
TIB ( -- n ) A variable holding a pointer to the Terminal Input Buf.
TO ( -- ) Sets flag for storing value into integer, e.g. 1 TO X1
TOGGLE ( n addr -- ) Toggles bits n at word address addr
TONE ( freq dur -- ) Emits a tone for a given frequency(Hz) and duration(ms)
TRUE ( -- -1 ) Returns the constant TRUE = -1
TUCK ( n1 n2 -- n2 n1 n2 ) Makes a copy of n2 under n1
TYPE ( addr n -- ) Types out the n characters at address addr
U* ( n1 n2 -- n3 ) Unsigned multiply n3 = n1 * n2
U. ( n -- ) Prints the unsigned value of the number
U/ ( n1 n2 -- n3 ) Unsigned divide, n3 = n1 / n2
U*/MOD ( a b c - r d ) Unsigned d = a * b/c r=remainder
UALLOT ( n -- ) Allot n words of USER variable space
UDP ( -- addr ) Address of USER variable pointer
USER ( -- ) Creates a USER variable called
USER0 ( -- addr ) Address of beginning of USER varaible area
VARIABLE ( -- ) Creates a variable called
VIEW ( -- ) Views the description for a word. VIEW VIEW
VOCABULARY ( - ) Creates a new vocabulary: VOCABULARY VOC1
W ( -- 4 ) Word size for addresses, variables, etc. Also CELL
W- ( n -- n-4 ) Subtracts the word size 4. Also CELL-
W+ ( n -- n+4 ) Adds the word size 4. Also CELL+
W* ( n -- n*4 ) Multiplies by the word size 4. Also CELLS
W! ( addr 16b -- ) Stores the 16-bit word at addr. DO NOT CONFUSE WITH W
[email protected] ( addr -- 16b ) Fetches the 16-bit word at addr. DO NOT CONFUSE WITH W
WORD ( char string-- a ) Parse string delimited by char, return counted string
WORDS ( -- ) Shows the forth words in CONTEXT
XOR ( n1 n2 -- n3 ) Returns the bit-wise exclusive-OR of n3 = n1 XOR n2
XY ( x y -- ) Move cursor to screen location (x,y)=(col,row)
[ ( -- ) Stops compilation in : definitions
[COMPILE] ( -- ) Compiles the following immediate word into definition
\ ( -- ) Signals that the rest of the line is a comment
] ( -- ) Resumes compilation in : definitions

STRINGS String words: WORD " =" "CAT "MOVE MOVE>" =STRING STRING



5. TECHNICAL DESCRIPTION

5.1 Internals

Forth/2 is a direct, subroutine threaded Forth. Colon definitions are
made up of a series of 32-bit relative calls to other words. The opcode
for these calls is E8h, followed by a four-byte offset to the code.

The return stack pointer (RP) is the ESP register.
The user stack pointer (SP) is the EBX register and grows downward.
The user area pointer (UP) is the EBP register.
There is no need for an instruction pointer (IP).

So, a DUP instruction would be coded this way in MASM:
mov eax,[ebx]
sub ebx,4
mov [ebx],eax
ret

DO...LOOP's are created as in-line code, thereby maximizing performance.
Literal numbers are also coded in-line. They would take up 9 bytes to
code using the more traditional call to LIT, followed by the literal.
However, in only ten bytes it can be done much more quickly using
in-line code.

Currently, Forth/2 is case insensitive. All defined words are converted
to upper-case before being entered into the dictionary. Forth words can
be entered in any mix of upper and lower case, they are all the same.

All math words like * / + - etc. are fully 32-bit. In addtion, the
words */ and */MOD use 32-bit arguments but do generate 64-bit
intermediate results. No 64-bit double-number words such as D+, M*, or
UM/MOD have been defined.



5.2 OS/2 Interface

OS/2 32-bit system calls are implemented by saving most registers,
saving the ESP in EBP, setting ESP to EBX, calling the system routine,
then restoring ESP and the other registers. Basically, this switches
the stacks so that the parameters passed to OS/2 calls do not need to be
shuffled around. The function which makes the system call must drop all
the parameters it put on, as OS/2 does not do this (just as in C).

To call OS/2 system functions, you must use the word SYSCALL which
requires the address of the system call on the top of the stack. Words
such as SYS$BEEP, SYS$READ, SYS$WRITE, SYS$OPEN et. al. return the
addresses corresponding to the OS/2 functions DosBeep, DosRead,
DosWrite, DosOpen, etc. All use 32-bit arguments.

The function calls are kept in a separate vocabulary called SYSTEM.
Before you use any of these system calls, make sure SYSTEM gets executed
to put the vocabulary into CONTEXT so that these calls will be visible.

To translate OS/2 documentation, usually given with C calling
conventions, into equivalent Forth/2 code, remember that in C the
parameters are pushed onto the stack from right to left. For the
function DosBeep, the OS/2 Technical Library lists the function this
way:

APIRET DosBeep (ULONG ulFrequency, ULONG ulDuration)

To call this from Forth, do the following:

Duration @ Frequency @ SYS$BEEP SYSCALL 3 DROPS

You must drop all parameters after each SYSCALL.


To pass pointers to OS/2 functions, simply use the names of the
variables, which place their address on the stack. DosGetDateTime is
listed as:

APIRET DosGetDateTime (PDATETIME ppPDateTime)

so call it as follows:

VARIABLE DateTime 7 ALLOT ( DateTime structure is 11 bytes long )

DateTime SYS$GetDateTime SYSCALL 2DROP

The ASCII characters representing the date and time will be stored in
the memory structure at DateTime.



5.3 USER Variables and Multi-Tasking



5.4 TO Variables (VALUE's)

TO variables use a system variable called %TO to determine if they should
return their value, or store or add into the variable. If %TO is zero, the
variables return their value. If %TO is >0, the number on top of the stack
is stored into the variable. If %TO is <0, the number on top of the stack
is added to the variable. The following words, defined in assembly for
speed but reproduced here, show how this works.

VARIABLE %TO
: TO 1 %TO ! ;
: +TO -1 %TO ! ;
: ( addr -- ? ) %TO @ 0 = IF @ ELSE
%TO @ 0 > IF ! ELSE +! THEN THEN
0 %TO ! ;

: INTEGER ( -- ) CREATE 0 , DOES> ;

This can be augmented to accept initial values when the INTEGER is created.

: INTEGER ( -- ) CREATE HERE 0 ,
%TO @ IF ELSE DROP THEN
DOES> ;

To store an initial value into the first version of INTEGER, you must do:

INTEGER X 100 TO X

whereas with the second version you can combine these as:

100 TO INTEGER X



5.5 VOCABULARIES

Vocabulary structures are made up of three words:

1 0 Unused
2 VocPtr Pointer to the last Forth word created in this vocabulary
3 Voc-Link Link to most recently created vocabulary

The word VOC-LINK is the head of a linked-list of vocabularies in the
order in which they were created. VOC-LINK is needed in order to
FORGET words across multiple vocabularies.

When created, each vocabulary VocPtr is set to zero. That way, all
vocabularies chain back to 0, not any other vocabularies. To search
multiple vocabularies, each vocabulary name must be executed to
have it insert itself into the list of vocabularies in CONTEXT.



5.6 Specifications and Limits

The following limits are present in the current Forth/2 system. Since this
is a 32-bit Forth using a flat, non-segmented address space, most of these
limits are arbitrary and are simply controlled by constants in the compiler.

Word size: 4 Bytes (32 bits)
Word name length maximum size: 31 Bytes
Base code (.EXE) size: 30 K Bytes (So far)
FORTH.INI code size: 13 K Bytes (So far)

Dictionary size limit (incl. FORTH.INI): 64 K Bytes
User stack size limit: 4 K Bytes (1024 elements)
User stack underflow size limit: 4 K Bytes (1024 elements)
Return stack size limit: 8 K Bytes* (2048 elements)
Return stack underflow size limit: 0 K Bytes
USER variable area size limit: 4 K Bytes
File Buffer Size limit: 16 K Bytes (for FORTH.INI)
Terminal Input Buffer (TIB) Size limit: 256 Bytes

Terminal I/O: ANSI compatibility
Alphabetic case sensitivity: Case insensitive (Dup==DUP)


* Note: The return stack is handled by OS/2 using a guard-page technique.
If the stack grows past 4K, a new 4K Byte page is allocated.
Forth/2 will allow up to 1 one more page to be allocated.




6. COMPATIBILITY

There are some differences in the way this Forth works compared to most
other Forths. Some of these may be changed in a future version.


6.1 Compatibility with Forth-83

Since this Forth is still in development, not all words are supported
yet. The following words from the Forth-83 Standard have not been
created yet in Forth/2:

>BODY CONVERT
D+ D< DNEGATE UM* UM/MOD

In the file BLOCKS.4TH which accompanies Forth/2 are some preliminary
words which can read and load block files. However, multiple buffers
and the ability to save those blocks back to disk has not been imp-
lemented. It is recommended that you use the conversion utility to
convert your block files into sequential files.

The following words are therefore only partially supported:

BLK BLOCK BUFFER FLUSH LOAD SAVE-BUFFERS UPDATE




6.2 Compatibility issues with specific words and functions


File Loading

Currently, files are loaded into a 16K buffer and compiled in one
pass. This will be changed in future releases to support line-by-line
compilation. So, the file size limit for FORTH.INI is currently 16K.

Block files are not directly supported. A preliminary block file handler
has been implemented using the OS/2 functions SYS$READ, SYS$SEEK, and
SYS$WRITE in the file BLOCKS.4TH.



WORD and Strings

All string lengths are fully 32-bits long (no 255-character limits on
strings.) This will certainly cause problems if you are using
COUNT. If so, replace it with @+ which does the same thing except for
using a 32-bit count. Remember, though, that doing a [email protected] on the string
will return the correct length if it is less than 256 characters long
because the 80386 stores the least significant byte in lower memory.
And, using it the normal way will usually do no harm. You will simply
be typing out extra zeros in the front and chopping off the last three
characters.

Feel free to redefine COUNT if necessary, but people often use COUNT
for purposes other than with strings, so it was left alone.

Or, redefine WORD like this:

: WORD ( char string"-- addr ) WORD DUP [email protected] OVER 3 + C! 3 + ;

which stores an extra copy of the count immediately before the first
character, and returns that address instead. Be careful, however, to
redefine some of the system functions such as ". because these fetch
the full 32-bit length.

WORD will not work accross lines while loading a file.



" (Quotes)

" string" returns the address of 32-bit count of a counted string.



Dictionary Headers and ' (tick)

The dictionary structure reflects the fully 32-bit theme of this
Forth. All fields, including the length of the word, are 32 bits long.
A separate 32-bit flag field replaces the use of bits 6 and 7
of the length byte of typical Forths. The word IMMEDIATE works in
the usual way, however. It sets bit 1 (of 0-31) of the flag field.

The name field is always set to 32 bytes. With 4GB of virtual memory to
play with, code size was not a major concern.

The order of the dictionary fields is non-standard. The dictionary
header is structured like this:

Field Length
Name in bytes Contents
----- -------- --------------------------------------------------------
LFA 4 Link Field Address, link to previous definition
FFA 4 Flag Field Address, flags immediate definitions
CFA 4 Code Field Address, pointer to executable code
NFA 4 Name Field Address, length of the word followed by
SFA 32 String Field Address, address of first character
PFA ? Parameter Field Address, start of code and/or parameters

The string field is initialized to zeros.

' (tick) returns the address of the link field address of the word, the
start of the definition. To get to the other fields, use the following
words:

' ==> LFA of word, then use:

DECIMAL
: FFA 04 + ; ( converts LFA to FFA or Flag Field Address )
: CFA 08 + ; ( converts LFA to CFA or Code Field Address )
: NFA 12 + ; ( converts LFA to NFA or Name Field Address )
: SFA 16 + ; ( converts LFA to SFA or String Field Address )
: PFA 32 + ; ( converts LFA to PFA or Parameter Field Address )

: LFA 32 - ; ( converts PFA to LFA )

If you ' a constant or a word created with DOES> you will have to add 5
to the address to get the real parameters. The 5 skips the jump opcode
and the four byte offset.



TIB

The word TIB returns the address of a variable which holds the location
of the terminal input buffer. To make it compatible, redefine
: TIB TIB @ ;
(It seems to be more flexible as a pointer anyway.)



Dictionary Pointer

Currently the dictionary pointer cannot be externally accessed with a
variable address. Instead, use HERE to get the current dictionary
pointer and use DP! to store into the dictionary pointer (instead of
DP !).



CASE

An implementation of Chuck Eaker's CASE words is included in the
FORTH.INI file. There is one major difference. You must do a DROP or
equivalent before the ENDOF statement at the end. This was done because
you should not have to DUP the value which fell through in order to
handle it (as in 0 DAY in section 3.1). It does not make sense that
ENDCASE requires a parameter. It DOES make sense that you should always
deal with an exception, either by ignoring it with DROP or by using some
type of error handling.




7. WHERE TO SEND COMMENTS AND SUGGESTIONS

Michael Warot (ka9dgx) created Forth/2 and welcomes any comments,
or suggestions about improving this product. You may contact him
via email over the Internet at:

[email protected]

or via USMail at:

Michael A. Warot
PO BOX 4043
Hammond, Indiana 46324

Also contact Mike about licensing this product and obtaining the source
code for Forth/2.


Brian Mathewson has helped Mike develop this Forth and the documentation.
Please send any special requests, ideas, or suggestions to:

[email protected]

or via USMail at:

Brian Mathewson
21576 Kenwood Avenue
Rocky River, OH 44116-1232

<<< End of Forth/2 Documentation 23 March 1993 >>>


 December 30, 2017  Add comments

Leave a Reply