aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.linux52
-rw-r--r--ReadMe.txt49
-rw-r--r--dict.c836
-rw-r--r--doc/Logo.jpgbin0 -> 2349 bytes
-rw-r--r--doc/favicon.icobin0 -> 1078 bytes
-rw-r--r--doc/ficl.html1519
-rw-r--r--doc/ficl1.icobin0 -> 3310 bytes
-rw-r--r--doc/ficl_debug.html111
-rw-r--r--doc/ficl_guts.htm69
-rw-r--r--doc/ficl_loc.html161
-rw-r--r--doc/ficl_logo.jpgbin0 -> 2349 bytes
-rw-r--r--doc/ficl_oop.html1387
-rw-r--r--doc/ficl_oop.jpgbin0 -> 63155 bytes
-rw-r--r--doc/ficl_parse.html197
-rw-r--r--doc/ficl_rel.html804
-rw-r--r--doc/ficl_top.jpgbin0 -> 51512 bytes
-rw-r--r--doc/ficlddj.PDFbin0 -> 34088 bytes
-rw-r--r--doc/ficlheader.js19
-rw-r--r--doc/index.html116
-rw-r--r--doc/jwsforml.PDFbin0 -> 140379 bytes
-rw-r--r--doc/oo_in_c.html223
-rw-r--r--doc/primer.txt1164
-rw-r--r--doc/sigplan9906.docbin0 -> 75776 bytes
-rw-r--r--doc/skey.gifbin0 -> 4364 bytes
-rw-r--r--ficl.c691
-rw-r--r--ficl.dsp301
-rw-r--r--ficl.dsw29
-rw-r--r--ficl.h1117
-rw-r--r--fileaccess.c423
-rw-r--r--float.c1065
-rw-r--r--math64.c559
-rw-r--r--math64.h86
-rw-r--r--prefix.c197
-rw-r--r--search.c391
-rw-r--r--softcore.c1028
-rw-r--r--softwords/classes.fr172
-rw-r--r--softwords/ficlclass.fr84
-rw-r--r--softwords/ficllocal.fr46
-rw-r--r--softwords/fileaccess.fr23
-rw-r--r--softwords/forml.fr72
-rw-r--r--softwords/ifbrack.fr48
-rw-r--r--softwords/jhlocal.fr103
-rw-r--r--softwords/makefile9
-rw-r--r--softwords/marker.fr25
-rw-r--r--softwords/oo.fr693
-rw-r--r--softwords/oo.fr.bak678
-rw-r--r--softwords/prefix.fr57
-rw-r--r--softwords/softcore.bat1
-rw-r--r--softwords/softcore.fr207
-rwxr-xr-xsoftwords/softcore.pl144
-rw-r--r--softwords/softcore.py152
-rw-r--r--softwords/softcore.py.bat1
-rw-r--r--softwords/string.fr148
-rw-r--r--softwords/win32.fr10
-rw-r--r--stack.c367
-rw-r--r--sysdep.c409
-rw-r--r--sysdep.h465
-rw-r--r--test/asm68k.4th308
-rw-r--r--test/core.fr997
-rw-r--r--test/fib.fr12
-rw-r--r--test/ficltest.fr106
-rw-r--r--test/ooptest.fr73
-rw-r--r--test/prefix.fr8
-rw-r--r--test/sarray.fr17
-rw-r--r--test/testcase.fr83
-rw-r--r--test/tester.fr59
-rw-r--r--test/vocab.fr32
-rw-r--r--testmain.c367
-rw-r--r--tools.c893
-rw-r--r--unix.c21
-rw-r--r--vm.c799
-rw-r--r--win32.c406
-rw-r--r--words.c5201
73 files changed, 25890 insertions, 0 deletions
diff --git a/Makefile.linux b/Makefile.linux
new file mode 100644
index 000000000000..5dbca1012744
--- /dev/null
+++ b/Makefile.linux
@@ -0,0 +1,52 @@
+OBJECTS= dict.o ficl.o fileaccess.o float.o math64.o prefix.o search.o softcore.o stack.o sysdep.o tools.o unix.o vm.o words.o
+HEADERS= ficl.h math64.h sysdep.h
+#
+# Flags for shared library
+TARGET= -Dlinux # riscos MOTO_CPU32
+SHFLAGS = -fPIC
+CFLAGS= -O -c $(SHFLAGS) $(TARGET)
+CC=gcc
+LIB = ar cr
+RANLIB = ranlib
+
+MAJOR = 3
+MINOR = 0.1
+
+ficl: testmain.o ficl.h sysdep.h libficl.a
+ $(CC) testmain.o -o ficl -L. -lficl -lm
+
+lib: libficl.so.$(MAJOR).$(MINOR)
+
+# static library build
+libficl.a: $(OBJECTS)
+ $(LIB) libficl.a $(OBJECTS)
+ $(RANLIB) libficl.a
+
+# shared library build
+libficl.so.$(MAJOR).$(MINOR): $(OBJECTS)
+ $(CC) -shared -Wl,-soname,libficl.so.$(MAJOR).$(MINOR) \
+ -o libficl.so.$(MAJOR).$(MINOR) $(OBJECTS)
+ ln -sf libficl.so.$(MAJOR).$(MINOR) libficl.so
+
+testmain: testmain.o ficl.h sysdep.h libficl.so.$(MAJOR).$(MINOR)
+ $(CC) testmain.o -o testmain -L. -lficl -lm
+ ln -sf libficl.so.$(MAJOR).$(MINOR) libficl.so.$(MAJOR)
+
+#
+# generic object code
+#
+.SUFFIXES: .cxx .cc .c .o
+
+.c.o:
+ $(CC) $(CFLAGS) -c $*.c
+
+.cxx.o:
+ $(CPP) $(CPFLAGS) -c $*.cxx
+
+.cc.o:
+ $(CPP) $(CPFLAGS) -c $*.cc
+#
+# generic cleanup code
+#
+clean:
+ rm -f *.o *.a libficl.*
diff --git a/ReadMe.txt b/ReadMe.txt
new file mode 100644
index 000000000000..6985b807d248
--- /dev/null
+++ b/ReadMe.txt
@@ -0,0 +1,49 @@
+FICL 3.03
+April 2002
+
+________
+OVERVIEW
+
+Ficl is a complete programming language interpreter designed to be embedded
+into other systems (including firmware based ones) as a command, macro,
+and development prototype language. Ficl stands for "Forth Inspired
+Command Language".
+
+For more information, please see the "doc" directory.
+For release notes, please see "doc/ficl_rel.html".
+
+____________
+INSTALLATION
+
+Ficl builds out-of-the-box on the following platforms:
+ * Linux: use "Makefile.linux".
+ * RiscOS: use "Makefile.riscos".
+ * Win32: use "ficl.dsw" / "ficl.dsp".
+To port to other platforms, be sure to examine "sysdep.h", and
+we suggest you start with the Linux makefile. (And please--feel
+free to submit your portability changes!)
+
+____________
+FICL LICENSE
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
diff --git a/dict.c b/dict.c
new file mode 100644
index 000000000000..5f61c301555b
--- /dev/null
+++ b/dict.c
@@ -0,0 +1,836 @@
+/*******************************************************************
+** d i c t . c
+** Forth Inspired Command Language - dictionary methods
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: dict.c,v 1.12 2001-10-28 10:59:22-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** This file implements the dictionary -- FICL's model of
+** memory management. All FICL words are stored in the
+** dictionary. A word is a named chunk of data with its
+** associated code. FICL treats all words the same, even
+** precompiled ones, so your words become first-class
+** extensions of the language. You can even define new
+** control structures.
+**
+** 29 jun 1998 (sadler) added variable sized hash table support
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <stdio.h> /* sprintf */
+#include <string.h>
+#include <ctype.h>
+#include "ficl.h"
+
+static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si);
+
+/**************************************************************************
+ d i c t A b o r t D e f i n i t i o n
+** Abort a definition in process: reclaim its memory and unlink it
+** from the dictionary list. Assumes that there is a smudged
+** definition in process...otherwise does nothing.
+** NOTE: this function is not smart enough to unlink a word that
+** has been successfully defined (ie linked into a hash). It
+** only works for defs in process. If the def has been unsmudged,
+** nothing happens.
+**************************************************************************/
+void dictAbortDefinition(FICL_DICT *pDict)
+{
+ FICL_WORD *pFW;
+ ficlLockDictionary(TRUE);
+ pFW = pDict->smudge;
+
+ if (pFW->flags & FW_SMUDGE)
+ pDict->here = (CELL *)pFW->name;
+
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ a l i g n P t r
+** Aligns the given pointer to FICL_ALIGN address units.
+** Returns the aligned pointer value.
+**************************************************************************/
+void *alignPtr(void *ptr)
+{
+#if FICL_ALIGN > 0
+ char *cp;
+ CELL c;
+ cp = (char *)ptr + FICL_ALIGN_ADD;
+ c.p = (void *)cp;
+ c.u = c.u & (~FICL_ALIGN_ADD);
+ ptr = (CELL *)c.p;
+#endif
+ return ptr;
+}
+
+
+/**************************************************************************
+ d i c t A l i g n
+** Align the dictionary's free space pointer
+**************************************************************************/
+void dictAlign(FICL_DICT *pDict)
+{
+ pDict->here = alignPtr(pDict->here);
+}
+
+
+/**************************************************************************
+ d i c t A l l o t
+** Allocate or remove n chars of dictionary space, with
+** checks for underrun and overrun
+**************************************************************************/
+int dictAllot(FICL_DICT *pDict, int n)
+{
+ char *cp = (char *)pDict->here;
+#if FICL_ROBUST
+ if (n > 0)
+ {
+ if ((unsigned)n <= dictCellsAvail(pDict) * sizeof (CELL))
+ cp += n;
+ else
+ return 1; /* dict is full */
+ }
+ else
+ {
+ n = -n;
+ if ((unsigned)n <= dictCellsUsed(pDict) * sizeof (CELL))
+ cp -= n;
+ else /* prevent underflow */
+ cp -= dictCellsUsed(pDict) * sizeof (CELL);
+ }
+#else
+ cp += n;
+#endif
+ pDict->here = PTRtoCELL cp;
+ return 0;
+}
+
+
+/**************************************************************************
+ d i c t A l l o t C e l l s
+** Reserve space for the requested number of cells in the
+** dictionary. If nCells < 0 , removes space from the dictionary.
+**************************************************************************/
+int dictAllotCells(FICL_DICT *pDict, int nCells)
+{
+#if FICL_ROBUST
+ if (nCells > 0)
+ {
+ if (nCells <= dictCellsAvail(pDict))
+ pDict->here += nCells;
+ else
+ return 1; /* dict is full */
+ }
+ else
+ {
+ nCells = -nCells;
+ if (nCells <= dictCellsUsed(pDict))
+ pDict->here -= nCells;
+ else /* prevent underflow */
+ pDict->here -= dictCellsUsed(pDict);
+ }
+#else
+ pDict->here += nCells;
+#endif
+ return 0;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d C e l l
+** Append the specified cell to the dictionary
+**************************************************************************/
+void dictAppendCell(FICL_DICT *pDict, CELL c)
+{
+ *pDict->here++ = c;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d C h a r
+** Append the specified char to the dictionary
+**************************************************************************/
+void dictAppendChar(FICL_DICT *pDict, char c)
+{
+ char *cp = (char *)pDict->here;
+ *cp++ = c;
+ pDict->here = PTRtoCELL cp;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d W o r d
+** Create a new word in the dictionary with the specified
+** name, code, and flags. Name must be NULL-terminated.
+**************************************************************************/
+FICL_WORD *dictAppendWord(FICL_DICT *pDict,
+ char *name,
+ FICL_CODE pCode,
+ UNS8 flags)
+{
+ STRINGINFO si;
+ SI_SETLEN(si, strlen(name));
+ SI_SETPTR(si, name);
+ return dictAppendWord2(pDict, si, pCode, flags);
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d W o r d 2
+** Create a new word in the dictionary with the specified
+** STRINGINFO, code, and flags. Does not require a NULL-terminated
+** name.
+**************************************************************************/
+FICL_WORD *dictAppendWord2(FICL_DICT *pDict,
+ STRINGINFO si,
+ FICL_CODE pCode,
+ UNS8 flags)
+{
+ FICL_COUNT len = (FICL_COUNT)SI_COUNT(si);
+ char *pName;
+ FICL_WORD *pFW;
+
+ ficlLockDictionary(TRUE);
+
+ /*
+ ** NOTE: dictCopyName advances "here" as a side-effect.
+ ** It must execute before pFW is initialized.
+ */
+ pName = dictCopyName(pDict, si);
+ pFW = (FICL_WORD *)pDict->here;
+ pDict->smudge = pFW;
+ pFW->hash = hashHashCode(si);
+ pFW->code = pCode;
+ pFW->flags = (UNS8)(flags | FW_SMUDGE);
+ pFW->nName = (char)len;
+ pFW->name = pName;
+ /*
+ ** Point "here" to first cell of new word's param area...
+ */
+ pDict->here = pFW->param;
+
+ if (!(flags & FW_SMUDGE))
+ dictUnsmudge(pDict);
+
+ ficlLockDictionary(FALSE);
+ return pFW;
+}
+
+
+/**************************************************************************
+ d i c t A p p e n d U N S
+** Append the specified FICL_UNS to the dictionary
+**************************************************************************/
+void dictAppendUNS(FICL_DICT *pDict, FICL_UNS u)
+{
+ *pDict->here++ = LVALUEtoCELL(u);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t C e l l s A v a i l
+** Returns the number of empty cells left in the dictionary
+**************************************************************************/
+int dictCellsAvail(FICL_DICT *pDict)
+{
+ return pDict->size - dictCellsUsed(pDict);
+}
+
+
+/**************************************************************************
+ d i c t C e l l s U s e d
+** Returns the number of cells consumed in the dicionary
+**************************************************************************/
+int dictCellsUsed(FICL_DICT *pDict)
+{
+ return pDict->here - pDict->dict;
+}
+
+
+/**************************************************************************
+ d i c t C h e c k
+** Checks the dictionary for corruption and throws appropriate
+** errors.
+** Input: +n number of ADDRESS UNITS (not Cells) proposed to allot
+** -n number of ADDRESS UNITS proposed to de-allot
+** 0 just do a consistency check
+**************************************************************************/
+void dictCheck(FICL_DICT *pDict, FICL_VM *pVM, int n)
+{
+ if ((n >= 0) && (dictCellsAvail(pDict) * (int)sizeof(CELL) < n))
+ {
+ vmThrowErr(pVM, "Error: dictionary full");
+ }
+
+ if ((n <= 0) && (dictCellsUsed(pDict) * (int)sizeof(CELL) < -n))
+ {
+ vmThrowErr(pVM, "Error: dictionary underflow");
+ }
+
+ if (pDict->nLists > FICL_DEFAULT_VOCS)
+ {
+ dictResetSearchOrder(pDict);
+ vmThrowErr(pVM, "Error: search order overflow");
+ }
+ else if (pDict->nLists < 0)
+ {
+ dictResetSearchOrder(pDict);
+ vmThrowErr(pVM, "Error: search order underflow");
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ d i c t C o p y N a m e
+** Copy up to nFICLNAME characters of the name specified by si into
+** the dictionary starting at "here", then NULL-terminate the name,
+** point "here" to the next available byte, and return the address of
+** the beginning of the name. Used by dictAppendWord.
+** N O T E S :
+** 1. "here" is guaranteed to be aligned after this operation.
+** 2. If the string has zero length, align and return "here"
+**************************************************************************/
+static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si)
+{
+ char *oldCP = (char *)pDict->here;
+ char *cp = oldCP;
+ char *name = SI_PTR(si);
+ int i = SI_COUNT(si);
+
+ if (i == 0)
+ {
+ dictAlign(pDict);
+ return (char *)pDict->here;
+ }
+
+ if (i > nFICLNAME)
+ i = nFICLNAME;
+
+ for (; i > 0; --i)
+ {
+ *cp++ = *name++;
+ }
+
+ *cp++ = '\0';
+
+ pDict->here = PTRtoCELL cp;
+ dictAlign(pDict);
+ return oldCP;
+}
+
+
+/**************************************************************************
+ d i c t C r e a t e
+** Create and initialize a dictionary with the specified number
+** of cells capacity, and no hashing (hash size == 1).
+**************************************************************************/
+FICL_DICT *dictCreate(unsigned nCells)
+{
+ return dictCreateHashed(nCells, 1);
+}
+
+
+FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash)
+{
+ FICL_DICT *pDict;
+ size_t nAlloc;
+
+ nAlloc = sizeof (FICL_DICT) + nCells * sizeof (CELL)
+ + sizeof (FICL_HASH) + (nHash - 1) * sizeof (FICL_WORD *);
+
+ pDict = ficlMalloc(nAlloc);
+ assert(pDict);
+
+ pDict->size = nCells;
+ dictEmpty(pDict, nHash);
+ return pDict;
+}
+
+
+/**************************************************************************
+ d i c t C r e a t e W o r d l i s t
+** Create and initialize an anonymous wordlist
+**************************************************************************/
+FICL_HASH *dictCreateWordlist(FICL_DICT *dp, int nBuckets)
+{
+ FICL_HASH *pHash;
+
+ dictAlign(dp);
+ pHash = (FICL_HASH *)dp->here;
+ dictAllot(dp, sizeof (FICL_HASH)
+ + (nBuckets-1) * sizeof (FICL_WORD *));
+
+ pHash->size = nBuckets;
+ hashReset(pHash);
+ return pHash;
+}
+
+
+/**************************************************************************
+ d i c t D e l e t e
+** Free all memory allocated for the given dictionary
+**************************************************************************/
+void dictDelete(FICL_DICT *pDict)
+{
+ assert(pDict);
+ ficlFree(pDict);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t E m p t y
+** Empty the dictionary, reset its hash table, and reset its search order.
+** Clears and (re-)creates the hash table with the size specified by nHash.
+**************************************************************************/
+void dictEmpty(FICL_DICT *pDict, unsigned nHash)
+{
+ FICL_HASH *pHash;
+
+ pDict->here = pDict->dict;
+
+ dictAlign(pDict);
+ pHash = (FICL_HASH *)pDict->here;
+ dictAllot(pDict,
+ sizeof (FICL_HASH) + (nHash - 1) * sizeof (FICL_WORD *));
+
+ pHash->size = nHash;
+ hashReset(pHash);
+
+ pDict->pForthWords = pHash;
+ pDict->smudge = NULL;
+ dictResetSearchOrder(pDict);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t H a s h S u m m a r y
+** Calculate a figure of merit for the dictionary hash table based
+** on the average search depth for all the words in the dictionary,
+** assuming uniform distribution of target keys. The figure of merit
+** is the ratio of the total search depth for all keys in the table
+** versus a theoretical optimum that would be achieved if the keys
+** were distributed into the table as evenly as possible.
+** The figure would be worse if the hash table used an open
+** addressing scheme (i.e. collisions resolved by searching the
+** table for an empty slot) for a given size table.
+**************************************************************************/
+#if FICL_WANT_FLOAT
+void dictHashSummary(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_HASH *pFHash;
+ FICL_WORD **pHash;
+ unsigned size;
+ FICL_WORD *pFW;
+ unsigned i;
+ int nMax = 0;
+ int nWords = 0;
+ int nFilled;
+ double avg = 0.0;
+ double best;
+ int nAvg, nRem, nDepth;
+
+ dictCheck(dp, pVM, 0);
+
+ pFHash = dp->pSearch[dp->nLists - 1];
+ pHash = pFHash->table;
+ size = pFHash->size;
+ nFilled = size;
+
+ for (i = 0; i < size; i++)
+ {
+ int n = 0;
+ pFW = pHash[i];
+
+ while (pFW)
+ {
+ ++n;
+ ++nWords;
+ pFW = pFW->link;
+ }
+
+ avg += (double)(n * (n+1)) / 2.0;
+
+ if (n > nMax)
+ nMax = n;
+ if (n == 0)
+ --nFilled;
+ }
+
+ /* Calc actual avg search depth for this hash */
+ avg = avg / nWords;
+
+ /* Calc best possible performance with this size hash */
+ nAvg = nWords / size;
+ nRem = nWords % size;
+ nDepth = size * (nAvg * (nAvg+1))/2 + (nAvg+1)*nRem;
+ best = (double)nDepth/nWords;
+
+ sprintf(pVM->pad,
+ "%d bins, %2.0f%% filled, Depth: Max=%d, Avg=%2.1f, Best=%2.1f, Score: %2.0f%%",
+ size,
+ (double)nFilled * 100.0 / size, nMax,
+ avg,
+ best,
+ 100.0 * best / avg);
+
+ ficlTextOut(pVM, pVM->pad, 1);
+
+ return;
+}
+#endif
+
+/**************************************************************************
+ d i c t I n c l u d e s
+** Returns TRUE iff the given pointer is within the address range of
+** the dictionary.
+**************************************************************************/
+int dictIncludes(FICL_DICT *pDict, void *p)
+{
+ return ((p >= (void *) &pDict->dict)
+ && (p < (void *)(&pDict->dict + pDict->size))
+ );
+}
+
+
+/**************************************************************************
+ d i c t L o o k u p
+** Find the FICL_WORD that matches the given name and length.
+** If found, returns the word's address. Otherwise returns NULL.
+** Uses the search order list to search multiple wordlists.
+**************************************************************************/
+FICL_WORD *dictLookup(FICL_DICT *pDict, STRINGINFO si)
+{
+ FICL_WORD *pFW = NULL;
+ FICL_HASH *pHash;
+ int i;
+ UNS16 hashCode = hashHashCode(si);
+
+ assert(pDict);
+
+ ficlLockDictionary(1);
+
+ for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i)
+ {
+ pHash = pDict->pSearch[i];
+ pFW = hashLookup(pHash, si, hashCode);
+ }
+
+ ficlLockDictionary(0);
+ return pFW;
+}
+
+
+/**************************************************************************
+ f i c l L o o k u p L o c
+** Same as dictLookup, but looks in system locals dictionary first...
+** Assumes locals dictionary has only one wordlist...
+**************************************************************************/
+#if FICL_WANT_LOCALS
+FICL_WORD *ficlLookupLoc(FICL_SYSTEM *pSys, STRINGINFO si)
+{
+ FICL_WORD *pFW = NULL;
+ FICL_DICT *pDict = pSys->dp;
+ FICL_HASH *pHash = ficlGetLoc(pSys)->pForthWords;
+ int i;
+ UNS16 hashCode = hashHashCode(si);
+
+ assert(pHash);
+ assert(pDict);
+
+ ficlLockDictionary(1);
+ /*
+ ** check the locals dict first...
+ */
+ pFW = hashLookup(pHash, si, hashCode);
+
+ /*
+ ** If no joy, (!pFW) --------------------------v
+ ** iterate over the search list in the main dict
+ */
+ for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i)
+ {
+ pHash = pDict->pSearch[i];
+ pFW = hashLookup(pHash, si, hashCode);
+ }
+
+ ficlLockDictionary(0);
+ return pFW;
+}
+#endif
+
+
+/**************************************************************************
+ d i c t R e s e t S e a r c h O r d e r
+** Initialize the dictionary search order list to sane state
+**************************************************************************/
+void dictResetSearchOrder(FICL_DICT *pDict)
+{
+ assert(pDict);
+ pDict->pCompile = pDict->pForthWords;
+ pDict->nLists = 1;
+ pDict->pSearch[0] = pDict->pForthWords;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t S e t F l a g s
+** Changes the flags field of the most recently defined word:
+** Set all bits that are ones in the set parameter, clear all bits
+** that are ones in the clr parameter. Clear wins in case the same bit
+** is set in both parameters.
+**************************************************************************/
+void dictSetFlags(FICL_DICT *pDict, UNS8 set, UNS8 clr)
+{
+ assert(pDict->smudge);
+ pDict->smudge->flags |= set;
+ pDict->smudge->flags &= ~clr;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t S e t I m m e d i a t e
+** Set the most recently defined word as IMMEDIATE
+**************************************************************************/
+void dictSetImmediate(FICL_DICT *pDict)
+{
+ assert(pDict->smudge);
+ pDict->smudge->flags |= FW_IMMEDIATE;
+ return;
+}
+
+
+/**************************************************************************
+ d i c t U n s m u d g e
+** Completes the definition of a word by linking it
+** into the main list
+**************************************************************************/
+void dictUnsmudge(FICL_DICT *pDict)
+{
+ FICL_WORD *pFW = pDict->smudge;
+ FICL_HASH *pHash = pDict->pCompile;
+
+ assert(pHash);
+ assert(pFW);
+ /*
+ ** :noname words never get linked into the list...
+ */
+ if (pFW->nName > 0)
+ hashInsertWord(pHash, pFW);
+ pFW->flags &= ~(FW_SMUDGE);
+ return;
+}
+
+
+/**************************************************************************
+ d i c t W h e r e
+** Returns the value of the HERE pointer -- the address
+** of the next free cell in the dictionary
+**************************************************************************/
+CELL *dictWhere(FICL_DICT *pDict)
+{
+ return pDict->here;
+}
+
+
+/**************************************************************************
+ h a s h F o r g e t
+** Unlink all words in the hash that have addresses greater than or
+** equal to the address supplied. Implementation factor for FORGET
+** and MARKER.
+**************************************************************************/
+void hashForget(FICL_HASH *pHash, void *where)
+{
+ FICL_WORD *pWord;
+ unsigned i;
+
+ assert(pHash);
+ assert(where);
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ pWord = pHash->table[i];
+
+ while ((void *)pWord >= where)
+ {
+ pWord = pWord->link;
+ }
+
+ pHash->table[i] = pWord;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ h a s h H a s h C o d e
+**
+** Generate a 16 bit hashcode from a character string using a rolling
+** shift and add stolen from PJ Weinberger of Bell Labs fame. Case folds
+** the name before hashing it...
+** N O T E : If string has zero length, returns zero.
+**************************************************************************/
+UNS16 hashHashCode(STRINGINFO si)
+{
+ /* hashPJW */
+ UNS8 *cp;
+ UNS16 code = (UNS16)si.count;
+ UNS16 shift = 0;
+
+ if (si.count == 0)
+ return 0;
+
+ /* changed to run without errors under Purify -- lch */
+ for (cp = (UNS8 *)si.cp; si.count && *cp; cp++, si.count--)
+ {
+ code = (UNS16)((code << 4) + tolower(*cp));
+ shift = (UNS16)(code & 0xf000);
+ if (shift)
+ {
+ code ^= (UNS16)(shift >> 8);
+ code ^= (UNS16)shift;
+ }
+ }
+
+ return (UNS16)code;
+}
+
+
+
+
+/**************************************************************************
+ h a s h I n s e r t W o r d
+** Put a word into the hash table using the word's hashcode as
+** an index (modulo the table size).
+**************************************************************************/
+void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW)
+{
+ FICL_WORD **pList;
+
+ assert(pHash);
+ assert(pFW);
+
+ if (pHash->size == 1)
+ {
+ pList = pHash->table;
+ }
+ else
+ {
+ pList = pHash->table + (pFW->hash % pHash->size);
+ }
+
+ pFW->link = *pList;
+ *pList = pFW;
+ return;
+}
+
+
+/**************************************************************************
+ h a s h L o o k u p
+** Find a name in the hash table given the hashcode and text of the name.
+** Returns the address of the corresponding FICL_WORD if found,
+** otherwise NULL.
+** Note: outer loop on link field supports inheritance in wordlists.
+** It's not part of ANS Forth - ficl only. hashReset creates wordlists
+** with NULL link fields.
+**************************************************************************/
+FICL_WORD *hashLookup(FICL_HASH *pHash, STRINGINFO si, UNS16 hashCode)
+{
+ FICL_UNS nCmp = si.count;
+ FICL_WORD *pFW;
+ UNS16 hashIdx;
+
+ if (nCmp > nFICLNAME)
+ nCmp = nFICLNAME;
+
+ for (; pHash != NULL; pHash = pHash->link)
+ {
+ if (pHash->size > 1)
+ hashIdx = (UNS16)(hashCode % pHash->size);
+ else /* avoid the modulo op for single threaded lists */
+ hashIdx = 0;
+
+ for (pFW = pHash->table[hashIdx]; pFW; pFW = pFW->link)
+ {
+ if ( (pFW->nName == si.count)
+ && (!strincmp(si.cp, pFW->name, nCmp)) )
+ return pFW;
+#if FICL_ROBUST
+ assert(pFW != pFW->link);
+#endif
+ }
+ }
+
+ return NULL;
+}
+
+
+/**************************************************************************
+ h a s h R e s e t
+** Initialize a FICL_HASH to empty state.
+**************************************************************************/
+void hashReset(FICL_HASH *pHash)
+{
+ unsigned i;
+
+ assert(pHash);
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ pHash->table[i] = NULL;
+ }
+
+ pHash->link = NULL;
+ pHash->name = NULL;
+ return;
+}
+
+
diff --git a/doc/Logo.jpg b/doc/Logo.jpg
new file mode 100644
index 000000000000..c3332dbf65a1
--- /dev/null
+++ b/doc/Logo.jpg
Binary files differ
diff --git a/doc/favicon.ico b/doc/favicon.ico
new file mode 100644
index 000000000000..57b1c723f583
--- /dev/null
+++ b/doc/favicon.ico
Binary files differ
diff --git a/doc/ficl.html b/doc/ficl.html
new file mode 100644
index 000000000000..a3096a4e5d33
--- /dev/null
+++ b/doc/ficl.html
@@ -0,0 +1,1519 @@
+<!DOCTYPE html PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<HTML>
+ <HEAD>
+ <META name="Author" content="john sadler">
+ <META name="Description" content="Ficl - embedded scripting with object oriented programming">
+ <META name="Keywords" content="scripting prototyping tcl OOP Forth interpreter C">
+<STYLE>
+
+</STYLE>
+ <LINK rel="SHORTCUT ICON" href="ficl.ico">
+ <TITLE>
+ Ficl - Embedded Scripting
+ </TITLE>
+ </HEAD>
+ <BODY>
+ <H1>
+ <B>Ficl Documentation</B>
+ </H1>
+<SCRIPT language="javascript" src="ficlheader.js" type="text/javascript">
+</SCRIPT>
+ <DIV style="width:675px">
+ <BR>
+
+ <H1>
+ <A name="whatis"></A>What is ficl?
+ </H1>
+ Ficl is a complete programming language interpreter designed to be embedded into other systems (including firmware based ones) as a command, macro, and development prototype language. Unlike other scripting interpreters,
+ Ficl:
+ <UL>
+ <LI>
+ typically takes under 2 hours to port to a new system -- much less if the target operating system is one of several already supported (Win32, Linux, FreeBSD, RiscOS, and more)
+ </LI>
+ <LI>
+ has a small memory footprint: a fully featured Win32 console version takes less than 100K of memory, and a minimal version is less than half that
+ </LI>
+ <LI>
+ is relatively quick thanks to its threaded code virtual machine design and just in time compiling
+ </LI>
+ <LI>
+ is a complete and powerful programming language
+ </LI>
+ <LI>
+ is interactive
+ </LI>
+ <LI>
+ has object oriented programming features that can be used to wrap data structures or classes of the host system without altering them - even if the host is mainly written in a non-OO
+ language
+ </LI>
+ </UL>
+ <P>
+ Ficl syntax is based on ANS Forth and the code is Standard C. See below for examples of <A href="#includesficl">software and products that include ficl</A>. Ficl stands for "Forth inspired
+ command language".&nbsp;
+ </P>
+ <H3>
+ Ficl vs. other Forth interpreters
+ </H3>
+ Where Forths usually view themselves as the center of the system and expect the rest of the system to be coded in Forth, Ficl acts as a component of the system. It is easy to export code
+ written in C or ASM to Ficl in the style of TCL, or to invoke Ficl code from a compiled module. This allows you to do incremental development in a way that combines the best features of
+ threaded languages (rapid development, quick code/test/debug cycle, reasonably fast) with the best features of C (everyone knows it, easier to support large blocks of code, efficient, type
+ checking). In addition, Ficl provides a simple and powerful object model that can act as an object oriented <I>adapter</I> for code written in C (or asm, Forth, C++...).&nbsp;
+ <H3>
+ Ficl Design goals
+ </H3>
+ <UL>
+ <LI>
+ Target 32 bit processors (<I>version 2.03 targets 64 bit processors too</I>)
+ </LI>
+ <LI>
+ Scripting, prototyping, and extension language for systems written also in C
+ </LI>
+ <LI>
+ Supportable - code is as transparent as I can make it
+ </LI>
+ <LI>
+ Interface to functions written in C
+ </LI>
+ <LI>
+ Conform to the Forth DPANS 94
+ </LI>
+ <LI>
+ Minimize porting effort - require an ANSI C runtime environment and minimal glue code
+ </LI>
+ <LI>
+ Provide object oriented extensions
+ </LI>
+ </UL>
+ <HR>
+ <H2>
+ <A name="download"></A>Download
+ </H2>
+ <UL>
+ <LI>
+ <B><A href="http://sourceforge.net/project/showfiles.php?group_id=24441">Download ficl (latest release)</A></B>
+ </LI>
+ </UL>
+ <H2>
+ <A name="links"></A>More information on Ficl and Forth
+ </H2>
+ <UL>
+ <LI>
+ <A href="http://ficl.sourceforge.net">Web home of Ficl</A>
+ </LI>
+ <LI>
+ <A href="http://ficl.sourceforge.net/pdf/Forth_Primer.pdf">An excellent Forth Primer by Hans Bezemer</A>
+ </LI>
+ <LI>
+ <A href="ficlddj.pdf">Manuscript of Ficl article for January 1999 Dr. Dobb's Journal</A>
+ </LI>
+ <LI>
+ <A href="jwsforml.pdf">1998 FORML Conference paper - OO Programming in Ficl</A>
+ </LI>
+ <LI>
+ <A href="http://www.taygeta.com/forth_intro/stackflo.html">An Introduction to Forth using Stack Flow</A> (start here if you're new to Forth)
+ </LI>
+ <LI>
+ <A href="http://www.softsynth.com/pforth/pf_tut.htm">Phil Burk's Forth Tutorial</A>
+ </LI>
+ <LI>
+ <A href="http://www.complang.tuwien.ac.at/forth/threaded-code.html">Anton Ertl's description of Threaded Code</A>
+ </LI>
+ <LI>
+ <A href="http://ficl.sourceforge.net/dpans/dpans.htm">Draft Proposed American National Standard for Forth</A> (quite readable, actually)
+ </LI>
+ <LI>
+ <A href="http://www.taygeta.com/forthlit.html">Forth literature index on Taygeta</A>
+ </LI>
+ <LI>
+ <A href="http://www.forth.org">Forth Interest Group</A>
+ </LI>
+ </UL>
+ <H2>
+ <A name="includesficl"></A>Some software that uses ficl
+ </H2>
+ <UL>
+ <LI>
+ <A href="http://www.freebsd.org/">FreeBSD</A> boot loader (Daniel Sobral, Jordan Hubbard)
+ </LI>
+ <LI>
+ <A href="http://www.chipcenter.com/networking/images/prod/prod158a.pdf">SwitchCore</A> Gigabit Ethernet switches (&Ouml;rjan Gustavsson )
+ </LI>
+ <LI>
+ <A href="http://debuffer.sourceforge.net/">Palm Pilot Debuffer</A> (Eric Sessoms) Also see ficlx, a C++ interface to ficl, on the same site
+ </LI>
+ <LI>
+ <A href="http://www.swcp.com/~jchavez/osmond.html">Osmond PC Board Layout tool</A>
+ </LI>
+ <LI>
+ <A href="http://www.netcomsystems.com">NetCom Systems</A> ML7710
+ </LI>
+ <LI>
+ <A href="http://www.parview.com/ds/homepage.html">ParView</A> GPS system
+ </LI>
+ <LI>
+ <A href="http://www.thekompany.com/products/powerplant/software/Languages/Embedded.php3">PowerPlant Software</A> Development Environment for Linux
+ </LI>
+ <LI>
+ <A href="http://www.vyyo.com/products/architecture_v3000.html">Vyyo V3000 Broadband Wireless Hub</A>
+ </LI>
+ <LI>
+ <A href="mailto:john_sadler@alum.mit.edu"><I>Your Product Name Here!!!</I></A>
+ </LI>
+ </UL>
+ <HR>
+ <H2>
+ <A name="lawyerbait"></A>LICENSE and DISCLAIMER
+ </H2>
+ <P>
+ Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu) All rights reserved.
+ </P>
+ <P>
+ I am interested in hearing from anyone who uses ficl. If you have a problem, a success story, a defect, an enhancement request, or if you would like to contribute to the ficl release, please
+ <A href="mailto:john_sadler@alum.mit.edu">send me email</A>.&nbsp;
+ </P>
+<PRE>
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+</PRE>
+ &nbsp;
+ <H2>
+ <A name="features"></A>Ficl features
+ </H2>
+ <UL>
+ <LI>
+ Simple to integrate into existing systems: the sample implementation requires three Ficl function calls (see the example program in testmain.c).
+ </LI>
+ <LI>
+ Written in ANSI C for portability.&nbsp;
+ </LI>
+ <LI>
+ Standard: Implements the ANS Forth CORE word set, part of the CORE EXT word set, SEARCH and SEARCH EXT, TOOLS and part of TOOLS EXT, LOCAL and LOCAL EXT, EXCEPTION, MEMORY,&nbsp; and
+ various extras.
+ </LI>
+ <LI>
+ Extensible: you can export code written in Forth, C, or asm in a straightforward way. Ficl provides open facilities for extending the language in an application specific way. You can even
+ add new control structures (not surprising if you're familiar with Forth)
+ </LI>
+ <LI>
+ Ficl and C/C++ can interact in two ways: Ficl can wrap C code, and C functions can invoke ficl code.
+ </LI>
+ <LI>
+ Ficl code is thread safe and re-entrant:&nbsp; All Ficl Virtual Machines share one system dictionary (version 3.0 will permit multiple dictionaries); each Ficl Virtual Machine has an
+ otherwise complete state, and each can be bound to a separate I/O channel (or none at all). An optional function called ficlLockDictionary() can control exclusive dictionary access. This
+ function is stubbed out by default (See FICL_MULTITHREAD in sysdep.h). As long as there is only one "session" that can compile words into the dictionary, you do not need exclusive
+ dictionary access for multithreading. <B>Note</B>: while the code is re-entrant, there are still restrictions on how you can use it safely in a multithreaded system. Specifically, the VM
+ itself maintains state, so you generally need a VM per thread in a multithreaded system. If interrupt service routines make calls into Ficl code that alters VM state, then these generally
+ need their own VM as well. Alternatively, you could provide a mutual exclusion mechanism to serialize access to a VM from multiple threads.
+ </LI>
+ <LI>
+ ROMable: Ficl is designed to work in RAM based and ROM code / RAM data environments. It does require somewhat more memory than a pure ROM implementation because it builds its system
+ dictionary in RAM at startup time.
+ </LI>
+ <LI>
+ Written in ANSI C to be as simple as I can make it to understand, support, debug, and port. Compiles without complaint at /Az /W4 (require ANSI C, max. warnings) under Microsoft VC++. Ports
+ to several other toolchains and operating systems (notably FreeBSD and Linux flavors) exist.
+ </LI>
+ <LI>
+ Does full 32 bit math (but you need to implement two mixed precision math primitives (see sysdep.c))
+ </LI>
+ </UL>
+ <HR>
+ <H2>
+ <A name="porting"></A>Porting ficl
+ </H2>
+ To install ficl on your target system, you need an ANSI C compiler and its runtime library. Inspect the system dependent macros and functions in <TT>sysdep.h</TT> and <TT>sysdep.c</TT> and edit
+ them to suit your system. For example, <TT>INT16</TT> is a <TT>short</TT> on some compilers and an <TT>int</TT> on others. Check the default <TT>CELL</TT> alignment controlled by <TT>
+ FICL_ALIGN</TT>. If necessary, add new definitions of <TT>ficlMalloc, ficlFree, ficlRealloc</TT>, and <TT>ficlTextOut</TT> to work with your operating system. Finally, use <TT>testmain.c</TT>
+ as a guide to installing the ficl system and one or more virtual machines into your code. You do not need to include <TT>testmain.c</TT> in your build.&nbsp;
+ <P>
+ Note: ficlLockDictionary can be left unimplemented in most multithreaded implementations - it's only necessary if you expect to have more than one thread modifying the dictionary at the same
+ time. If you do decide to implement it, make sure calls to ficlLockDictionary can nest properly (see the comments in sysdep.h). You need to keep count of nested locks and unlocks and do the
+ right thing.
+ </P>
+ <P>
+ Feel free to stub out the double precision math functions (which are presently implemented as inline assembly because it's so easy on many 32 bit processors) with kludge code that only goes
+ to 32 bit precision. In most applications, you won't notice the difference. If you're doing a lot of number crunching, consider implementing them correctly.&nbsp;
+ </P>
+ <H3>
+ Build controls
+ </H3>
+ The file sysdep.h contains default values for build controls. Most of these are written such that if you define them on the compiler command line, the defaults are overridden. I suggest you
+ take the defaults on everything below the "build controls" section until you're confident of your port. Beware of declaring too small a dictionary, for example. You need about 3200 cells for a
+ full system, about 2000 if you strip out most of the "soft" words.&nbsp;
+ <H3>
+ Soft Words
+ </H3>
+ Many words from all the supported wordsets are written in Forth, and stored as a big string that Ficl compiles when it starts. The sources for all of these words are in directory
+ ficl/softwords. There is a .bat file (softcore.bat) and a PERL 5 script (softcore.pl) that convert Forth files into the file softcore.c, so softcore.c is really dependent on the Forth sources.
+ This is not reflected in the Visual C++ project database. For the time being, it's a manual step. You can edit softcore.bat to change the list of files that contribute to softcore.c.&nbsp;
+ <H3>
+ To-Do List (target system dependent words)
+ </H3>
+ <UL>
+ <LI>
+ Unimplemented system dependent <TT>CORE</TT> word: <TT>KEY</TT> (implement this yourself if you need it)
+ </LI>
+ <LI>
+ Kludged <TT>CORE</TT> word: <TT>ACCEPT</TT> (implement this better if you need to)
+ </LI>
+ </UL>
+ <BR>
+ &nbsp;<BR>
+ &nbsp;
+ <H2>
+ <A name="api"></A>Application Programming Interface
+ </H2>
+ The following is a partial listing of functions that interface your system or program to ficl. For a complete listing, see ficl.h (heavily commented). For examples, see testmain.c and the
+ ficlwin sources (<A href="#download">below</A>). <I>See the comments in ficl.c and ficl.h for additional information, and the example in file testmain.c.</I>
+ <DL>
+ <DT>
+ <B>FICL_SYSTEM *ficlInitSystem(int nDictCells)</B>
+ </DT>
+ <DD>
+ Initializes Ficl's shared system data structures, and creates the dictionary allocating the specified number of CELLs from the heap (by a call to ficlMalloc)
+ </DD>
+ <DT>
+ <B>void ficlTermSystem(FICL_SYSTEM *pSys)</B>
+ </DT>
+ <DD>
+ Reclaims memory allocated for the ficl system including all dictionaries and all virtual machines created by vmCreate. Any uses of the memory allocation words (allocate and resize) are your
+ problem.
+ </DD>
+ <DT>
+ <B>int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags)</B>
+ </DT>
+ <DD>
+ Create a primitive word in ficl's main dictionary with the given name, code pointer, and properties (immediate, compile only, etc) as described by the flags (see ficl.h for flag
+ descriptions of the form FW_XXXX)
+ </DD>
+ <DT>
+ <B>int ficlExec(FICL_VM *pVM, char *text)</B>
+ </DT>
+ <DD>
+ Feed the specified C string ('\0' terminated) to the given virtual machine for evaluation. Returns various exception codes (VM_XXXX in ficl.h) to indicate the reason for returning. Normal
+ exit condition is VM_OUTOFTEXT, indicating that the VM consumed the string successfully and is back for more. ficlExec calls can be nested, and the function itself is re-entrant, but note
+ that a VM is static, so you have to take reasonable precautions (for example, use one VM per thread in a multithreaded system if you want multiple threads to be able to execute commands).
+ </DD>
+ <DT>
+ <B>int ficlExecC(FICL_VM *pVM, char *text, int nChars)</B>
+ </DT>
+ <DD>
+ Same as ficlExec, but takes a count indicating the length of the supplied string. Setting nChars to -1 is equivalent to ficlExec (expects '\0' termination).
+ </DD>
+ <DT>
+ <B>int ficlExecXT(FICL_VM *pVM, FICL_WORD *pFW)</B>
+ </DT>
+ <DD>
+ Same as ficlExec, but takes a pointer to a FICL_WORD instead of a string. Executes the word and returns after it has finished. If executing the word results in an exception, this function
+ will re-throw the same code if it is nested under another ficlExec family function, or return the exception code directly if not. This function is useful if you need to execute the same
+ word repeatedly - you save the dictionary search and outer interpreter overhead.
+ </DD>
+ <DT>
+ <B>void ficlFreeVM(FICL_VM *pVM)</B>
+ </DT>
+ <DD>
+ Removes the VM in question from the system VM list and deletes the&nbsp; memory allocated to it. This is an optional call, since ficlTermSystem will do this cleanup for you. This function
+ is handy if you're going to do a lot of dynamic creation of VMs.
+ </DD>
+ <DT>
+ <B>FICL_VM *ficlNewVM(FICL_SYSTEM *pSys)</B>
+ </DT>
+ <DD>
+ Create, initialize, and return a VM from the heap using ficlMalloc. Links the VM into the system VM list for later reclamation by ficlTermSystem.
+ </DD>
+ <DT>
+ <B>FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name)</B>
+ </DT>
+ <DD>
+ Returns the address (also known as an XT in this case) of the specified word in the main dictionary. If not found, returns NULL. The address can be used in a call to ficlExecXT.
+ </DD>
+ <DT>
+ <B>FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys)</B>
+ </DT>
+ <DD>
+ Returns a pointer to the main system dictionary, or NULL if the system is uninitialized.
+ </DD>
+ <DT>
+ <B>FICL_DICT *ficlGetEnv(FICL_SYSTEM *pSys)</B>
+ </DT>
+ <DD>
+ Returns a pointer to the environment dictionary. This dictionary stores information that describes this implementation as required by the Standard.
+ </DD>
+ <DT>
+ <B>void ficlSetEnv(FICL_SYSTEM *pSys, char *name, UNS32 value)</B>
+ </DT>
+ <DD>
+ Enters a new constant into the environment dictionary, with the specified name and value.
+ </DD>
+ <DT>
+ <B>void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, UNS32 hi, UNS32 lo)</B>
+ </DT>
+ <DD>
+ Enters a new double-cell constant into the environment dictionary with the specified name and value.
+ </DD>
+ <DT>
+ <B>FICL_DICT *ficlGetLoc(FICL_SYSTEM *pSys)</B>
+ </DT>
+ <DD>
+ Returns a pointer to the locals dictionary. This function is defined only if FICL_WANT_LOCALS is #defined as non-zero (see sysdep.h). The locals dictionary is the symbol table for <A href=
+ "ficl_loc.html">local variables</A>.
+ </DD>
+ <DT>
+ <B>void ficlCompileCore(FICL_SYSTEM *pSys)</B>
+ </DT>
+ <DD>
+ Defined in words.c, this function builds ficl's primitives.&nbsp;
+ </DD>
+ <DT>
+ <B>void ficlCompileSoftCore(FICL_SYSTEM *pSys)</B>
+ </DT>
+ <DD>
+ Defined in softcore.c, this function builds ANS required words and ficl extras by evaluating a text string (think of it as a memory mapped file ;-) ). The string itself is built from files
+ in the softwords directory by PERL script softcore.pl.&nbsp;
+ </DD>
+ </DL>
+ <HR>
+ <TABLE border="0" cellspacing="5" cols="2">
+ <TR>
+ <TD colspan="2">
+ <H2>
+ <A name="manifest"></A>Ficl Source Files
+ </H2>
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>ficl.h</B>
+ </TD>
+ <TD>
+ Declares most public functions and all data structures. Includes sysdep.h and math.h
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>sysdep.h</B>
+ </TD>
+ <TD>
+ Declares system dependent functions and contains build control macros. Edit this file to port to another system.
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>math.h</B>
+ </TD>
+ <TD>
+ Declares functions for 64 bit math
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>dict.c</B>
+ </TD>
+ <TD>
+ Dictionary
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>ficl.c</B>
+ </TD>
+ <TD>
+ System initialization, termination, and ficlExec
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>float.c</B>
+ </TD>
+ <TD>
+ Adds precompiled definitions from the optional FLOAT word set. Most of the file is conditioned on FICL_WANT_FLOAT
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>math64.c</B>
+ </TD>
+ <TD>
+ Implementation of 64 bit math words (except the two unsigned primitives declared in sysdep.h and implemented in sysdep.c)
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>prefix.c</B>
+ </TD>
+ <TD>
+ The optional prefix parse step (conditioned on FICL_EXTENDED_PREFIX). This parse step handles numeric constructs like 0xa100, for example. See the release notes for more on parse steps.
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>search.c</B>
+ </TD>
+ <TD>
+ Contains C implementations of several of the SEARCH and SEARCH EXT words
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>softcore.c</B>
+ </TD>
+ <TD>
+ Contains all of the "soft" words - those written in Forth and compiled by Ficl at startup time. Sources for these words are in the softwords directory. The files softwords/softcore.bat
+ and softwords/softcore.pl generate softcore.c from the .fr sources.
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>softwords/</B>
+ </TD>
+ <TD>
+ Directory contains sources and translation scripts for the words defined in softcore.c. Softcore.c depends on most of the files in this directory. See softcore.bat for the actual list of
+ files that contribute to softcore.c. This is where you'll find source code for the object oriented extensions. PERL script softcore.pl converts the .fr files into softcore.c.
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>stack.c</B>
+ </TD>
+ <TD>
+ Stack methods
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>sysdep.c</B>
+ </TD>
+ <TD>
+ Implementation of system dependent functions declared in sysdep.h
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>testmain.c</B>
+ </TD>
+ <TD>
+ The main() function for unix/linux/win32 console applications - use this as an example to integrate ficl into your system. Also contains some definitions for testing - also useful in
+ unix/linux/win32 land.
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>tools.c</B>
+ </TD>
+ <TD>
+ Contains C implementations of TOOLS and TOOLS EXT words, the debugger, and debugger support words.
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>vm.c</B>
+ </TD>
+ <TD>
+ Virtual Machine methods
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>win32.c &amp; unix.c</B>
+ </TD>
+ <TD>
+ Platform extensions words loaded in ficl.c by ficlCompilePlatform() - conditioned on FICL_WANT_PLATFORM
+ </TD>
+ </TR>
+ <TR>
+ <TD>
+ <B>words.c</B>
+ </TD>
+ <TD>
+ Exports ficlCompileCore(), the run-time dictionary builder, and contains most precompiled CORE and CORE-EXT words.
+ </TD>
+ </TR>
+ </TABLE>
+ <HR>
+ <H2>
+ <A name="extras"></A>Ficl extras
+ </H2>
+ <H3>
+ <A name="exnumber"></A>Number syntax
+ </H3>
+ You can precede a number with "0x", as in C, and it will be interpreted as a hex value regardless of the value of <CODE>BASE</CODE>. Likewise, numbers prefixed with "0d" will be interpreted as
+ decimal values. Example:
+<PRE>
+ok&gt; decimal 123 . cr
+123
+ok&gt; 0x123 . cr
+291
+ok&gt; 0x123 x. cr
+123
+</PRE>
+ Note: ficl2.05 and later - this behavior is controlled by the <A href="ficl_parse.html">prefix parser</A> defined in <CODE>prefix.c</CODE>. You can add other prefixes by defining handlers for
+ them in ficl or C.
+ <H3>
+ <A name="exsearch"></A> The <CODE>SEARCH</CODE> wordset and Ficl extensions
+ </H3>
+ <P>
+ Ficl implements many of the search order words in terms of two primitives called <CODE><A href="#tosearch">&gt;SEARCH</A></CODE> and <CODE><A href="#searchfrom">SEARCH&gt;</A></CODE>. As
+ their names suggest (assuming you're familiar with Forth), they push and pop the search order stack.
+ </P>
+ <P>
+ The standard does not appear to specify any conditions under which the search order is reset to a sane state. Ficl resets the search order to its default state whenever <TT>ABORT</TT>
+ happens. This includes stack underflows and overflows. <TT>QUIT</TT> does not affect the search order. The minimum search order (set by <TT>ONLY</TT>) is equivalent to
+ </P>
+<PRE>
+FORTH-WORDLIST 1 SET-ORDER
+</PRE>
+ <P>
+ There is a default maximum of 16 wordlists in the search order. This can be changed by redefining FICL_DEFAULT_VOCS (declared in sysdep.h).
+ </P>
+ <P>
+ <B>Note</B>: Ficl resets the search order whenever it does <TT>ABORT</TT>. If you don't like this behavior, just comment out the dictResetSearchOrder() lines in ficlExec().
+ </P>
+ <DL>
+ <DT>
+ <A name="tosearch"></A><CODE>&gt;search ( wid -- )</CODE>
+ </DT>
+ <DD>
+ Push <TT>wid</TT> onto the search order. Many of the other search order words are written in terms of the <TT>SEARCH&gt;</TT> and <TT>&gt;SEARCH</TT> primitives. This word can be defined in
+ ANS Forth as follows
+ </DD>
+ <DD>
+ <TT>: &gt;search&nbsp;&nbsp; &gt;r get-order 1+ r&gt; swap set-order ;</TT>
+ </DD>
+ <DT>
+ <A name="searchfrom"></A><TT>search&gt;&nbsp;&nbsp; ( -- wid )</TT>
+ </DT>
+ <DD>
+ Pop <TT>wid</TT> off the search order (can be coded in ANS Forth as&nbsp;<TT>: search&gt;&nbsp; get-order nip 1- set-order ;</TT> )
+ </DD>
+ <DT>
+ <A name="ficlsetcurrent"></A><TT>ficl-set-current&nbsp;&nbsp; ( wid -- old-wid )</TT>
+ </DT>
+ <DD>
+ Set wid as compile wordlist, leaving the previous compile wordlist on the stack
+ </DD>
+ <DT>
+ <A name="ficlvocabulary"></A><TT>ficl-vocabulary&nbsp;&nbsp; ( nBins "name" -- )</TT>
+ </DT>
+ <DD>
+ Creates a <TT>ficl-wordlist</TT> with the specified number of hash table bins, binds it to the name, and associates the semantics of <TT>vocabulary</TT> with it (replaces the top wid in the
+ search order list with its own wid when executed)
+ </DD>
+ <DT>
+ <A name="ficlwordlist"></A><TT>ficl-wordlist&nbsp;&nbsp; ( nBins -- wid )</TT>
+ </DT>
+ <DD>
+ Creates a wordlist with the specified number of hash table bins, and leaves the address of the wordlist on the stack. A <TT>ficl-wordlist</TT> behaves exactly as a regular wordlist, but it
+ may search faster depending on the number of bins chosen and the number of words it contains at search time. As implemented in ficl, a wordlist is single threaded by default. <TT>
+ ficl-named-wordlist</TT> takes a name for the wordlist and creates a word that pushes the <TT>wid</TT>. This is by contrast to <TT>VOCABULARY</TT>, which also has a name, but replaces the
+ top of the search order with its <TT>wid</TT>.
+ </DD>
+ <DT>
+ <A name="ficlforgetwid"></A><TT>forget-wid&nbsp;&nbsp; ( wid -- )</TT>
+ </DT>
+ <DD>
+ Iterates through the specified wordlist and unlinks all definitions whose xt addresses are greater than or equal to the value of <TT>HERE</TT>, the dictionary fill pointer.&nbsp;
+ </DD>
+ <DT>
+ <A name="ficlhide"></A><TT>hide&nbsp;&nbsp; ( -- current-wid-was )</TT>
+ </DT>
+ <DD>
+ Push the <TT>hidden</TT> wordlist onto the search order, and set it as the current compile wordlist (unsing <TT>ficl-set-current</TT>). Leaves the previous compile wordlist ID. I use this
+ word to hide implementation factor words that have low reuse potential so that they don't clutter the default wordlist. To undo the effect of hide, execute&nbsp; <B><TT>previous
+ set-current</TT></B>
+ </DD>
+ <DT>
+ <A name="ficlhidden"></A><TT>hidden&nbsp;&nbsp; ( -- wid )</TT>
+ </DT>
+ <DD>
+ Wordlist for storing implementation factors of ficl provided words. To see what's in there, try:&nbsp; <B><TT>hide words previous set-current</TT></B>
+ </DD>
+ <DT>
+ <A name="wid-get-name"></A><TT>wid-get-name&nbsp;&nbsp; ( wid -- c-addr u )</TT>
+ </DT>
+ <DD>
+ Ficl wordlists (2.05 and later) have a name property that can be assigned. This is used by <TT>ORDER</TT> to list the names of wordlists in the search order.&nbsp;
+ </DD>
+ <DT>
+ <A name="wid-set-name"></A><TT>wid-set-name&nbsp;&nbsp; ( c-addr wid -- )</TT>
+ </DT>
+ <DD>
+ Ficl wordlists (2.05 and later) have a name property that can be assigned. This is used by <TT>ORDER</TT> to list the names of wordlists in the search order. The name is assumed to be a \0
+ terminated string (C style), which conveniently is how Ficl stores word names.&nbsp; See softwords/softcore.fr definition of <TT>brand-wordlist</TT>&nbsp;
+ </DD>
+ <DT>
+ <A name="wid-set-super"></A><TT>wid-set-super&nbsp;&nbsp; ( wid -- )</TT>
+ </DT>
+ <DD>
+ Ficl wordlists have a parent wordlist pointer that is not specified in standard Forth. Ficl initializes this pointer to NULL whenever it creates a wordlist, so it ordinarily has no effect.
+ This word sets the parent pointer to the wordlist specified on the top of the stack. Ficl's implementation of <TT>SEARCH-WORDLIST</TT> will chain backward through the parent link of the
+ wordlist when searching. This simplifies Ficl's object model in that the search order does not need to reflect an object's class hierarchy when searching for a method. It is possible to
+ implement Ficl object syntax in strict ANS Forth, but method finders need to manipulate the search order explicitly.
+ </DD>
+ </DL>
+ <H3>
+ <A name="exuser"></A>User variables
+ </H3>
+ <DL>
+ <DT>
+ <TT>user&nbsp;&nbsp; ( -- ) name</TT>
+ </DT>
+ <DD>
+ Create a user variable with the given name. User variables are virtual machine local. Each VM allocates a fixed amount of storage for them. You can change the maximum number of user
+ variables allowed by defining FICL_USER_CELLS on your compiiler's command line. Default is 16 user cells. User variables behave like <TT>VARIABLE</TT>s in all other respects (you use @ and
+ ! on them, for example). Example:
+ </DD>
+ <DD>
+ <DL>
+ <DD>
+ <TT>user current-class</TT>
+ </DD>
+ <DD>
+ <TT>0 current-class !</TT>
+ </DD>
+ </DL>
+ </DD>
+ </DL>
+ <H3>
+ <A name="exmisc"></A>Miscellaneous
+ </H3>
+ <DL>
+ <DT>
+ <TT>-roll&nbsp;&nbsp; ( xu xu-1 ... x0 u -- x0 xu-1 ... x1 )&nbsp;</TT>
+ </DT>
+ <DD>
+ Rotate u+1 items on top of the stack after removing u. Rotation is in the opposite sense to <TT>ROLL</TT>
+ </DD>
+ </DL>
+ <DL>
+ <DT>
+ <A name="minusrot"></A><TT>-rot&nbsp;&nbsp; ( a b c -- c a b )</TT>
+ </DT>
+ <DD>
+ Rotate the top three stack entries, moving the top of stack to third place. I like to think of this as <TT>1<SUP>1</SUP>/<SUB>2</SUB>swap</TT> because it's good for tucking a single cell
+ value behind a cell-pair (like an object).&nbsp;
+ </DD>
+ </DL>
+ <DL>
+ <DT>
+ <TT>.env&nbsp;&nbsp; ( -- )</TT>
+ </DT>
+ <DD>
+ List all environment variables of the system
+ </DD>
+ <DT>
+ <TT>.hash&nbsp;&nbsp; ( -- )</TT>
+ </DT>
+ <DD>
+ List hash table performance statistics of the wordlist that's first in the search order
+ </DD>
+ <DT>
+ <TT>.ver&nbsp;&nbsp; ( -- )</TT>
+ </DT>
+ <DD>
+ Display ficl version ID
+ </DD>
+ <DT>
+ <TT>&gt;name&nbsp;&nbsp; ( xt -- c-addr u )</TT>
+ </DT>
+ <DD>
+ Convert a word's execution token into the address and length of its name
+ </DD>
+ <DT>
+ <TT>body&gt;&nbsp;&nbsp; ( a-addr -- xt )</TT>
+ </DT>
+ <DD>
+ Reverses the effect of <TT>CORE</TT> word <TT>&gt;body</TT> (converts a parameter field address to an execution token)
+ </DD>
+ <DT>
+ <TT>compile-only</TT>
+ </DT>
+ <DD>
+ Mark the most recently defined word as being executable only while in compile state. Many <TT>immediate</TT> words have this property.
+ </DD>
+ <DT>
+ <TT>empty&nbsp;&nbsp; ( -- )</TT>&nbsp;
+ </DT>
+ <DD>
+ Empty the parameter stack
+ </DD>
+ <DT>
+ <TT>endif</TT>
+ </DT>
+ <DD>
+ Synonym for <TT>THEN</TT>
+ </DD>
+ <DT>
+ <A name="last-word"></A><TT>last-word&nbsp;&nbsp; ( -- xt )</TT>
+ </DT>
+ <DD>
+ Pushes the xt address of the most recently defined word. This applies to colon definitions, constants, variables, and words that use <TT>create</TT>. You can print the name of the most
+ recently defined word with&nbsp;
+ </DD>
+ <DD>
+ <B><TT>last-word &gt;name type</TT>&nbsp;</B>
+ </DD>
+ <DT>
+ <TT>parse-word&nbsp;&nbsp; ( &lt;spaces&gt;name -- c-addr u )</TT>
+ </DT>
+ <DD>
+ Skip leading spaces and parse name delimited by a space. c-addr is the address within the input buffer and u is the length of the selected string. If the parse area is empty, the resulting
+ string has a zero length. (From the Standard)
+ </DD>
+ <DT>
+ <A name="qfetch"></A><TT>q@&nbsp;&nbsp; ( addr -- x )</TT>
+ </DT>
+ <DD>
+ Fetch a 32 bit quantity from the specified address
+ </DD>
+ <DT>
+ <A name="qbang"></A><TT>q!&nbsp;&nbsp; ( x addr -- )</TT>
+ </DT>
+ <DD>
+ Store a 32 bit quantity to the specified address&nbsp;
+ </DD>
+ <DT>
+ <TT>w@&nbsp;&nbsp; ( addr -- x )</TT>
+ </DT>
+ <DD>
+ Fetch a 16 bit quantity from the specified address
+ </DD>
+ <DT>
+ <TT>w!&nbsp;&nbsp; ( x addr -- )</TT>
+ </DT>
+ <DD>
+ Store a 16 bit quantity to the specified address (the low 16 bits of the given value)
+ </DD>
+ <DT>
+ <A name="xdot"></A><TT>x.&nbsp;&nbsp; ( x -- )</TT>
+ </DT>
+ <DD>
+ Pop and display the value in hex format, regardless of the current value of <TT>BASE</TT>
+ </DD>
+ </DL>
+ <H3>
+ <A name="exficlwin"></A>Extra words defined in testmain.c (Win32 and POSIX versions)
+ </H3>
+ <DL>
+ <DT>
+ <TT>break&nbsp;&nbsp; ( -- )</TT>
+ </DT>
+ <DD>
+ Does nothing - just a handy place to set a debugger breakpoint
+ </DD>
+ <DT>
+ <TT>cd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( "directory-name&lt;newline&gt;" -- )</TT>
+ </DT>
+ <DD>
+ Executes the Win32 chdir() function, changing the program's logged directory.
+ </DD>
+ <DT>
+ <A name="clock"></A><TT>clock&nbsp;&nbsp; ( -- now )</TT>
+ </DT>
+ <DD>
+ Wrapper for the ANSI C clock() function. Returns the number of clock ticks elapsed since process start.
+ </DD>
+ <DT>
+ <A name="clockspersec"></A><TT>clocks/sec&nbsp;&nbsp; ( -- clocks_per_sec )</TT>
+ </DT>
+ <DD>
+ Pushes the number of ticks in a second as returned by <TT>clock</TT>
+ </DD>
+ <DT>
+ <A name="ficlload"></A><TT>load&nbsp;&nbsp;&nbsp; ( "filename&lt;newline&gt;" -- )</TT>
+ </DT>
+ <DD>
+ Opens the Forth source file specified and loads it one line at a time, like <TT>INCLUDED (FILE)</TT>
+ </DD>
+ <DT>
+ <TT>pwd&nbsp;&nbsp;&nbsp;&nbsp; ( -- )</TT>
+ </DT>
+ <DD>
+ Prints the current working directory as set by <TT>cd</TT>
+ </DD>
+ <DT>
+ <TT>system&nbsp; ( "command&lt;newline&gt;" -- )</TT>
+ </DT>
+ <DD>
+ Issues a command to a shell; implemented with the Win32 system() call.
+ </DD>
+ <DT>
+ <TT>spewhash&nbsp;&nbsp; ( "filename&lt;newline&gt;" -- )</TT>
+ </DT>
+ <DD>
+ Dumps all threads of the current compilation wordlist to the specified text file. This was useful when I thought there might be some point in attempting to optimize the hash function. I no
+ longer harbor those illusions.
+ </DD>
+ </DL>
+ <H3>
+ Words defined in FiclWin only
+ </H3>
+ <DL>
+ <DT>
+ <TT>!oreg&nbsp;&nbsp; ( c -- )</TT>
+ </DT>
+ <DD>
+ Set the value of the simulated LED register as specified (0..255)
+ </DD>
+ <DT>
+ <TT>@ireg&nbsp;&nbsp; ( -- c )</TT>
+ </DT>
+ <DD>
+ Gets the value of the simulated switch block (0..255)
+ </DD>
+ <DT>
+ <TT>!dac&nbsp;&nbsp;&nbsp; ( c -- )</TT>
+ </DT>
+ <DD>
+ Sets the value of the bargraph control as specified. Valid values range from 0..255
+ </DD>
+ <DT>
+ <TT>@adc&nbsp;&nbsp;&nbsp; ( -- c )</TT>
+ </DT>
+ <DD>
+ Fetches the current position of the slider control. Range is 0..255
+ </DD>
+ <DT>
+ <TT>status"&nbsp;&nbsp; ( "ccc&lt;quote&gt;" -- )</TT>
+ </DT>
+ <DD>
+ Set the mainframe window's status line to the text specified, up to the first trailing quote character.
+ </DD>
+ <DT>
+ <A name="ficlms"></A><TT><A href="http://www.taygeta.com/forth/dpans10.htm#10.6.2.1905">ms</A>&nbsp;&nbsp; ( u -- )</TT>
+ </DT>
+ <DD>
+ Causes the running virtual machine to sleep() for the number of milliseconds specified by the top-of-stack value.
+ </DD>
+ </DL>
+ <HR>
+ <H2>
+ <A name="ansinfo"></A>ANS Required Information
+ </H2>
+ <B>ANS Forth System</B><BR>
+ <B>Providing names from the Core Extensions word set&nbsp;</B><BR>
+ <B>Providing the Exception word set</B><BR>
+ <B>Providing names from the Exception Extensions word set</B><BR>
+ <B>Providing the Locals word set&nbsp;</B><BR>
+ <B>Providing the Locals Extensions word set&nbsp;</B><BR>
+ <B>Providing the Memory Allocation word set</B><BR>
+ <B>Providing the Programming-Tools word set</B><BR>
+ <B>Providing names from the Programming-Tools Extensions word set</B><BR>
+ <B>Providing the Search-Order word set</B><BR>
+ <B>Providing the Search-Order Extensions word set</B>
+ <H3>
+ Implementation-defined Options
+ </H3>
+ The implementation-defined items in the following list represent characteristics and choices left to the discretion of the implementor, provided that the requirements of the Standard are met. A
+ system shall document the values for, or behaviors of, each item.&nbsp;
+ <UL>
+ <LI>
+ <B>aligned address requirements (3.1.3.3 Addresses);</B>&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">System dependent. You can change the default address alignment by defining FICL_ALIGN on your compiler's command line. The default value is set to 2 in sysdep.h. This
+ causes dictionary entries and <TT>ALIGN</TT> and <TT>ALIGNED</TT> to align on 4 byte boundaries. To align on <B>2<SUP>n</SUP></B> byte boundaries, set FICL_ALIGN to <B>n</B>.&nbsp;</FONT>
+ </LI>
+ <LI>
+ <B>behavior of 6.1.1320 EMIT for non-graphic characters</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Depends on target system, C runtime library, and your implementation of ficlTextOut().</FONT>
+ </LI>
+ <LI>
+ <B>character editing of 6.1.0695 ACCEPT and 6.2.1390 EXPECT</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">None implemented in the versions supplied in words.c. Because ficlExec() is supplied a text buffer externally, it's up to your system to define how that buffer will
+ be obtained.</FONT>
+ </LI>
+ <LI>
+ <B>character set (3.1.2 Character types, 6.1.1320 EMIT, 6.1.1750 KEY)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Depends on target system and implementation of ficlTextOut()</FONT>
+ </LI>
+ <LI>
+ <B>character-aligned address requirements (3.1.3.3 Addresses)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl characters are one byte each. There are no alignment requirements.</FONT>
+ </LI>
+ <LI>
+ <B>character-set-extensions matching characteristics (3.4.2 Finding definition n<FONT color="#000000">ames)</FONT></B><FONT color="#000000">;&nbsp;</FONT>
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">No special processing is performed on characters beyond case-folding. Therefore, extended characters will not match their unaccented counterparts.</FONT>
+ </LI>
+ <LI>
+ <B>conditions under which control characters match a space delimiter (3.4.1.1 Delimiters)</B>;<FONT color="#FF6666">&nbsp;</FONT>
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl uses the Standard C function isspace() to distinguish space characters. The rest is up to your library vendor.</FONT>
+ </LI>
+ <LI>
+ <B>format of the control-flow stack (3.2.3.2 Control-flow stack)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Uses the data stack</FONT>
+ </LI>
+ <LI>
+ <B>conversion of digits larger than thirty-five (3.2.1.2 Digit conversion)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">The maximum supported value of <TT>BASE</TT> is 36. Ficl will assertion fail in function ltoa of vm.c if the base is found to be larger than 36 or smaller than 2.
+ There will be no effect if NDEBUG is defined</FONT>, however, other than possibly unexpected behavior.&nbsp;
+ </LI>
+ <LI>
+ <B>display after input terminates in 6.1.0695 ACCEPT and 6.2.1390 EXPECT</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Target system dependent</FONT>
+ </LI>
+ <LI>
+ <B>exception abort sequence (as in 6.1.0680 ABORT")</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Does <TT>ABORT</TT></FONT>
+ </LI>
+ <LI>
+ <B>input line terminator (3.2.4.1 User input device)</B>;<FONT color="#FF0000">&nbsp;</FONT>
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Target system dependent (implementation of outer loop that calls ficlExec)</FONT>
+ </LI>
+ <LI>
+ <B>maximum size of a counted string, in characters (3.1.3.4 Counted strings, 6.1.2450 WORD)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">255</FONT>
+ </LI>
+ <LI>
+ <B>maximum size of a parsed string (3.4.1 Parsing)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ Limited by available memory and the maximum unsigned value that can fit in a CELL (2<SUP>32</SUP>-1).&nbsp;
+ </LI>
+ <LI>
+ <B>maximum size of a definition name, in characters (3.3.1.2 Definition names)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl stores the first 31 characters of a definition name.</FONT>
+ </LI>
+ <LI>
+ <B>maximum string length for 6.1.1345 ENVIRONMENT?, in characters</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Same as maximum definition name length</FONT>
+ </LI>
+ <LI>
+ <B>method of selecting 3.2.4.1 User input device</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ None supported. This is up to the target system&nbsp;
+ </LI>
+ <LI>
+ <B>method of selecting 3.2.4.2 User output device</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ None supported. This is up to the target system&nbsp;
+ </LI>
+ <LI>
+ <B>methods of dictionary compilation (3.3 The Forth dictionary)</B>;&nbsp;
+ </LI>
+ <LI>
+ <B>number of bits in one address unit (3.1.3.3 Addresses)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Target system dependent. Ficl generally supports processors that can address 8 bit quantities, but there is no dependency that I'm aware of.</FONT>
+ </LI>
+ <LI>
+ <B>number representation and arithmetic (3.2.1.1 Internal number representation)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ System dependent. Ficl represents a CELL internally as a union that can hold INT32 (a signed 32 bit scalar value), UNS32 (32 bits unsigned), and an untyped pointer. No specific byte
+ ordering is assumed.&nbsp;
+ </LI>
+ <LI>
+ <B>ranges for n, +n, u, d, +d, and ud (3.1.3 Single-cell types, 3.1.4 Cell-pair types)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ Assuming a 32 bit implementation, range for signed single-cell values is -2<SUP>31</SUP>..2<SUP>31</SUP>-1. Range for unsigned single cell values is 0..2<SUP>32</SUP>-1. Range for signed
+ double-cell values is -2<SUP>63</SUP>..2<SUP>63</SUP>-1. Range for unsigned single cell values is 0..2<SUP>64</SUP>-1.&nbsp;
+ </LI>
+ <LI>
+ <B>read-only data-space regions (3.3.3 Data space)</B>;
+ </LI>
+ <LI>
+ <BR>
+ None&nbsp;
+ </LI>
+ <LI>
+ <B>size of buffer at 6.1.2450 WORD (3.3.3.6 Other transient regions)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ Default is 255. Depends on the setting of nPAD in ficl.h.&nbsp;
+ </LI>
+ <LI>
+ <B>size of one cell in address units (3.1.3 Single-cell types)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">System dependent, generally four.</FONT>
+ </LI>
+ <LI>
+ <B>size of one character in address units (3.1.2 Character types)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">System dependent, generally one.</FONT>
+ </LI>
+ <LI>
+ <B>size of the keyboard terminal input buffer (3.3.3.5 Input buffers)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">This buffer is supplied by the host program. Ficl imposes no practical limit.</FONT>
+ </LI>
+ <LI>
+ <B>size of the pictured numeric output string buffer (3.3.3.6 Other transient regions)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ Default is 255 characters. Depends on the setting of nPAD in ficl.h.&nbsp;
+ </LI>
+ <LI>
+ <B>size of the scratch area whose address is returned by 6.2.2000 PAD (3.3.3.6 Other transient regions)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ Not presently supported&nbsp;
+ </LI>
+ <LI>
+ <B>system case-sensitivity characteristics (3.4.2 Finding definition names)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl is not case sensitive</FONT>
+ </LI>
+ <LI>
+ <B>system prompt (3.4 The Forth text interpreter, 6.1.2050 QUIT)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">"ok&gt;"</FONT>
+ </LI>
+ <LI>
+ <B>type of division rounding (3.2.2.1 Integer division, 6.1.0100 */, 6.1.0110 */MOD, 6.1.0230 /, 6.1.0240 /MOD, 6.1.1890 MOD)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Symmetric</FONT>
+ </LI>
+ <LI>
+ <B>values of 6.1.2250 STATE when true</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">One (no others)</FONT>
+ </LI>
+ <LI>
+ <B>values returned after arithmetic overflow (3.2.2.2 Other integer operations)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ System dependent. Ficl makes no special checks for overflow.&nbsp;
+ </LI>
+ <LI>
+ <B>whether the current definition can be found after 6.1.1250 DOES&gt; (6.1.0450 :)</B>.&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">No. Definitions are unsmudged after ; only, and only then if no control structure matching problems have been detected.</FONT>
+ </LI>
+ </UL>
+ <H3>
+ Ambiguous Conditions
+ </H3>
+ A system shall document the system action taken upon each of the general or specific ambiguous conditions identified in this Standard. See 3.4.4 Possible actions on an ambiguous
+ condition.&nbsp;
+ <P>
+ The following general ambiguous conditions could occur because of a combination of factors:&nbsp;
+ </P>
+ <UL>
+ <LI>
+ <B>a name is neither a valid definition name nor a valid number during text interpretation (3.4 The Forth text interpreter)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl does <TT>ABORT</TT> and prints the name followed by " not found".</FONT>
+ </LI>
+ <LI>
+ <B>a definition name exceeded the maximum length allowed (3.3.1.2 Definition names)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl stores the first 31 characters of the definition name, and uses all characters of the name in computing its hash code. The actual length of the name, up to 255
+ characters, is stored in the definition's length field.</FONT>
+ </LI>
+ <LI>
+ <B>addressing a region not listed in 3.3.3 Data Space</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">No problem: all addresses in ficl are absolute. You can reach any 32 bit address in Ficl's address space.</FONT>
+ </LI>
+ <LI>
+ <B>argument type incompatible with specified input parameter, e.g., passing a flag to a word expecting an n (3.1 Data types)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl makes no check for argument type compatibility. Effects of a mismatch vary widely depending on the specific problem and operands.</FONT>
+ </LI>
+ <LI>
+ <B>attempting to obtain the execution token, (e.g., with 6.1.0070 ', 6.1.1550 FIND, etc.) of a definition with undefined interpretation semantics</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl returns a valid token, but the result of executing that token while interpreting may be undesirable.</FONT>
+ </LI>
+ <LI>
+ <B>dividing by zero (6.1.0100 */, 6.1.0110 */MOD, 6.1.0230 /, 6.1.0240 /MOD, 6.1.1561 FM/MOD, 6.1.1890 MOD, 6.1.2214 SM/REM, 6.1.2370 UM/MOD, 8.6.1.1820 M*/)</B>;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Results are target procesor dependent. Generally, Ficl makes no check for divide-by-zero. The target processor will probably throw an exception.</FONT>
+ </LI>
+ <LI>
+ <B>insufficient data-stack space or return-stack space (stack overflow)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">With FICL_ROBUST (sysdep.h) set &gt;= 2, most parameter stack operations are checked for underflow and overflow. Ficl does not check the return stack.</FONT>
+ </LI>
+ <LI>
+ <B>insufficient space for loop-control parameters</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">No check - Evil results.</FONT>
+ </LI>
+ <LI>
+ <B>insufficient space in the dictionary</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl generates an error message if the dictionary is too full to create a definition header. It checks <TT>ALLOT</TT> as well, but it is possible to make an unchecked
+ allocation request that overflows the dictionary.</FONT>
+ </LI>
+ <LI>
+ <B>interpreting a word with undefined interpretation semantics</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl protects all ANS Forth words with undefined interpretation semantics from being executed while in interpret state. It is possible to defeat this protection using
+ ' (tick) and <TT>EXECUTE</TT>, though.</FONT>
+ </LI>
+ <LI>
+ <B>modifying the contents of the input buffer or a string literal (3.3.3.4 Text-literal regions, 3.3.3.5 Input buffers)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Varies depending on the nature of the buffer. The input buffer is supplied by ficl's host function, and may reside in read-only memory. If so, writing the input
+ buffer can ganerate an exception. String literals are stored in the dictionary, and are writable.</FONT>
+ </LI>
+ <LI>
+ <B>overflow of a pictured numeric output string</B>;
+ </LI>
+ <LI>
+ <BR>
+ In the unlikely event you are able to construct a pictured numeric string of more than 255 characters, the system will be corrupted unpredictably. The buffer area that holds pictured
+ numeric output is at the end of the virtual machine. Whatever is mapped after the offending VM in memory will be trashed, along with the heap structures that contain it.&nbsp;
+ </LI>
+ <LI>
+ <B>parsed string overflow</B>;
+ </LI>
+ <LI>
+ <BR>
+ Ficl does not copy parsed strings unless asked to. Ordinarily, a string parsed from the input buffer during normal interpretation is left in-place, so there is no possibility of overflow.
+ If you ask to parse a string into the dictionary, as in <TT>SLITERAL</TT>, you need to have enough room for the string, otherwise bad things may happen. This is not usually a problem.&nbsp;
+ </LI>
+ <LI>
+ <B>producing a result out of range, e.g., multiplication (using *) results in a value too big to be represented by a single-cell integer (6.1.0090 *, 6.1.0100 */, 6.1.0110 */MOD, 6.1.0570
+ &gt;NUMBER, 6.1.1561 FM/MOD, 6.1.2214 SM/REM, 6.1.2370 UM/MOD, 6.2.0970 CONVERT, 8.6.1.1820 M*/)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Value will be truncated</FONT>
+ </LI>
+ <LI>
+ <B>reading from an empty data stack or return stack (stack underflow)</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Most stack underflows are detected and prevented if FICL_ROBUST (sysdep.h) is set to 2 or greater. Otherwise, the stack pointer and size are likely to be
+ trashed.</FONT>
+ </LI>
+ <LI>
+ <B>unexpected end of input buffer, resulting in an attempt to use a zero-length string as a name</B>;&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ <FONT color="#000000">Ficl returns for a new input buffer until a non-empty one is supplied.</FONT>
+ </LI>
+ </UL>
+ The following specific ambiguous conditions are noted in the glossary entries of the relevant words:&nbsp;
+ <UL>
+ <LI>
+ <B>&gt;IN greater than size of input buffer (3.4.1 Parsing)</B>
+ </LI>
+ <LI>
+ <BR>
+ Bad Things occur - unpredictable bacause the input buffer is supplied by the host program's outer loop.&nbsp;
+ </LI>
+ <LI>
+ <B>6.1.2120 RECURSE appears after 6.1.1250 DOES&gt;</B>
+ </LI>
+ <LI>
+ <BR>
+ It finds the address of the definition before <TT>DOES&gt;</TT>
+ </LI>
+ <LI>
+ <B>argument input source different than current input source for 6.2.2148 RESTORE-INPUT</B>
+ </LI>
+ <LI>
+ <BR>
+ Not implemented&nbsp;
+ </LI>
+ <LI>
+ <B>data space containing definitions is de-allocated (3.3.3.2 Contiguous regions)</B>
+ </LI>
+ <LI>
+ <BR>
+ This is OK until the cells are overwritten with something else. The dictionary maintains a hash table, and the table must be updated in order to de-allocate words without corruption.&nbsp;
+ </LI>
+ <LI>
+ <B>data space read/write with incorrect alignment (3.3.3.1 Address alignment)</B>
+ </LI>
+ <LI>
+ <BR>
+ Target processor dependent. Consequences include: none (Intel), address error exception (68K).&nbsp;
+ </LI>
+ <LI>
+ <B>data-space pointer not properly aligned (6.1.0150 ,, 6.1.0860 C,)</B>
+ </LI>
+ <LI>
+ <BR>
+ See above on data space read/write alignment&nbsp;
+ </LI>
+ <LI>
+ <B>less than u+2 stack items (6.2.2030 PICK, 6.2.2150 ROLL)</B>
+ </LI>
+ <LI>
+ <BR>
+ Ficl detects a stack underflow and reports it, executing <TT>ABORT,</TT> as long as FICL_ROBUST is two or larger.&nbsp;
+ </LI>
+ <LI>
+ <B>loop-control parameters not available ( 6.1.0140 +LOOP, 6.1.1680 I, 6.1.1730 J, 6.1.1760 LEAVE, 6.1.1800 LOOP, 6.1.2380 UNLOOP)</B>
+ </LI>
+ <LI>
+ <BR>
+ Loop initiation words are responsible for checking the stack and guaranteeing that the control parameters are pushed. Any underflows will be detected early if FICL_ROBUST is set to two or
+ greater. Note however that Ficl only checks for return stack underflows at the end of each line of text.&nbsp;
+ </LI>
+ <LI>
+ <B>most recent definition does not have a name (6.1.1710 IMMEDIATE)</B>
+ </LI>
+ <LI>
+ <BR>
+ No problem.&nbsp;
+ </LI>
+ <LI>
+ <B>name not defined by 6.2.2405 VALUE used by 6.2.2295 TO</B>
+ </LI>
+ <LI>
+ <BR>
+ Ficl's version of <TT>TO</TT> works correctly with <TT>VALUE</TT>s, <TT>CONSTANT</TT>s and <TT>VARIABLE</TT>s.&nbsp;
+ </LI>
+ <LI>
+ <B>name not found (6.1.0070 ', 6.1.2033 POSTPONE, 6.1.2510 ['], 6.2.2530 [COMPILE])</B>
+ </LI>
+ <LI>
+ <BR>
+ Ficl prints an error message and does <TT>ABORT</TT>
+ </LI>
+ <LI>
+ <B>parameters are not of the same type (6.1.1240 DO, 6.2.0620 ?DO, 6.2.2440 WITHIN)</B>
+ </LI>
+ <LI>
+ <BR>
+ No check. Results vary depending on the specific problem.&nbsp;
+ </LI>
+ <LI>
+ <B>6.1.2033 POSTPONE or 6.2.2530 [COMPILE] applied to 6.2.2295 TO</B>
+ </LI>
+ <LI>
+ <BR>
+ The word is postponed correctly.&nbsp;
+ </LI>
+ <LI>
+ <B>string longer than a counted string returned by 6.1.2450 WORD</B>
+ </LI>
+ <LI>
+ <BR>
+ Ficl stores the first FICL_STRING_MAX-1 chars in the destination buffer. (The extra character is the trailing space required by the standard. Yuck.)&nbsp;
+ </LI>
+ <LI>
+ <B>u greater than or equal to the number of bits in a cell (6.1.1805 LSHIFT, 6.1.2162 RSHIFT)</B>
+ </LI>
+ <LI>
+ <BR>
+ Depends on target process or and C runtime library implementations of the &lt;&lt; and &gt;&gt; operators on unsigned values. For I386, the processor appears to shift modulo the number of
+ bits in a cell.&nbsp;
+ </LI>
+ <LI>
+ <B>word not defined via 6.1.1000 CREATE (6.1.0550 &gt;BODY, 6.1.1250 DOES&gt;)</B>
+ </LI>
+ <LI>
+ <BR>
+ <B>words improperly used outside 6.1.0490 &lt;# and 6.1.0040 #&gt; (6.1.0030 #, 6.1.0050 #S, 6.1.1670 HOLD, 6.1.2210 SIGN)</B><BR>
+ Don't. <TT>CREATE</TT> reserves a field in words it builds for <TT>DOES&gt;</TT>to fill in. If you use <TT>DOES&gt;</TT> on a word not made by <TT>CREATE</TT>, it will overwrite the first
+ cell of its parameter area. That's probably not what you want. Likewise, pictured numeric words assume that there is a string under construction in the VM's scratch buffer. If that's not
+ the case, results may be unpleasant.
+ </LI>
+ </UL>
+ <H3>
+ Locals Implementation-defined options
+ </H3>
+ <UL>
+ <LI>
+ <B>maximum number of locals in a definition (13.3.3 Processing locals, 13.6.2.1795 LOCALS|)</B>
+ </LI>
+ <LI>
+ <BR>
+ Default is 16. Change by redefining FICL_MAX_LOCALS, defined in sysdep.h
+ </LI>
+ </UL>
+ <H3>
+ Locals Ambiguous conditions
+ </H3>
+ <UL>
+ <LI>
+ <B>executing a named local while in interpretation state (13.6.1.0086 (LOCAL))</B>
+ </LI>
+ <LI>
+ <BR>
+ Locals can be found in interpretation state while in the context of a definition under construction. Under these circumstances, locals behave correctly. Locals are not visible at all
+ outside the scope of a definition.&nbsp;
+ </LI>
+ <LI>
+ <B>name not defined by VALUE or LOCAL (13.6.1.2295 TO)</B>
+ </LI>
+ <LI>
+ <BR>
+ See the CORE ambiguous conditions, above (no change)
+ </LI>
+ </UL>
+ <H3>
+ Programming Tools Implementation-defined options
+ </H3>
+ <UL>
+ <LI>
+ <B>source and format of display by 15.6.1.2194 SEE</B>
+ </LI>
+ <LI>
+ <BR>
+ SEE de-compiles definitions from the dictionary. Because Ficl words are threaded by their header addresses, it is very straightforward to print the name and other characteristics of words
+ in a definition. Primitives are so noted. Colon definitions are decompiled, but branch target labels are not reconstructed. Literals and string literals are so noted, and their contents
+ displayed.
+ </LI>
+ </UL>
+ <H3>
+ Search Order Implementation-defined options
+ </H3>
+ <UL>
+ <LI>
+ <B>maximum number of word lists in the search order (16.3.3 Finding definition names, 16.6.1.2197 SET-ORDER)</B>&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ Defaults to 16. Can be changed by redefining FICL_DEFAULT_VOCS, declared in sysdep.h&nbsp;
+ </LI>
+ <LI>
+ <B>minimum search order (16.6.1.2197 SET-ORDER, 16.6.2.1965 ONLY)</B>&nbsp;
+ </LI>
+ <LI>
+ <BR>
+ Equivalent to <TT>FORTH-WORDLIST 1 SET-ORDER</TT>
+ </LI>
+ </UL>
+ <H3>
+ Search Order Ambiguous conditions
+ </H3>
+ <UL>
+ <LI>
+ <B>changing the compilation word list (16.3.3 Finding definition names)</B>
+ </LI>
+ <LI>
+ <BR>
+ Ficl stores a link to the current definition independently of the compile wordlist while it is being defined, and links it into the compile wordlist only after the definition completes
+ successfully. Changing the compile wordlist mid-definition will cause the definition to link into the <I>new</I> compile wordlist.&nbsp;
+ </LI>
+ <LI>
+ <B>search order empty (16.6.2.2037 PREVIOUS)</B>
+ </LI>
+ <LI>
+ <BR>
+ Ficl prints an error message if the search order underflows, and resets the order to its default state.&nbsp;
+ </LI>
+ <LI>
+ <B>too many word lists in search order (16.6.2.0715 ALSO)</B>
+ </LI>
+ <LI>
+ <BR>
+ Ficl prints an error message if the search order overflows, and resets the order to its default state.
+ </LI>
+ </UL>
+ </DIV>
+ </BODY>
+</HTML>
+
diff --git a/doc/ficl1.ico b/doc/ficl1.ico
new file mode 100644
index 000000000000..403bd6f56cb9
--- /dev/null
+++ b/doc/ficl1.ico
Binary files differ
diff --git a/doc/ficl_debug.html b/doc/ficl_debug.html
new file mode 100644
index 000000000000..647b7b27a8d0
--- /dev/null
+++ b/doc/ficl_debug.html
@@ -0,0 +1,111 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta name="Author" content="john sadler">
+ <title>Ficl Debugger</title>
+</head>
+<body>
+<link REL="SHORTCUT ICON" href="ficl.ico">
+<h1><b>Ficl Debugger</b></h1>
+
+<script language="javascript" src="ficlheader.js"></script>
+
+<table COLS=1 WIDTH="650" >
+<tr>
+<td>
+<p>Ficl release 2.05 includes a simple step debugger for colon definitions
+and does> words. If you use it and can suggest improvements (or better
+yet if you write some), please let me know.</p>
+<h2>Using the debugger</h2>
+To debug a word, set up the stack with any parameters the word requires,
+then type:
+
+<b><pre>debug &lt;your word here></pre></b>
+
+<p>If the word is unnamed, or all you have is an xt, you can instead use:</p>
+<b><code>debug-xt ( xt -- )</code></b>
+<p>The debugger invokes <tt>see</tt> on the word, printing a crude source
+listing, then stops at the first instruction of the definition. There are
+four (case insensitive) commands you can use from here onwards:</p>
+
+<dl>
+<dt>I (step in)</dt>
+<dd>If the next instruction is a colon defintion or does> word, steps into
+that word's code. If the word is a primitive, simply executes the word.</dd>
+
+<dt>O (step over)</dt>
+<dd>Executes the next instruction in its entirety</dd>
+
+<dt>G (go)</dt>
+<dd>Run the word to completion and exit the debugger</dd>
+
+<dt>L (list)</dt>
+<dd>Lists the source code of the word presently being stepped</dd>
+
+<dt>Q (quit)</dt>
+<dd>Abort the word and exit the debugger, clearing the stack</dd>
+
+<dt>X (eXecute)</dt>
+<dd>Interpret the remainder of the line as ficl words for their side effects.
+Any errors will abort the debug session and reset the VM. Usage example:
+<pre>
+x drop 3 \ fix argument on stack
+</pre>
+</dd>
+
+<dt>Anything else</dt>
+<dd>Prints a list of available debugger commands</dd>
+</dl>
+
+<h2>The on-step event</h2>
+<p>If there is a defined word named <code>on-step</code> when the debugger starts, that
+word will be executed before every step. As a guideline, this word should
+have no side effects. Its intended use is to display the stacks and any other
+VM state you're interested in, but you
+may have some better ideas. If so, please let me know. The default on-step is:<p>
+<b><code>: on-step ." S: " .s cr ;</code></b>
+
+<h3>Other useful words for debugging and on-step</h3>
+<dl>
+<dt><code>r.s ( -- )</code></dt>
+<dd>Prints a represention of the state of the return stack non-destructively. You have to have
+a good understanding of the return stack side-effects of control words to make sense of it,
+but it does give an accurate representation of what's there. Example: <code>DO .. LOOP</code>s stack
+three parameters on the return stack: the loop count and limit, and the <code>LEAVE</code> target
+address.</dd>
+<dt><code>.s ( -- )</code></dt>
+<dd>Prints the parameter stack non-destructively</dd>
+<dt><code>f.s ( -- )</code></dt>
+<dd>Prints the float stack non-destructively (only available if FICL_WANT_FLOAT is enabled)</dd>
+</dl>
+<h2>Debugger internals</h2>
+<p>
+The debugger words are mostly located in source file <tt>tools.c</tt>. There are
+supporting words (<code>debug</code> and <code>on-step</code>) in softcore.fr as well.
+There are two main words that make the debugger go: debug-xt and step-break.
+Debug-xt takes the xt of a word to debug (as returned by <tt>'</tt>, for example)
+checks to see if it is debuggable (not a primitive), sets a breakpoint at its
+first instruction, and runs <code>see</code> on it. To set a breakpoint,
+<code>debug-xt</code>
+replaces the instruction at the breakpoint with the xt of <code>step-break</code>, and
+stores the original instruction and its address in a static breakpoint
+record. To clear the breakpoint, <code>step-break</code> simply replaces the original
+instruction and adjusts the target virtual machine's instruction pointer
+to run it.
+</p>
+<p><code>Step-break</code> is responsible for processing debugger commands and setting
+breakpoints at subsequent instructions.</p>
+<h3>To Do</h3>
+<bl>
+<li>The debugger needs to exit automatically when it encounters the end of the word
+it was asked to debug. Perhaps this could be a special kind of breakpoint?
+</li>
+<li>Add user-set breakpoints</li>
+<li>Add "step out" command</li>
+</bl>
+</td>
+</tr>
+</table>
+
+</body>
+</html>
diff --git a/doc/ficl_guts.htm b/doc/ficl_guts.htm
new file mode 100644
index 000000000000..ba982c454a3f
--- /dev/null
+++ b/doc/ficl_guts.htm
@@ -0,0 +1,69 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="john sadler">
+ <meta name="Description" content="the coolest embedded scripting language ever">
+ <title>Ficl - Internals</title>
+</head>
+<body>
+<link REL="SHORTCUT ICON" href="ficl.ico">
+<h1><b>Ficl Internal Structures</b></h1>
+
+<script language="javascript" src="ficlheader.js"></script>
+
+<h2>Contents</h2>
+
+<h2>Major Data Structures</h2>
+<p>
+A running memory image of Ficl consists of one or more FICL_SYSTEMs,
+each of which owns exactly one dictionary (FICL_DICT),
+and one or more virtual machines (FICL_VM). Each VM owns two stacks
+(FICL_STACK) - one for parameters (the parameter stack)
+and one for return addresses (the return stack).
+Ficl is a permissive, untyped language by nature,
+so its fundamental unit of storage is a CELL: a chunk of memory
+large enough to hold an address or a scalar type.
+</p>
+<h3>FICL_SYSTEM</h3>
+The system structure associates one or more virtual machines with a dictionary. All FICL_SYSTEMS
+include a link pointer that is used to keep track of every allocated system so that memory
+can be freed by ficlTermSystem. Each system contains a list of virtual machines associated with
+it. Each system has at least one virtual machine. In a typical implementation, there is one virtual
+machine per native OS thread, and there may be several VMs sharing a single FICL_SYSTEM, or one
+FICL_SYSTEM per VM if the implementation needs to support multiple user sessions in a robust way.
+
+A FICL_SYSTEM also includes a special dictionary for local variable support (if enabled
+by FICL_WANT_LOCALS) and another for environment variable support. Environment variables
+describe the configuration of the system in conformance with American National Standard Forth
+(ANS Forth).
+<h3>FICL_DICT</h3>
+A dictionary manages a fixed-size block of contiguous memory. It serves two roles: to keep track
+of allocated memory, and to collect symbol tables called wordlists. Each dictionary contains at
+least one wordlist. The dictionary organized memory (perhaps this is too kind) as an array of
+CELLs that grows from low memory to high memory within fixed limits determined by the
+FICL_DEFAULT_DICT parameter in sysdep.h.
+
+A wordlist is the controlling structure of a Ficl symbol table. Each wordlist is a hash table
+containing pointers to FICL_WORDs. Each FICL_WORD associates a pointer to code with one or more
+CELLs of the dictionay. Each word usually has a name as well, but this is not required. It is
+possible to create anonymous words using :NONAME.
+
+Each word's code pointer determines that word's runtime behavior, and by implication the purpose
+of its payload data. Some words interpret their payload as a list of Ficl words, and execute them.
+This is how new behaviors of the language are defined. Other words view their payload field as
+a location in which one or more CELLs can be stored (VARIABLEs, for example). At runtime, such
+words push the address of their payload area onto the parameter stack.
+<h3>FICL_VM</h3>
+The virtual machine collects state related to execution of Ficl words. Each VM includes
+registers used by the inner interpreter, some state variables (AKA user variables) such as
+the current numeric base, and a jmpbuf.
+A VM has a pointer to the FICL_SYSTEM of which it is a part. It also has a pointer to an incoming
+text string that it is interpreting. There are VM methods that excute a word given its address
+(xt), and ones that interpret a text string.
+<h3>FICL_STACK</h3>
+Each VM owns a parameter stack, a return stack, and if float support is enabled, a float parameter
+stack. Parameters, return addresses, and floats are all CELL sized, and values may be
+moved back and forth among stacks using various Ficl words for that purpose.
+</BODY>
+</HTML>
diff --git a/doc/ficl_loc.html b/doc/ficl_loc.html
new file mode 100644
index 000000000000..6e00e74b9ee3
--- /dev/null
+++ b/doc/ficl_loc.html
@@ -0,0 +1,161 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="john sadler">
+ <meta name="Description" content="the coolest embedded scripting language ever">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (Win98; U) [Netscape]">
+ <title>ficl Local Variables</title>
+</head>
+<body>
+
+<h1>
+<b>Local Variables in Ficl</b></h1>
+
+
+<script language="javascript" src="ficlheader.js"></script>
+
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h2>
+<a NAME="locals"></a>Local Variables</h2>
+Named locally scoped variables came late to Forth. Purists feel that experienced
+Forth programmers can (and should) write supportable code using only anonymous
+stack variables and good factoring, but they complain that novices use
+global variables too much. Local variables cost little in terms of code
+size and execution speed, and are very convenient for OO programming, where
+stack effects are more complex. I use them a lot (maybe I'm a weenie).&nbsp;
+<br><a href="http://www.taygeta.com/forth/dpans13.htm">Please refer to
+the Standard</a> for more information on local variables.
+<h2>
+<a NAME="jhlocal"></a>Johns-Hopkins local variables</h2>
+ANS Forth does not specify a complete and satisfying local variable facility.
+Instead it defines a foundation upon which to build one. Ficl comes with
+an adaptation of the Johns-Hopkins local variable syntax developed by John
+Hayes et al. This is my preferred form, and I've extended it to make <a href="ficl_oop.html">OOP</a>
+a bit simpler. Local variables can only be declared inside a definition,
+and are only visible in that definition. Here's the syntax of a JH local
+variable declaration:
+<blockquote><tt><b>{ </b>&lt;initialized-locals><b> | </b>&lt;cleared-locals><b>
+-- </b>&lt;ignored><b> }</b></tt></blockquote>
+The declaration is designed to look like a stack comment, but it uses curly
+braces instead of parens. The &lt;initialized-locals> names get their initial
+values from the stack when the word executes. The &lt;cleared-locals> names
+are (you guessed it) set to zero when the word executes, and any characters
+between -- and } are treated as a comment. The | and -- sections are optional,
+but they must appear in the order shown if they appear at all.&nbsp;
+<br><b>Double cell locals </b>(AKA 2locals): ordinarily, each local represents
+one cell. Local variable names prefixed with the characters "2:" in the declaration
+are double-cell locals. The prefix is not part of the local variable's name, only
+part of the declaration.
+They behave the same as single cell locals in all
+other respects. I use 2locals quite a bit in Ficl's OO classes, because
+objects in Ficl require two cells on the stack. You can modify the
+value of a double cell local with <tt><a href="http://www.taygeta.com/forth/dpans13.htm#13.6.1.2295">TO</a></tt>
+the same as you would a single cell local.
+<br>Following are some examples to illustrate usage (they are not intended
+to be good code otherwise). Try these out in FiclWin to get a feeling for
+how they work. Also see <code>softwords/string.fr</code> for an example of use of locals
+in OO code.
+<blockquote><b><tt>: local-demo&nbsp; { a b | c -- }</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; ." a = " a . cr</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; ." b = " b . cr</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; ." c = " c . cr ;</tt></b>
+<br><b><tt>1 2 local-demo&nbsp; ( you should see 1 2 0 )</tt></b>
+<p><b><tt>: my2dup&nbsp; { 2:x }&nbsp;&nbsp; x x ;&nbsp; ( uses a 2local
+)</tt></b>
+<br><b><tt>1 2 my2dup .s&nbsp;</tt></b>
+<br><b><tt>.( you should see 1 2 1 2 on the stack ) cr empty</tt></b>
+<p><b><tt>: my2swap&nbsp;&nbsp; { 2:x 2:y -- y x }&nbsp;&nbsp; y x ;&nbsp;
+( note use of 2locals )</tt></b>
+<br><b><tt>1 2 3 4 my2swap .s</tt></b>
+<br><b><tt>.( you should see 3 4 1 2 on the stack ) cr empty</tt></b>
+<p><b><tt>: totally-lame-swap&nbsp; { x y | temp -- y x }</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; y to temp</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; x to y</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; temp to x</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; x y ;</tt></b></blockquote>
+The last definition introduces the use of <tt>TO</tt> applied to local
+variables. <tt>TO</tt> knows whether it's operating on a <tt>LOCAL</tt>,
+a <tt>2LOCAL</tt>, or a <tt>VALUE</tt>, and does the right thing accordingly.&nbsp;
+<br>&nbsp;
+<h2>Other local variable syntaxes (deprecated)</h2>
+There are other syntaxes in use for local variables. You get the same compiled
+code regardless of which style of local declaration you choose, but the
+Johns-Hopkins syntax is more readable, more flexible, and supports <tt>2LOCAL</tt>s
+- if you agree, then skip this section.&nbsp;
+<p>Ficl includes support for <tt>LOCALS</tt> and <tt>LOCALS EXT</tt> words
+(all three of them!). I've implemented both of the local variable syntaxes
+suggested in DPANS Appendix A.13. Examples: (By the way, Ficl implements
+<tt>-ROT</tt>
+as <tt>: -rot&nbsp;&nbsp; 2 -roll ;</tt> )
+<blockquote><b><tt>\ Using LOCALS| from LOCALS EXT</tt></b>
+<br><b><tt>: -rot&nbsp;&nbsp; ( a b c -- c a b )</tt></b>
+<br><b><tt>&nbsp;&nbsp; locals| c b a |</tt></b>
+<br><b><tt>&nbsp; c a b&nbsp;</tt></b>
+<br><b><tt>;</tt></b></blockquote>
+
+<ul><b><tt>\ Using LOCAL END-LOCAL</tt></b>
+<br><b><tt>: -rot&nbsp;&nbsp; ( a b c -- c a b )</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; local c</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; local b</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; local a</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; end-locals</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; c a b</tt></b>
+<br><b><tt>;</tt></b></ul>
+
+<h2>
+Build Controls</h2>
+Local variable support is optional because it adds a small amount of overhead
+to the outer interpreter. You can disable it by setting FICL_WANT_LOCALS
+to 0 in sysdep.h. Beware: <a href="ficl_oop.html">Ficl's OOP</a> code makes
+heavy use of local variables, so if you disable locals, you're going to
+lose other capabilities too. Local variables can make Forth code quite
+a bit easier to read, so I'd encourage you to experiment with them.&nbsp;
+<p>The default maximum number of local variables is 16. It's controlled
+by FICL_MAX_LOCALS in sysdep.h.&nbsp;
+<h2>
+Release notes for local variables</h2>
+<p>Ficl 2.05 adds 2LOCALS using the "2:" prefix</p>
+<p>Ficl 2.02 includes by default an implementation of the Johns Hopkins local
+syntax (as best I can determine it from examples on the web). This syntax
+lets you declare local variables that look very much like a stack comment.
+Variables in the declaration appear in the "correct" order for a stack
+comment. Everything after the -- is treated as a comment. In addition,
+you can insert a | before the -- to declare one or more zero-initialized
+locals. Example:&nbsp;</p>
+<blockquote><b><tt>:tuck0&nbsp;&nbsp; { a b c | d -- 0 a b c }</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; d a b c ;</tt></b></blockquote>
+The | and -- delimiters can appear at most once, and must appear in the
+order shown in the example to work correctly. The local declaration ends
+at the first occurrence of }. The declaration must all be on one line as
+presently implemented.&nbsp;
+<p><b>Deprecated</b>: Ficl 2.01 added yet another local syntax that models
+a stack comment. This one is not compiled in the release, but you can add
+it by editing softwords/softcore.bat to include the file ficllocal.fr.
+In this case, parameters are re-ordered so that the rightmost initialized
+param comes from the top of the stack. The syntax is:&nbsp;
+<blockquote><b><tt>{{ &lt;initialized params> -- &lt;cleared params> }}</tt></b></blockquote>
+You can omit either the initialized or the cleared parameters. Parameters
+after the double dash are set to zero initially. Those to the left are
+initialized from the stack at execution time. Examples (lame ones, admittedly):&nbsp;
+<br>&nbsp;
+<blockquote>
+<pre><b><tt>: -rot&nbsp;&nbsp; ( a b c -- c a b )
+&nbsp;&nbsp;&nbsp; {{ a b c }}</tt></b>&nbsp;
+&nbsp;&nbsp;&nbsp; <b><tt>c a b&nbsp;</tt></b>&nbsp;
+<b><tt>;</tt></b>&nbsp;
+
+<b><tt>: tuck0&nbsp; ( a b c -- 0 a b c )</tt></b>&nbsp;
+<b><tt>&nbsp;&nbsp;&nbsp; {{ a b c -- d }}</tt></b>&nbsp;
+<b><tt>&nbsp;&nbsp;&nbsp; d a b c&nbsp;</tt></b>&nbsp;
+<b><tt>;&nbsp;</tt></b></pre>
+</blockquote>
+</td>
+</tr>
+</table>
+
+</body>
+</html>
diff --git a/doc/ficl_logo.jpg b/doc/ficl_logo.jpg
new file mode 100644
index 000000000000..c3332dbf65a1
--- /dev/null
+++ b/doc/ficl_logo.jpg
Binary files differ
diff --git a/doc/ficl_oop.html b/doc/ficl_oop.html
new file mode 100644
index 000000000000..438eaebdcfb2
--- /dev/null
+++ b/doc/ficl_oop.html
@@ -0,0 +1,1387 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="john sadler">
+ <meta name="Description" content="object oriented programming in the coolest embedded scripting language ever">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (Win98; U) [Netscape]">
+ <title>Ficl Object Oriented Programming</title>
+</head>
+<body>
+
+<h1>
+<b>Object Oriented Programming in ficl</b></h1>
+
+
+<script language="javascript" src="ficlheader.js"></script>
+
+
+<h2>
+Contents</h2>
+
+<ul>
+<li>
+<a href="#objects">Object Oriented Programming in ficl</a></li>
+
+<li>
+<a href="#ootutorial">Ficl OO Tutorial</a></li>
+
+<li>
+<a href="#cstring">Ficl String Classes</a></li>
+
+<li>
+<a href="ficl.html#oopgloss">OOP glossary</a></li>
+
+<li>
+<a href="#glossinstance">Instance variable glossary</a></li>
+
+<li>
+<a href="#glossclass">Class methods glossary</a></li>
+
+<li>
+<a href="#objectgloss"><tt>OBJECT</tt> base class methods glossary</a></li>
+
+<li>
+<a href="#stockclasses">Supplied Classes</a></li>
+</ul>
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h2>
+<a NAME="objects"></a>Object Oriented Programming in ficl</h2>
+
+<h3>
+Review of <a href="http://whatis.techtarget.com/definition/0,289893,sid9_gci212681,00.html">OO</a> ideas</h3>
+Click <a href="oo_in_c.html#review">here</a> for a short review of OO ideas,
+terms, and implementations in other languages, or <a href="http://www.soft-design.com/softinfo/objects.html">here</a>
+for an introduction to the terms and principles of Object Oriented Programming
+<h3>
+Design goals of Ficl OO syntax</h3>
+Ficl's object extensions provide the traditional OO benefits of associating
+data with the code that manipulates it, and reuse through single inheritance.
+Ficl also has some unusual capabilities that support interoperation with
+systems written in C.&nbsp;
+<ul>
+<li>
+Ficl objects are normally late bound for safety (late binding guarantees
+that the appropriate method will always be invoked for a particular object).
+Early binding is also available, provided you know the object's class at
+compile-time.</li>
+
+<li>
+Ficl OOP supports single inheritance, aggregation, and arrays of objects.</li>
+
+<li>
+Classes have independent name spaces for their methods: methods are only
+visible in the context of a class or object. Methods can be overridden
+or added in subclasses; there is no fixed limit on the number of methods
+of a class or subclass.</li>
+
+<li>
+Ficl OOP syntax is regular and unified over classes and objects. In ficl,
+all classes are objects. Class methods include the ability to subclass
+and instantiate.</li>
+
+<li>
+Ficl can adapt legacy data structures with object wrappers. You can model
+a structure in a Ficl class, and create an instance that refers to an address
+in memory that holds an instance of the structure. The <i>ref object</i>
+can then manipulate the structure directly. This lets you wrap data structures
+written and instantiated in C.</li>
+</ul>
+
+<h3>
+Acknowledgements</h3>
+Ficl is not the first Forth to include Object Oriented extensions. Ficl's
+OO syntax owes a debt to the work of John Hayes and Dick Pountain, among
+others. OO Ficl is different from other OO Forths in a few ways, though
+(some things never change). First, unlike several implementations, the
+syntax is documented (<a href="#ootutorial">below</a>) beyond the source
+code. In Ficl's spirit of working with C code, the OO syntax provides means
+to adapt existing data structures. I've tried to make Ficl's OO model simple
+and safe by unifying classes and objects, providing late binding by default,
+and separating namespaces so that methods and regular Forth words are not
+easily confused.&nbsp;</td>
+</tr>
+</table>
+
+<br>&nbsp;
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h3>
+Ficl Object Model</h3>
+All classes in Ficl are derived from the common base class <tt><a href="#objectgloss">OBJECT,</a></tt>
+as shown in the <a href="#figure1">figure</a> below. All classes are instances
+of <tt><a href="#glossclass">METACLASS</a></tt>. This means that classes
+are objects, too. <tt>METACLASS</tt> implements the methods for messages
+sent to classes. Class methods create instances and subclasses, and give
+information about the class. Each class is represented by a data stucture
+of three elements:&nbsp;
+<ul>
+<li>
+The address (named <tt>.CLASS</tt> ) of a parent class, or zero if it's
+a base class (only <tt>OBJECT</tt> and <tt>METACLASS</tt> have this property)</li>
+
+<li>
+The size (named <tt>.SIZE</tt> ) in address units of an instance of the
+class</li>
+
+<li>
+A wordlist ID (named <tt>.WID</tt> ) for the methods of the class</li>
+</ul>
+In the figure below, <tt>METACLASS</tt> and <tt>OBJECT</tt> are real system-supplied
+classes. The others are contrived to illustrate the relationships among
+derived classes, instances, and the two system base classes. The dashed
+line with an arrow at the end indicates that the object/class at the arrow
+end is an instance of the class at the other end. The vertical line with
+a triangle denotes inheritance.&nbsp;
+<p>Note for the curious: <tt>METACLASS</tt> behaves like a class - it responds
+to class messages and has the same properties as any other class. If you
+want to twist your brain in knots, you can think of <tt>METACLASS</tt>
+as an instance of itself.&nbsp;
+<br>&nbsp;</td>
+</tr>
+</table>
+
+<p><a NAME="figure1"></a><img SRC="ficl_oop.jpg" VSPACE=10 height=442 width=652>
+<br>&nbsp;
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h2>
+<a NAME="ootutorial"></a>Ficl OO Syntax Tutorial</h2>
+
+<h3>
+Introduction</h3>
+It's helpful to have some familiarity with Forth and the customary Forth
+stack notation to understand this tutorial. To get started, take a look
+at this <a href="http://www.taygeta.com/forth_intro/stackflo.html">web-based
+Forth tutorial</a>. If you're comfortable with both OO and Forth, you can
+<a href="#ootutorial-finally">jump
+ahead</a>.
+<p>A Ficl <a href="oo_in_c.html#object-def">object</a> associates a <a href="oo_in_c.html#class-def">class</a>
+with an <a href="oo_in_c.html#instance-def">instance</a> (the storage for
+one set of instance variables). This is done explicitly on Ficl's stack,
+in that any Ficl object is represented by a cell pair:&nbsp;
+<blockquote><b><tt>( instance-addr class-addr )</tt></b></blockquote>
+The instance-addr is the address of the object's storage, and the class-addr
+is the address of its class. Whenever a named Ficl object executes (eg.
+when you type its name and press enter at the Ficl prompt), it leaves this
+"signature". All methods by convention expect a class and instance on the
+stack when they execute, too. In many other OO languages, including C++,
+instances contain information about their classes (a <a href="http://www.mvps.org/vbvision/vtable.htm">vtable</a>
+pointer, for example). By making this pairing explicit rather than implicit,
+Ficl can be OO about chunks of data that don't realize that they are objects,
+without sacrificing any robustness for native objects. That means that
+you can use Ficl to write object wrappers for data structures created in
+C or assembly language, as long as you can determine how they're laid out
+in memory.&nbsp;
+<br>Whenever&nbsp; you create an object in Ficl, you specify its class.
+After that, the object always pushes its class and the address of its <a href="http://www.aware.com/Glossary/main.htm#P">payload
+</a>(instance
+variable space) when invoked by name.&nbsp;
+<p>Classes are special kinds of objects that store the methods of their
+instances, the size of an instance's payload, and a parent class pointer.
+Classes themselves are instances of a special base class called <tt>METACLASS</tt>,
+and all classes inherit from class <tt>OBJECT</tt>. This is confusing at
+first, but it means that Ficl has a very simple syntax for constructing
+and using objects. Class methods include subclassing (<tt>SUB</tt>), creating
+initialized and uninitialized instances (<tt>NEW</tt> and <tt>INSTANCE</tt>),
+and creating reference instances (<tt>REF</tt>), described later. Classes
+also have methods for disassembling their methods (<tt>SEE</tt>), identifying
+themselves (<tt>ID</tt>), and listing their pedigree (<tt>PEDIGREE</tt>).
+All objects inherit (from <tt>OBJECT</tt>) methods for initializing instances
+and arrays of instances, for performing array operations, and for getting
+information about themselves.&nbsp;
+<h3>
+Methods and messages</h3>
+Methods are the functions that objects execute in response to messages.
+A message is a request to an object for a behavior that the object supports.
+When it receives a message, the target object looks up a method that performs
+the behavior for its class, and executes it. Any specific message may be
+bound to different methods in different objects, according to class. This
+separation of messages and methods allows objects to behave <a href="http://www.whatis.com/polymorp.htm">polymorphically</a>.
+(In Ficl, methods are words defined in the context of a class, and messages
+are the names of those words.) Ficl classes associate messages with methods
+for their instances (a fancy way of saying that each class owns a wordlist).
+Ficl provides a late-binding operator <b><tt>--></tt></b> that sends messages
+to objects at run-time, and an early-binding operator <b><tt>=></tt></b>
+that compiles a specific class's method. These operators are the only supported
+way to invoke methods. Regular Forth words are not visible to the method-binding
+operators,&nbsp; so there's no chance of confusing a message with a regular
+word of the same name.&nbsp;</td>
+</tr>
+</table>
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h3>
+<a NAME="ootutorial-finally"></a>Tutorial (finally!)</h3>
+This is a tutorial. It works best if you follow along by pasting the examples
+into ficlWin, the Win32 version of Ficl included with the release sources
+(or some other build that includes the OO part of softcore.c). If you're
+not familiar with Forth, please see one of these <a href="#links">references</a>.
+Ficl's OOP words are in vocabulary <tt>OOP</tt>. To put <tt>OOP</tt> in
+the search order and make it the compilation wordlist, type:&nbsp;
+<pre>
+ONLY ( reset to default search order )
+ALSO OOP DEFINITIONS
+</pre>
+(<b>Note for beginners</b>: to see the effect of the commands above, type
+<tt>ORDER</tt>
+after each line. You can repeat the sequence above if you like.)
+<p>To start, we'll work with the two base classes <tt>OBJECT</tt> and <tt>METACLASS</tt>.
+Try this:&nbsp;
+<pre>
+metaclass --> methods
+</pre>
+The line above contains three words. The first is the name of a class,
+so it pushes its signature on the stack. Since all classes are instances
+of <tt>METACLASS</tt>, <tt>METACLASS</tt> behaves as if it is an instance
+of itself (this is the only class with this property). It pushes the same
+address twice: once for the class and once for the payload, since they
+are the same. The next word finds a method in the context of a class and
+executes it. In this case, the name of the method is <tt>methods</tt>.
+Its job is to list all the methods that a class knows. What you get when
+you execute this line is a list of all the class methods Ficl provides.&nbsp;
+<pre>
+object --> sub c-led
+</pre>
+Causes base-class <tt>OBJECT</tt> to derive from itself a new class called
+c-led. Now we'll add some instance variables and methods to the new class...&nbsp;
+<br><b>Note</b>: I like to prefix the names of classes with "c-", and the
+names of member variables with a dot, but this is just a convention. If
+you don't like it, you can pick your own.
+<pre>
+c-byte obj: .state
+: init { 2:this -- }
+ this --> super --> init
+ ." initializing an instance of "
+ this --> class --> id type cr ;
+: on { led# 2:this -- }
+ this --> .state --> get
+ 1 led# lshift or dup !oreg
+ this --> .state --> set ;
+: off { led# 2:this -- }
+ this --> .state --> get
+ 1 led# lshift invert and dup !oreg
+ this --> .state --> set&nbsp; ;
+end-class
+</pre>
+The first line adds an instance variable called <tt>.state</tt> to the
+class. This particular instance variable is an object - it will be an instance
+of c-byte, one of ficl's stock classes (the source for which can be found
+in the distribution in sorftowrds/classes.fr).&nbsp;
+<br>Next we've defined a method called <tt>init</tt>. This line also declares
+a <a href="ficl_loc.html">local variable</a> called <b><tt>this</tt></b>
+(the 2 in front tells Ficl that this is a double-cell local). All methods
+by convention expect the address of the class and instance on top of the
+stack when called.&nbsp; The next three lines define <tt>init</tt>'s behavior.
+It first calls its superclass's version of <tt>init</tt> (which in this
+case is <tt>object => init</tt> - this default implementation clears all
+instance variables). The rest displays some text and causes the instance
+to print its class name (<tt>this --> class --> id</tt>).
+<br>The <b><tt>init</tt></b> method is special for Ficl objects: whenever
+you create an initialized instance using <b><tt>new</tt></b> or <b><tt>new-array</tt></b>,
+Ficl calls the class's <tt>init</tt> method for you on that instance. The
+default <tt>init</tt> method supplied by <tt>object</tt> clears the instance,
+so we didn't really need to override it in this case (see the source code
+in ficl/softwords/oo.fr).&nbsp;
+<br>The <tt>ON</tt> and <tt>OFF</tt> methods defined above hide the details
+of turning LEDs on and off. The interface to FiclWin's simulated hardware
+is handled by <tt>!OREG</tt>. The class keeps the LED state in a shadow
+variable (<tt>.STATE</tt>) so that <tt>ON</tt> and <tt>OFF</tt> can work
+in terms of LED number rather than a bitmask.
+<p>Now make an instance of the new class:&nbsp;
+<pre>
+c-led --> new led
+</pre>
+And try a few things...&nbsp;
+<pre>
+led --> methods
+led --> pedigree
+1 led --> on
+1 led --> off
+</pre>
+Or you could type this with the same effect:&nbsp;
+<pre>
+led 2dup --> methods --> pedigree
+</pre>
+Notice (from the output of <tt>methods</tt>) that we've overridden the
+init method supplied by object, and added two more methods for the member
+variables. If you type <tt>WORDS</tt>, you'll see that these methods are
+not visible outside the context of the class that contains them. The method
+finder <b><tt>--></tt></b> uses the class to look up methods. You can use
+this word in a definition, as we did in <tt>init</tt>, and it performs
+late binding, meaning that the mapping from message (method name) to method
+(the code) is deferred until run-time. To see this, you can decompile the
+init method like this:&nbsp;
+<pre>
+c-led --> see init
+</pre>
+or
+<pre>
+led --> class --> see init
+</pre>
+
+<h3>
+Early binding</h3>
+Ficl also provides early binding if you ask for it. Early binding is not
+as safe as late binding, but it produces code that is more compact and
+efficient because it compiles method addresses rather then their names.
+In the preferred uses of early binding, the class is assumed to be the
+one you're defining. This kind of early binding can only be used inside
+a class definition. Early bound methods still expect to find a class and
+instance cell-pair on top of the stack when they run.
+<br>Here's an example that illustrates a potential problem:
+<pre>
+object --> sub c1
+: m1 { 2:this -- } ." c1's m1" cr ;
+: m2 { 2:this -- } ." Running " this my=> m1 ; ( early )
+: m3 { 2:this -- } ." Running " this --> m1 ( late )
+end-class
+c1 --> sub c2
+: m1 { 2:this -- } ." c2's m1" cr ;
+end-class
+c2 --> new i2
+i2 --> m1 ( runs the m1 defined in c2 )
+i2 --> m2 ( is this what you wanted? )
+i2 --> m3 { runs the overridden m1)
+</pre>
+Even though we overrode method m1 in class c2, the definition of m2 with
+early binding forced the use of m1 as defined in c1. If that's what you
+want, great, but more often you'll want the flexibility of overriding parent
+class behaviors appropriately.&nbsp;
+<ol>
+<li>
+<code>my=></code> binds early to a method in the class being defined,
+as in the example above.
+</li>
+<li>
+<code>my=[ ]</code> binds a sequence of methods in the current class.
+Useful when the class has object members. Lines like <code>this --> state
+--> set</code> in the definition of c-led above can be replaced with
+<code>this my=[ state set ]</code> to get early binding.
+</li>
+<li>
+<code>=></code> (dangerous) pops a class off the stack and compiles
+the method in that class. Since you have to specify the class explicitly,
+there is a real danger that this will be out of sync with the class you
+really wanted. I recommend the <code>my=</code> operations.
+</li>
+</ol>
+Early binding using <code>=></code> is dangerous because it partially
+defeats the data-to-code matching mechanism object oriented languages were
+created to provide, but it does increase run-time speed by binding the
+method at compile time. In many cases, such as the <code>init</code> method,
+you can be reasonably certain of the class of thing you're working on.
+This is also true when invoking class methods, since all classes are instances
+of <code>metaclass</code>. Here's an example from the definition of <code>metaclass</code>
+in oo.fr (don't paste this into ficlWin - it's already there):
+<pre>
+: new \ ( class metaclass "name" -- )
+ metaclass => instance --> init ;
+</pre>
+Try this...
+<pre>
+metaclass --> see new
+</pre>
+Decompiling the method with <code>SEE</code> shows the difference between the
+two strategies. The early bound method is compiled inline, while the late-binding
+operator compiles the method name and code to find and execute it in the
+context of whatever class is supplied on the stack at run-time.
+<br>Notice that the primitive early-binding operator <code>=></code> requires
+a class at compile time. For this reason, classes are <code>IMMEDIATE</code>,
+meaning that they push their signature at compile time or run time. I'd
+recommend that you avoid early binding until you're very comfortable with
+Forth, object-oriented programming, and Ficl's OOP syntax.
+<br>
+<h3>
+More About Instance Variables</h3>
+<i>Untyped</i> instance variable methods (created by <tt>cell: cells: char:</tt>
+and <tt>chars:</tt>) just push the address of the corresponding instance
+variable when invoked on an instance of the class. It's up to you to remember
+the size of the instance variable and manipulate it with the usual Forth
+words for fetching and storing.&nbsp;
+<p>As advertised earlier, Ficl provides ways to objectify existing data
+structures without changing them. Instead, you can create a Ficl class
+that models the structure, and instantiate a <b>ref </b>from this class,
+supplying the address of the structure. After that, the <i>ref instance</i>
+behaves as a Ficl object, but its instance variables take on the values
+in the existing structure. Example (from ficlclass.fr):&nbsp;
+<blockquote><b><tt>object subclass c-wordlist</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; c-wordlist ref: .parent</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; c-ptr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj:
+.name</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; c-cell&nbsp;&nbsp;&nbsp;&nbsp; obj: .size</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; c-word&nbsp;&nbsp;&nbsp;&nbsp; ref: .hash</tt></b>
+<p><b><tt>&nbsp;&nbsp;&nbsp; : ?</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2drop ." ficl wordlist
+" cr ;</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; : push&nbsp; drop&nbsp; >search ;</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; : pop&nbsp;&nbsp; 2drop previous ;</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; : set-current&nbsp;&nbsp; drop set-current
+;</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; : words&nbsp;&nbsp; --> push&nbsp; words
+previous ;</tt></b>
+<br><b><tt>end-class</tt></b></blockquote>
+In this case, <tt>c-wordlist</tt> describes Ficl's wordlist structure;
+named-wid creates a wordlist and binds it to a ref instance of <tt>c-wordlist</tt>.
+The fancy footwork with <tt>POSTPONE</tt> and early binding is required
+because classes are immediate. An equivalent way to define named-wid with
+late binding is:&nbsp;
+<blockquote><b><tt>: named-wid&nbsp;&nbsp; ( "name" -- )</tt></b>
+<br><b><tt>&nbsp;&nbsp;&nbsp; wordlist&nbsp; postpone c-wordlist&nbsp;
+--> ref ;</tt></b></blockquote>
+To do the same thing at run-time (and call it my-wordlist):&nbsp;
+<blockquote><b><tt>wordlist&nbsp; c-wordlist --> ref&nbsp; my-wordlist</tt></b></blockquote>
+Now you can deal with the wordlist through the ref instance:&nbsp;
+<blockquote><b><tt>my-wordlist --> push</tt></b>
+<br><b><tt>my-wordlist --> set-current</tt></b>
+<br><b><tt>order</tt></b></blockquote>
+Ficl can also model linked lists and other structures that contain pointers
+to structures of the same or different types. The class constructor word
+<b><tt><a href="#exampleref:">ref:</a></tt></b>
+makes an aggregate reference to a particular class. See the <a href="#glossinstance">instance
+variable glossary</a> for an <a href="#exampleref:">example</a>.&nbsp;
+<p>Ficl can make arrays of instances, and aggregate arrays into class descripions.
+The <a href="#glossclass">class methods</a> <b><tt>array</tt></b> and <b><tt>new-array</tt></b>
+create uninitialized and initialized arrays, respectively, of a class.
+In order to initialize an array, the class must define (or inherit) a reasonable
+<b><tt>init</tt></b>
+method. <b><tt>New-array</tt></b> invokes it on each member of the array
+in sequence from lowest to highest. Array instances and array members use
+the object methods <b><tt>index</tt></b>, <b><tt>next</tt></b>, and <b><tt>prev</tt></b>
+to navigate. Aggregate a member array of objects using <b><tt><a href="#arraycolon">array:</a></tt></b>.
+The objects are not automatically initialized in this case - your class
+initializer has to call <b><tt>array-init</tt></b> explicitly if you want
+this behavior.&nbsp;
+<p>For further examples of OOP in Ficl, please see the source file ficl/softwords/ficlclass.fr.
+This file wraps several Ficl internal data structures in objects and gives
+use examples.&nbsp;</td>
+</tr>
+
+<tr>
+<td>
+<h2>
+<a NAME="cstring"></a>Ficl String classes</h2>
+c-string (ficl 2.04 and later) is a reasonably useful dynamic string class.
+Source code for the class is located in ficl/softwords/string.fr. Features:
+dynamic creation and resizing; deletion, char cout, concatenation, output,
+comparison; creation from quoted string constant (s").
+<p>Examples of use:
+<blockquote>
+<pre><b>c-string --> new homer
+s" In this house, " homer --> set
+s" we obey the laws of thermodynamics!" homer --> cat
+homer --> type</b></pre>
+</blockquote>
+</td>
+</tr>
+</table>
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h2>
+<a NAME="oopgloss"></a>OOP Glossary</h2>
+Note: with the exception of the binding operators (the first two definitions
+here), all of the words in this section are internal factors that you don't
+need to worry about. These words provide method binding for all classes
+and instances. Also described are supporting words and execution factors.
+All are defined in softwords/oo.fr.&nbsp;
+<dl>
+<dt>
+<b><tt>-->&nbsp;&nbsp; ( instance class "method-name" -- xn )</tt></b></dt>
+
+<dd>
+Late binding: looks up and executes the given method in the context of
+the class on top of the stack.&nbsp;</dd>
+
+<dt>
+<b><tt>c->&nbsp;&nbsp; ( instance class "method-name" -- xn exc )</tt></b></dt>
+
+<dd>
+Late binding with <tt>CATCH</tt>: looks up and <tt>CATCH</tt>es the given
+method in the context of the class on top of the stack, pushes zero or
+exception code upon return.</dd>
+
+<dt>
+<b><tt>my=> comp: ( "method-name" -- )&nbsp; exec: ( inst class -- xn )</tt></b></dt>
+
+<dd>
+Early binding: compiles code to execute the method of the class being defined.
+Only visible and valid in the scope of a <tt>--> sub</tt> .. <tt>end-class</tt>
+class definition.</dd>
+
+<dt>
+<b><tt>my=[ comp: ( "obj1 obj2 .. method ]" -- ) exec:( inst class -- xn
+)</tt></b></dt>
+
+<dd>
+Early binding: compiles code to execute a chain of methods of the class
+being defined. Only visible and valid in the scope of a <tt>--> sub</tt>
+.. <tt>end-class</tt> class definition.</dd>
+
+<dt>
+<b><tt>=>&nbsp;&nbsp; comp: ( class meta "method-name" -- )&nbsp; exec:
+( inst class -- xn )</tt></b></dt>
+
+<dd>
+Early binding: compiles code to execute the method of the class specified
+at compile time.</dd>
+
+<dt>
+<b><tt>do-do-instance</tt></b></dt>
+
+<dd>
+When executed, causes the instance to push its ( instance class ) stack
+signature. Implementation factor of <b><tt>metaclass --> sub</tt></b>.
+Compiles <b><tt>.do-instance</tt></b> in the context of a class; <tt>.do-instance</tt>
+implements the <tt>does></tt> part of a named instance.&nbsp;</dd>
+
+<dt>
+<b><tt>exec-method&nbsp;&nbsp; ( instance class c-addr u -- xn )</tt></b></dt>
+
+<dd>
+Given the address and length of a message (method name) on the stack, finds
+the method in the context of the specified class and invokes it. Upon entry
+to the method, the instance and class are on top of the stack, as usual.
+If unable to find the method, prints an error message and aborts.</dd>
+
+<dt>
+<b><tt>find-method-xt&nbsp;&nbsp; ( class "method-name" -- class xt )</tt></b></dt>
+
+<dd>
+Attempts to map the message to a method in the specified class. If successful,
+leaves the class and the execution token of the method on the stack. Otherwise
+prints an error message and aborts.</dd>
+
+<dt>
+<b><tt>lookup-method&nbsp;&nbsp; ( class c-addr u -- class xt )</tt></b></dt>
+
+<dd>
+Given the address and length of a message (method name) on the stack, finds
+the method in the context of the specified class. If unable to find the
+method, prints an error message and aborts.</dd>
+
+<dt>
+<b><tt>parse-method&nbsp;&nbsp; comp: ( "method-name" -- )&nbsp; exec:
+( -- c-addr u )</tt></b></dt>
+
+<dd>
+Parse "name" from the input stream and compile code to push its length
+and address when the enclosing definition runs.</dd>
+</dl>
+</td>
+</tr>
+</table>
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h3>
+<a NAME="glossinstance"></a>Instance Variable Glossary</h3>
+<b>Note</b>: these words are only visible when creating a subclass! To
+create a subclass, use the <tt>sub</tt> method on <tt>object</tt> or any
+class derived from it (<i>not</i> <tt>metaclass</tt>). Source code for
+Ficl OOP is in ficl/softwords/oo.fr.&nbsp;
+<br>Instance variable words do two things: they create methods that do
+an action appropriate for the type of instance variable they represent,
+and they reserve space in the class template for the instance variable.
+We'll use the term <i>instance variable</i> to refer both to the method
+that gives access to a particular field of an object, and to the field
+itself. Rather than give esentially the same example over and over, here's
+one example that shows several of the instance variable construction words
+in use:
+<blockquote><tt>object subclass c-example</tt>
+<br><tt>&nbsp;&nbsp; cell:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+.cell0</tt>
+<br><tt>&nbsp;&nbsp; c-4byte&nbsp;&nbsp; obj: .nCells</tt>
+<br><tt>&nbsp;4 c-4byte array: .quad</tt>
+<br><tt>&nbsp;&nbsp; char:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+.length</tt>
+<br><tt>79 chars:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name</tt>
+<br><tt>end-class</tt>&nbsp;</blockquote>
+This class only defines instance variables, and it inherits some methods
+from <tt>object</tt>. Each untyped instance variable (.cell0, .length,
+.name) pushes its address when executed. Each object instance variable
+pushes the address and class of the aggregate object. Similar to C, an
+array instance variable leaves its base address (and its class) when executed.
+The word <tt>subclass</tt> is shorthand for "<tt>--> sub</tt>"&nbsp;
+<dl>
+<dt>
+<b><font face="Courier New"><font size=-1>cell:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+( offset "name" -- offset' )</font></font></b></dt>
+
+<dt>
+<b><font face="Courier New"><font size=-1>Execution:&nbsp; ( -- cell-addr
+)</font></font></b></dt>
+
+<dd>
+Create an untyped instance variable one cell wide. The instance variable
+leaves its payload's address when executed.&nbsp;</dd>
+
+<dt>
+<b><tt>cells:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( offset nCells "name"
+-- offset' )</tt></b></dt>
+
+<dt>
+<b><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Execution:&nbsp; ( -- cell-addr )</tt></b></dt>
+
+<dd>
+Create an untyped instance variable n cells wide.</dd>
+
+<dt>
+<b><tt>char:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( offset "name"
+-- offset' )</tt></b></dt>
+
+<dt>
+<b><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Execution:&nbsp; ( -- char-addr )</tt></b></dt>
+
+<dd>
+Create an untyped member variable one char wide</dd>
+
+<dt>
+<b><tt>chars:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( offset nChars "name"
+-- offset' )</tt></b></dt>
+
+<dt>
+<b><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Execution:&nbsp; ( -- char-addr )</tt></b></dt>
+
+<dd>
+Create an untyped member variable n chars wide.</dd>
+
+<dt>
+<b><tt>obj:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( offset class
+meta "name" -- offset' )</tt></b></dt>
+
+<dt>
+<b><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Execution:&nbsp; ( -- instance class )</tt></b></dt>
+
+<dd>
+Aggregate an uninitialized instance of <b>class</b> as a member variable
+of the class under construction.</dd>
+
+<dt>
+<a NAME="arraycolon"></a><b><tt>array:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+( offset n class meta "name" -- offset' )</tt></b></dt>
+
+<dt>
+<b><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Execution:&nbsp; ( -- instance class )</tt></b></dt>
+
+<dd>
+Aggregate an uninitialized array of instances of the class specified as
+a member variable of the class under construction.</dd>
+
+<dt>
+<a NAME="exampleref:"></a><b><tt>ref:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+( offset class meta "name" -- offset' )</tt></b></dt>
+
+<dt>
+<b><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+Execution:&nbsp; ( -- ref-instance ref-class )</tt></b></dt>
+
+<dd>
+Aggregate a reference to a class instance. There is no way to set the value
+of an aggregated ref - it's meant as a way to manipulate existing data
+structures with a Ficl OO model. For example, if your system contains a
+linked list of 4 byte quantities, you can make a class that represents
+a list element like this:&nbsp;</dd>
+
+<dl>
+<dd>
+<tt>object subclass c-4list</tt></dd>
+
+<dd>
+<tt>c-4list ref: .link</tt></dd>
+
+<dd>
+<tt>c-4byte obj: .payload</tt></dd>
+
+<dd>
+<tt>end-class;</tt></dd>
+
+<dd>
+<tt>address-of-existing-list c-4list --> ref mylist</tt></dd>
+</dl>
+
+<dd>
+The last line binds the existing structure to an instance of the class
+we just created. The link method pushes the link value and the class c_4list,
+so that the link looks like an object to Ficl and like a struct to C (it
+doesn't carry any extra baggage for the object model - the Ficl methods
+alone take care of storing the class information).&nbsp;</dd>
+
+<dd>
+Note: Since a ref: aggregate can only support one class, it's good for
+modeling static structures, but not appropriate for polymorphism. If you
+want polymorphism, aggregate a c_ref (see classes.fr for source) into your
+class - it has methods to set and get an object.</dd>
+
+<dd>
+By the way, it is also possible to construct a pair of classes that contain
+aggregate pointers to each other. Here's an example:</dd>
+
+<dl>
+<dd>
+<tt>object subclass akbar</tt></dd>
+
+<dd>
+<tt>suspend-class&nbsp;&nbsp;&nbsp;&nbsp; \ put akbar on hold while we
+define jeff</tt></dd>
+
+<dd>
+<tt>object subclass jeff</tt></dd>
+
+<dd>
+<tt>&nbsp;&nbsp;&nbsp; akbar ref: .significant-other</tt></dd>
+
+<dd>
+<tt>&nbsp;&nbsp;&nbsp; ( your additional methods here )</tt></dd>
+
+<dd>
+<tt>end-class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \ done with
+jeff</tt></dd>
+
+<dd>
+<tt>akbar --> resume-class&nbsp; \ resume defining akbar</tt></dd>
+
+<dd>
+<tt>&nbsp;&nbsp;&nbsp; jeff ref: .significant-other</tt></dd>
+
+<dd>
+<tt>&nbsp;&nbsp;&nbsp; ( your additional methods here )</tt></dd>
+
+<dl><tt>end-class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \ done
+with akbar</tt></dl>
+</dl>
+</dl>
+</td>
+</tr>
+</table>
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h3>
+<a NAME="glossclass"></a>Class Methods Glossary</h3>
+These words are methods of <tt>metaclass</tt>. They define the manipulations
+that can be performed on classes. Methods include various kinds of instantiation,
+programming tools, and access to member variables of classes. Source is
+in softwords/oo.fr.&nbsp;
+<dl>
+<dt>
+<b><tt>instance&nbsp;&nbsp;&nbsp;&nbsp; ( class metaclass "name" -- instance
+class )</tt></b>&nbsp;</dt>
+
+<dd>
+Create an uninitialized instance of the class, giving it the name specified.
+The method leaves the instance 's signature on the stack (handy if you
+want to initialize). Example:</dd>
+
+<dd>
+<tt>c_ref --> instance uninit-ref&nbsp; 2drop</tt></dd>
+
+<dt>
+<b><tt>new&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( class
+metaclass "name" -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Create an initialized instance of class, giving it the name specified.
+This method calls init to perform initialization.&nbsp;</dd>
+
+<dt>
+<b><tt>array&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( nObj class metaclass
+"name" -- nObjs instance class )</tt></b>&nbsp;</dt>
+
+<dd>
+Create an array of nObj instances of the specified class. Instances are
+not initialized. Example:</dd>
+
+<dd>
+<tt>10 c_4byte --> array&nbsp; 40-raw-bytes&nbsp; 2drop drop</tt></dd>
+
+<dt>
+<b><tt>new-array&nbsp;&nbsp;&nbsp; ( nObj class metaclass "name" -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Creates an initialized array of nObj instances of the class. Same syntax
+as <tt>array</tt></dd>
+
+<dt>
+<a NAME="alloc"></a><b><tt>alloc&nbsp;&nbsp; ( class metaclass -- instance
+class )</tt></b></dt>
+
+<dd>
+Creates an anonymous instance of <b>class</b> from the heap (using a call
+to ficlMalloc() to get the memory). Leaves the payload and class addresses
+on the stack. Usage example:</dd>
+
+<dd>
+<tt>c-ref --> alloc&nbsp; 2constant instance-of-ref</tt></dd>
+
+<dd>
+Creates a double-cell constant that pushes the payload and class address
+of a heap instance of c-ref.</dd>
+
+<dt>
+<a NAME="allocarray"></a><b><tt>alloc-array&nbsp;&nbsp; ( nObj class metaclass
+-- instance class )</tt></b></dt>
+
+<dd>
+Same as new-array, but creates anonymous instances from the heap using
+a call to ficlMalloc(). Each instance is initialized using the class's
+<tt>init</tt>
+method</dd>
+
+<dt>
+<a NAME="allot"></a><b><tt>allot&nbsp;&nbsp; ( class metaclass -- instance
+class )</tt></b></dt>
+
+<dd>
+Creates an anonymous instance of <b>class</b> from the dictionary. Leaves
+the payload and class addresses on the stack. Usage example:</dd>
+
+<dd>
+<tt>c-ref --> allot&nbsp; 2constant instance-of-ref</tt></dd>
+
+<dd>
+Creates a double-cell constant that pushes the payload and class address
+of a heap instance of c-ref.</dd>
+
+<dt>
+<a NAME="allotarray"></a><b><tt>allot-array&nbsp;&nbsp; ( nObj class metaclass
+-- instance class )</tt></b></dt>
+
+<dd>
+Same as new-array, but creates anonymous instances from the dictionary.
+Each instance is initialized using the class's
+<tt>init</tt> method</dd>
+
+<dt>
+<b><tt>ref&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance-addr
+class metaclass "name" -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Make a ref instance of the class that points to the supplied instance address.
+No new instance space is allotted. Instead, the instance refers to the
+address supplied on the stack forever afterward. For wrapping existing
+structures.</dd>
+</dl>
+
+<dl>
+<dt>
+<b><tt>sub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( class
+metaclass -- old-wid addr[size] size )</tt></b></dt>
+
+<dd>
+Derive a subclass. You can add or override methods, and add instance variables.
+Alias: <tt>subclass</tt>. Examples:</dd>
+
+<dl>
+<dd>
+<tt>c_4byte --> sub c_special4byte</tt></dd>
+
+<dd>
+<tt>( your new methods and instance variables here )</tt></dd>
+
+<dd>
+<tt>end-class</tt></dd>
+
+<dd>
+or</dd>
+
+<dd>
+<tt>c_4byte subclass c_special4byte</tt></dd>
+
+<dd>
+<tt>( your new methods and instance variables here )</tt></dd>
+
+<dd>
+<tt>end-class</tt></dd>
+</dl>
+
+<dt>
+<b><tt>.size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( class metaclass
+-- instance-size )</tt></b>&nbsp;</dt>
+
+<dd>
+Returns address of the class's instance size field, in address units. This
+is a metaclass member variable.</dd>
+
+<dt>
+<b><tt>.super&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( class metaclass --
+superclass )</tt></b>&nbsp;</dt>
+
+<dd>
+Returns address of the class's superclass field. This is a metaclass member
+variable.</dd>
+
+<dt>
+<b><tt>.wid&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( class metaclass
+-- wid )</tt></b>&nbsp;</dt>
+
+<dd>
+Returns the address of the class's wordlist ID field. This is a metaclass
+member variable.</dd>
+
+<dt>
+<b><tt>get-size</tt></b></dt>
+
+<dd>
+Returns the size of an instance of the class in address units. Imeplemented
+as</dd>
+
+<dd>
+<tt>: get-size&nbsp;&nbsp; metaclass => .size @ ;</tt></dd>
+
+<dt>
+<b><tt>get-wid</tt></b></dt>
+
+<dd>
+Returns the wordlist ID of the class. Implemented as&nbsp;</dd>
+
+<dd>
+<tt>: get-wid&nbsp;&nbsp; metaclass => .wid @ ;</tt></dd>
+
+<dt>
+<b><tt>get-super</tt></b></dt>
+
+<dd>
+Returns the class's superclass. Implemented as</dd>
+
+<dd>
+<tt>: get-super&nbsp;&nbsp; metaclass => .super @ ;</tt></dd>
+
+<dt>
+<b><tt>id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (
+class metaclass -- c-addr u )</tt></b>&nbsp;</dt>
+
+<dd>
+Returns the address and length of a string that names the class.</dd>
+
+<dt>
+<b><tt>methods&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( class metaclass -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Lists methods of the class and all its superclasses</dd>
+
+<dt>
+<b><tt>offset-of&nbsp;&nbsp;&nbsp; ( class metaclass "name" -- offset )</tt></b></dt>
+
+<dd>
+Pushes the offset from the instance base address of the named member variable.
+If the name is not that of an instance variable method, you get garbage.
+There is presently no way to detect this error. Example:</dd>
+
+<dl>
+<dd>
+<tt>metaclass --> offset-of .wid</tt></dd>
+</dl>
+
+<dt>
+<b><tt>pedigree&nbsp;&nbsp;&nbsp;&nbsp; ( class metaclass -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Lists the pedigree of the class (inheritance trail)</dd>
+
+<dt>
+<b><tt>see&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( class
+metaclass "name" -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Decompiles the specified method - obect version of <tt>SEE</tt>, from the
+<tt>TOOLS</tt>
+wordset.</dd>
+</dl>
+</td>
+</tr>
+</table>
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h3>
+<a NAME="objectgloss"></a><tt>object</tt> base-class Methods Glossary</h3>
+These are methods that are defined for all instances by the base class
+<tt>object</tt>.
+The methods include default initialization, array manipulations, aliases
+of class methods, upcasting, and programming tools.&nbsp;
+<dl>
+<dt>
+<b><tt>init&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance
+class -- )</tt>&nbsp;</b></dt>
+
+<dd>
+Default initializer called automatically for all instances created with
+<tt>new</tt>
+or <tt>new-array</tt>. Zero-fills the instance. You do not normally need
+to invoke <tt>init</tt> explicitly.</dd>
+
+<dt>
+<b><tt>array-init&nbsp;&nbsp; ( nObj instance class -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Applies <tt>init</tt> to an array of objects created by <tt>new-array</tt>.
+Note that <tt>array:</tt> does not cause aggregate arrays to be initialized
+automatically. You do not normally need to invoke <tt>array-init</tt> explicitly.</dd>
+
+<dt>
+<a NAME="oofree"></a><b><tt>free&nbsp;&nbsp; ( instance class -- )</tt></b></dt>
+
+<dd>
+Releases memory used by an instance previously created with <tt>alloc</tt>
+or <tt>alloc-array</tt>. Note - this method is not presently protected
+against accidentally deleting something from the dictionary. If you do
+this, Bad Things are likely to happen. Be careful for the moment to apply
+free only to instances created with <tt>alloc</tt> or <tt>alloc-array</tt>.</dd>
+
+<dt>
+<b><tt>class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance class
+-- class metaclass )</tt></b>&nbsp;</dt>
+
+<dd>
+Convert an object signature into that of its class. Useful for calling
+class methods that have no object aliases.</dd>
+
+<dt>
+<b><tt>super&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance class
+-- instance parent-class )</tt></b>&nbsp;</dt>
+
+<dd>
+Upcast an object to its parent class. The parent class of <tt>object</tt>
+is zero. Useful for invoking an overridden parent class method.</dd>
+
+<dt>
+<b><tt>pedigree&nbsp;&nbsp;&nbsp;&nbsp; ( instance class -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Display an object's pedigree - its chain of inheritance. This is an alias
+for the corresponding class method.</dd>
+
+<dt>
+<b><tt>size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance
+class -- sizeof(instance) )</tt></b>&nbsp;</dt>
+
+<dd>
+Returns the size, in address units, of one instance. Does not know about
+arrays! This is an alias for the class method <tt>get-size</tt></dd>
+
+<dt>
+<b><tt>methods&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance class -- )</tt></b>&nbsp;</dt>
+
+<dd>
+Class method alias. Displays the list of methods of the class and all superclasses
+of the instance.</dd>
+
+<dt>
+<b><tt>index&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( n instance class
+-- instance[n] class )</tt></b>&nbsp;</dt>
+
+<dd>
+Convert array-of-objects base signature into signature for array element
+n. No check for bounds overflow. Index is zero-based, like C, so&nbsp;</dd>
+
+<dl>
+<dd>
+<tt>0 my-obj --> index</tt>&nbsp;</dd>
+</dl>
+
+<dd>
+is equivalent to&nbsp;</dd>
+
+<dl>
+<dd>
+<tt>my-obj</tt></dd>
+</dl>
+
+<dd>
+Check out the <a href="#minusrot">description of <tt>-ROT</tt></a> for
+help in dealing with indices on the stack.</dd>
+
+<dt>
+<b><tt>next&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance[n]
+class -- instance[n+1] class )</tt></b>&nbsp;</dt>
+
+<dd>
+Convert an array-object signature&nbsp; into the signature of the next
+object in the array. No check for bounds overflow.</dd>
+
+<dt>
+<b><tt>prev&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( instance[n]
+class -- instance[n-1] class )</tt></b>&nbsp;</dt>
+
+<br>Convert an object signature into the signature of the previous object
+in the array. No check for bounds underflow.</dl>
+</td>
+</tr>
+</table>
+
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+<h3>
+<a NAME="stockclasses"></a>Supplied Classes (See classes.fr)</h3>
+
+<dl>
+<dt>
+<b><tt>metaclass&nbsp;</tt></b></dt>
+
+<dd>
+Describes all classes of Ficl. Contains class methods. Should never be
+directly instantiated or subclassed. Defined in oo.fr. Methods described
+above.</dd>
+
+<dt>
+<b><tt>object</tt>&nbsp;</b></dt>
+
+<dd>
+Mother of all Ficl objects. Defines default initialization and array indexing
+methods. Defined in oo.fr. Methods described above.</dd>
+
+<dt>
+<b><tt>c-ref</tt>&nbsp;</b></dt>
+
+<dd>
+Holds the signature of another object. Aggregate one of these into a data
+structure or container class to get polymorphic behavior. Methods &amp;
+members:&nbsp;</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; ( inst class -- ref-inst ref-class )</tt></dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( ref-inst ref-class inst class -- )</tt></dd>
+
+<dd>
+<tt>.instance&nbsp;&nbsp; ( inst class -- a-addr ) </tt>cell member that
+holds the instance</dd>
+
+<dd>
+<tt>.class&nbsp;&nbsp; ( inst class -- a-addr ) </tt>cell member that holds
+the class</dd>
+
+<dt>
+<b><tt>c-byte&nbsp;</tt></b></dt>
+
+<dd>
+Primitive class derived from <tt>object</tt>, with a 1-byte payload. Set
+and get methods perform correct width fetch and store. Methods &amp; members:</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; ( inst class -- c )</tt></dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( c inst class -- )</tt></dd>
+
+<dd>
+<tt>.payload&nbsp;&nbsp; ( inst class -- addr ) </tt>member holds instance's
+value</dd>
+
+<dt>
+<b><tt>c-2byte</tt></b>&nbsp;</dt>
+
+<dd>
+Primitive class derived from <tt>object</tt>, with a 2-byte payload. Set
+and get methods perform correct width fetch and store. Methods &amp; members:</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; ( inst class -- 2byte )</tt></dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( 2byte inst class -- )</tt></dd>
+
+<dd>
+<tt>.payload&nbsp;&nbsp; ( inst class -- addr ) </tt>member holds instance's
+value</dd>
+
+<dt>
+<b><tt>c-4byte</tt></b>&nbsp;</dt>
+
+<dd>
+Primitive class derived from <tt>object</tt>, with a 4-byte payload. Set
+and get methods perform correct width fetch and store. Methods &amp; members:</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; ( inst class -- x )</tt></dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( x inst class -- )</tt></dd>
+
+<dd>
+<tt>.payload&nbsp;&nbsp; ( inst class -- addr ) </tt>member holds instance's
+value</dd>
+
+<dt>
+<b><tt>c-cell</tt></b>&nbsp;</dt>
+
+<dd>
+Primitive class derived from <tt>object</tt>, with a cell payload (equivalent
+to c-4byte in 32 bit implementations, 64 bits wide on Alpha). Set and get
+methods perform correct width fetch and store. Methods &amp; members:</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; ( inst class -- x )</tt></dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( x inst class -- )</tt></dd>
+
+<dd>
+<tt>.payload&nbsp;&nbsp; ( inst class -- addr ) </tt>member holds instance's
+value</dd>
+
+<dt>
+<b><tt>c-ptr</tt></b></dt>
+
+<dd>
+Base class derived from <tt>object</tt> for pointers to non-object types.
+This class is not complete by itself: several methods depend on a derived
+class definition of <tt>@size</tt>. Methods &amp; members:</dd>
+
+<dd>
+<tt>.addr&nbsp;&nbsp; ( inst class -- a-addr )</tt> member variable - holds
+the pointer address</dd>
+
+<dd>
+<tt>get-ptr&nbsp;&nbsp; ( inst class -- ptr )</tt></dd>
+
+<dd>
+<tt>set-ptr&nbsp;&nbsp; ( ptr inst class -- )</tt></dd>
+
+<dd>
+<tt>inc-ptr&nbsp;&nbsp; ( inst class -- )</tt> Adds @size to pointer address</dd>
+
+<dd>
+<tt>dec-ptr&nbsp;&nbsp; ( inst class -- )</tt> Subtracts @size from pointer
+address</dd>
+
+<dd>
+<tt>index-ptr&nbsp;&nbsp; ( i inst class -- )</tt> Adds i*@size to pointer
+address</dd>
+
+<dt>
+<b><tt>c-bytePtr</tt></b></dt>
+
+<dd>
+Pointer to byte derived from c-ptr. Methods &amp; members:</dd>
+
+<dd>
+<tt>@size&nbsp;&nbsp; ( inst class -- size )</tt> Push size of the pointed-to
+thing</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; (&nbsp; inst class -- c ) </tt>Fetch the pointer's
+referent byte</dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( c inst class -- ) </tt>Store c at the pointer address</dd>
+
+<dt>
+<b><tt>c-2bytePtr</tt></b></dt>
+
+<dd>
+Pointer to double byte derived from c-ptr. Methods &amp; members:</dd>
+
+<dd>
+<tt>@size&nbsp;&nbsp; ( inst class -- size )</tt> Push size of the pointed-to
+thing</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; (&nbsp; inst class -- x ) </tt>Fetch the pointer's
+referent 2byte</dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( x inst class -- )</tt> Store 2byte x at the pointer
+address</dd>
+
+<dt>
+<b><tt>c-4bytePtr</tt></b></dt>
+
+<dd>
+Pointer to quad-byte derived from c-ptr. Methods &amp; members:</dd>
+
+<dd>
+<tt>@size&nbsp;&nbsp; ( inst class -- size )</tt> Push size of the pointed-to
+thing</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; (&nbsp; inst class -- x ) </tt>Fetch the pointer's
+referent 2byte</dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( x inst class -- )</tt> Store 2byte x at the pointer
+address</dd>
+
+<dt>
+<b><tt>c-cellPtr</tt></b></dt>
+
+<dd>
+Pointer to cell derived from c-ptr. Methods &amp; members:</dd>
+
+<dd>
+<tt>@size&nbsp;&nbsp; ( inst class -- size )</tt> Push size of the pointed-to
+thing</dd>
+
+<dd>
+<tt>get&nbsp;&nbsp; (&nbsp; inst class -- x ) </tt>Fetch the pointer's
+referent cell</dd>
+
+<dd>
+<tt>set&nbsp;&nbsp; ( x inst class -- )</tt> Storex at the pointer address</dd>
+
+<dt>
+<b><tt>c-string</tt></b>&nbsp; (see string.fr)</dt>
+
+<dd>
+Dynamically allocated string similar to MFC CString (Partial list of methods
+follows)</dd>
+
+<dd>
+<font face="Courier New"><font size=-1>set ( c-addr u 2:this -- ) </font></font><font size=+0>Initialize
+buffer to the specified string</font></dd>
+
+<dd>
+<font face="Courier New"><font size=-1>get ( 2:this -- c-addr u ) Return
+buffer contents as counted string</font></font></dd>
+
+<dd>
+<font face="Courier New"><font size=-1>cat ( c-addr u 2:this -- ) Append
+given string to end of buffer</font></font></dd>
+
+<dd>
+<font face="Courier New"><font size=-1>compare ( 2string 2:this -- n ) Return
+result of lexical compare</font></font></dd>
+
+<dd>
+<font face="Courier New"><font size=-1>type ( 2:this -- ) Print buffer to
+the output stream</font></font></dd>
+
+<dd>
+<font face="Courier New"><font size=-1>hashcode ( 2:this -- x ) Return hashcode
+of string (as in dictionary)</font></font></dd>
+
+<dd>
+<font face="Courier New"><font size=-1>free ( 2:this -- ) Release internal
+buffer</font></font></dd>
+
+<dt>
+<b><tt>c-hashstring</tt>&nbsp; </b>(see string.fr)</dt>
+
+<dd>
+Derived from c-string. This class adds a hashcode member variable.</dd>
+</dl>
+</td>
+</tr>
+</table>
+
+</body>
+</html>
diff --git a/doc/ficl_oop.jpg b/doc/ficl_oop.jpg
new file mode 100644
index 000000000000..b4aee1021a98
--- /dev/null
+++ b/doc/ficl_oop.jpg
Binary files differ
diff --git a/doc/ficl_parse.html b/doc/ficl_parse.html
new file mode 100644
index 000000000000..a90607778f0e
--- /dev/null
+++ b/doc/ficl_parse.html
@@ -0,0 +1,197 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="john sadler">
+ <meta name="Description" content="the coolest embedded scripting language ever">
+ <title>Ficl Parse Steps</title>
+</head>
+<body>
+<link REL="SHORTCUT ICON" href="ficl.ico">
+<table BORDER=0 CELLSPACING=3 COLS=1 WIDTH="675" ><tr><td>
+<h1>Ficl Parse Steps</h1>
+<script language="javascript" src="ficlheader.js"></script>
+
+<h2>Overview</h2>
+<p>
+Ficl 2.05 and later includes an extensible parser chain. Ficl feeds every incoming token
+(chunk of text with no internal whitespace) to each step in the parse chain in turn. The
+first parse step that successfully matches the token applies semantics to it and returns
+a TRUE flag, ending the sequence. If all parse steps fire without a match, ficl prints
+an error message and resets the virtual machine. Parse steps can be written in precompiled
+code, or in ficl itself, and can be appended to the chain at run-time if you like.
+</p>
+<p>
+More detail:
+</p>
+<ul>
+<li>
+If compiling and local variable support is enabled, attempt to find the token in the local
+variable dictionary. If found, execute the token's compilation semantics and return
+</li>
+<li>
+Attempt to find the token in the system dictionary. If found, execute the token's semantics
+(may be different when compiling than when interpreting) and return
+</li>
+<li>
+If prefix support is enabled (Compile-time constant FICL_WANT_PREFIX in sysdep.h is non-zero),
+attempt to match the beginning of the token to the list of known prefixes. If there's a match,
+execute the associated prefix method.
+</li>
+<li>
+Attempt to convert the token to a number in the present <code>BASE</code>. If successful, push the
+value onto the stack if interpreting, compile it if compiling. Return
+</li>
+<li>
+All previous parse steps failed to recognize the token. Print "<token> not found" and abort
+</li>
+</ul>
+You can add steps to the parse chain, and you can add prefixes.
+<h2>Adding Parse Steps</h2>
+You can add a parse step in two ways. The first is to write a ficl word that
+has the correct stack signature for a parse step:
+<pre>
+my-parse-step ( c-addr u -- ??? flag )
+</pre>
+Where <code>c-addr u</code> are the address and length of the incoming token,
+and <code>flag</code> is <code>true</code> if the parse step recognizes the token
+and <code>false</code> otherwise.
+<br>
+Install the parse step using <code>add-parse-step</code>.
+A trivial example:
+<pre>
+: ?silly ( c-addr u -- flag )
+ ." Oh no! Not another " type cr true ;
+' ?silly add-parse-step
+parse-order
+</pre>
+<p>
+The other way to add a parse step is by writing it in C, and inserting it into the
+parse chain with:
+</p>
+<pre>
+void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep);
+</pre>
+Where <code>name</code> is the display name of the parse step in the parse chain (as revealed
+by <code>parse-order</code>). Parameter pStep is a pointer to the code for the parse step itself,
+and must match the following declaration:
+<pre>
+typedef int (*FICL_PARSE_STEP)(FICL_VM *pVM, STRINGINFO si);
+</pre>
+<p>
+Upon entry to the parse step, <code>si</code> points to the incoming token. The parse step
+must return <code>FICL_TRUE</code> if it succeeds in matching the token, and
+<code>FICL_TRUE</code> otherwise. If it succeeds in matching a token, the parse step
+applies semantics to it before returning. See <code>ficlParseNumber()</code> in words.c for
+an example.
+</p>
+
+<h2>Adding Prefixes</h2>
+<p>
+What's a prefix, anyway? A prefix (contributed by Larry Hastings) is a token that's
+recognized as the beginning of another token. Its presence modifies the semantics of
+the rest of the token. An example is <code>0x</code>, which causes digits following
+it to be converted to hex regardless of the current value of <code>BASE</code>.
+</p><p>
+Caveat: Prefixes are matched in sequence, so the more of them there are,
+the slower the interpreter gets. On the other hand, because the prefix parse step occurs
+immediately after the dictionary lookup step, if you have a prefix for a particular purpose,
+using it may save time since it stops the parse process.
+</p><p>
+Each prefix is a ficl word stored in a special wordlist called <code>&lt;prefixes&gt;</code>. When the
+prefix parse step (<code>?prefix</code> AKA ficlParsePrefix()) fires, it searches each word
+in <code>&lt;prefixes&gt;</code> in turn, comparing it with the initial characters of the incoming
+token. If a prefix matches, the parse step returns the remainder of the token to the input stream
+and executes the code associated with the prefix. This code can be anything you like, but it would
+typically do something with the remainder of the token. If the prefix code does not consume the
+rest of the token, it will go through the parse process again (which may be what you want).
+</p><p>
+Prefixes are defined in prefix.c and in softwords/prefix.fr. The easiest way to add a new prefix is
+to insert it into prefix.fr and rebuild the system. You can also add prefixes interactively
+by bracketing prefix definitions as follows (see prefix,fr):
+</p>
+<pre>
+start-prefixes ( defined in prefix.fr )
+\ make dot-paren a prefix (create an alias for it in the prefixes list)
+: .( .( ;
+: 0b 2 __tempbase ; immediate
+end-prefixes
+</pre>
+<p>
+The precompiled word <code>__tempbase</code> is a helper for prefixes that specify a
+temporary value of <code>BASE</code>.
+</p><p>
+Constant <code>FICL_EXTENDED_PREFIX</code> controls the inclusion of a bunch of additional
+prefix definitions. This is turned off in the default build since several of these prefixes
+alter standard behavior, but you might like them.
+</p>
+
+<h2>Notes</h2>
+<p>
+Prefixes and parser extensions are non-standard, although with the exception of prefix support,
+ficl's default parse order follows the standard. Inserting parse steps in some other order
+will almost certainly break standard behavior.
+</p>
+<p>
+The number of parse steps that can be added to the system is limited by the value of
+<code>FICL_MAX_PARSE_STEPS</code> (defined in sysdep.h unless you define it first), which defaults
+to 8. More parse steps means slower average interpret and compile performance,
+so be sparing. Same applies to the number of prefixes defined for the system, since each one
+has to be matched in turn before it can be proven that no prefix matches. On the other hand,
+if prefixes are defined, use them when possible: since they are matched early in the parse order,
+a prefix match short circuits the parse process, saving time relative to
+(for example) using a number builder parse step at the end of the parse chain.
+</p>
+<p>
+Compile time constant <code>FICL_EXTENDED_PREFIX</code> enables several more prefix
+definitions in prefix.c and prefix.fr. Please note that this will slow average compile and
+interpret speed in most cases.
+</p>
+<h2>Parser Glossary</h2>
+<dl>
+<dt><b><code>parse-order ( -- )</code></b></dt>
+<dd>
+Prints the list of parse steps in the order in which they are evaluated.
+Each step is the name of a ficl word with the following signature:
+<pre>
+parse-step ( c-addr u -- ??? flag )
+</pre>
+A parse step consumes a counted string (the incoming token) from the stack,
+and exits leaving a flag on top of the stack (it may also leave other parameters as side effects).
+The flag is true if the parse step succeeded at recognizing the token, false otherwise.
+</dd>
+<dt><b><code>add-parse-step ( xt -- )</code></b></dt>
+<dd>
+Appends a parse step to the parse chain. XT is the adress (execution token) of a ficl
+word to use as the parse step. The word must have the following signature:
+<pre>
+parse-step ( c-addr u -- ??? flag )
+</pre>
+A parse step consumes a counted string (the incoming token) from the stack,
+and exits leaving a flag on top of the stack (it may also leave other parameters as side effects).
+The flag is true if the parse step succeeded at recognizing the token, false otherwise.
+</dd>
+<dt><b><code>show-prefixes ( -- )</code></b></dt>
+<dd>
+Defined in <code>softwords/prefix.fr</code>.
+Prints the list of all prefixes. Each prefix is a ficl word that is executed if its name
+is found at the beginning of a token. See <code>softwords/prefix.fr</code> and <code>prefix.c</code> for examples.
+</dd>
+<dt><b><code>start-prefixes ( -- )</code></b></dt>
+<dd>
+Defined in <code>softwords/prefix.fr</code>.
+Declares the beginning of one or more prefix definitions (it just switches the compile wordlist
+to <code>&lt;prefixes&gt;</code>
+</dd>
+<dt><b><code>end-prefixes ( -- )</code></b></dt>
+<dd>
+Defined in <code>softwords/prefix.fr</code>.
+Restores the compilation wordlist that was in effect before the last invocation of
+<code>start-prefixes</code>. Note: the prior wordlist ID is stored in a Ficl variable, so
+attempts to nest <code>start-prefixes end-prefixes</code> blocks wil result in mildly silly
+side effects.
+</dd>
+</dl>
+</td></tr></table>
+</body>
+</html> \ No newline at end of file
diff --git a/doc/ficl_rel.html b/doc/ficl_rel.html
new file mode 100644
index 000000000000..e3c499813ca6
--- /dev/null
+++ b/doc/ficl_rel.html
@@ -0,0 +1,804 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="john sadler">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (Win98; U) [Netscape]">
+ <title>Ficl Release Notes</title>
+</head>
+<body>
+
+<h1>
+<b>Ficl Release Notes</b>
+</h1>
+
+
+<script language="javascript" src="ficlheader.js"></script>
+
+
+<br>&nbsp;
+<table BORDER=0 CELLPADDING=3 COLS=1 WIDTH="675" >
+<tr>
+<td>
+
+<h2><a NAME="whatsnew">Version 3.03</a></h2>
+<ul>
+<li>
+Bugfix: Compiled floating-point numbers now work. (Floats in compiled code were simply broken in 3.02 and some previous version.)
+</li>
+<li>
+Newly implemented CORE EXT words: <code>CASE</code>, <code>OF</code>, <code>ENDOF</code>, and <code>ENDCASE</code>. Also added <code>FALLTHROUGH</code>, which works like <code>ENDOF</code> but jumps to the instruction just after the next <code>OF</code>.
+</li>
+<li>
+New words: <code>random</code>, <code>seed-random</code>, and <code>(lookup-method)</code>
+</li>
+<li>
+Bugfix: <code>included</code> never closed its file.
+</li>
+<li>
+Bugfix: <code>include</code> was not <code>IMMEDIATE</code>.
+</li>
+<li>
+Bugfix: John-Hopkins locals syntax now accepts <code>|</code> and <code>--</code> in the comment (between the first <code>--</code> and the <code>}</code>.)
+</li>
+<li>
+Un-hid the OO words <code>parse-method</code>, <code>lookup-method</code>, and <code>find-method-xt</code>, as there are perfectly legitimate reasons why you might want to use them.
+</li>
+<li>
+Changed the prefix version of <code>.(</code> to be <code>IMMEDIATE</code> too.
+</li>
+<li>
+Bugfix: Changed <code>vmGetWord0()</code> to make Purify happier. The resulting code is no slower, no larger, and slightly more robust.
+</li>
+<li>
+Got rid of the <code>/objects/common/ficl</code> nonsense in the VC++ project. Build-time files are now stored their default places, in relative directories called <code>debug</code> and <code>release</code>.
+</li>
+<li>
+Fixed comment in Python softcore builder.
+</li>
+<li>
+Put the <b>doc</b> directory back in to the distribution. (It was missing from 3.02... where'd it go?)
+</li>
+</ul>
+
+
+
+<h2>Version 3.02</h2>
+<ul>
+<li>
+Added support for <code>nEnvCells</code> (number of environment cells) to <code>FICL_SYSTEM_INFO</code>.
+</li>
+<li>
+Consolidated <code>context</code> and <code>pExtend</code> pointers of <code>FICL_SYSTEM</code>&#151;VM's <code>pExtend</code> pointer is initialized from the copy in <code>FICL_SYSTEM</code> upon VM creation.
+</li>
+<li>
+Added <code>ficl-robust</code> environment variable.
+</li>
+<li>
+Added <code>FW_ISOBJECT</code> word type.
+</li>
+<li>
+Bugfix: <code>environment?</code> was ignoring the length of the supplied string.
+</li>
+<li>
+Portability cleanup in fileaccess.c.
+</li>
+<li>
+Bugfix in <code>ficlParsePrefix</code>: if the prefix dictionary isn't in the wordlist, the word being examined cannot be a prefix, so return failure.
+</li>
+<li>
+<code>SEE</code> improvements: <code>SEE</code> (and consequently <code>DEBUG</code>) have improved source listings with instruction offsets.
+</li>
+<li>
+It's turned off with the preprocessor, but we have the beginnings of a switch-threaded implementation of the inner loop.
+</li>
+<li>
+Added <code>objectify</code> and <code>?object</code> for use by OO infrastructure.
+</li>
+<li>
+<code>my=[</code> detects object members (using <code>?object</code>) and assumes all other members leave class unchanged.
+</li>
+<li>
+Removed <code>MEMORY-EXT</code> environment variable (there is no such wordset).
+</li>
+<li>
+Ficlwin changes:
+<ul>
+<li>
+Ficlwin character handling is more robust
+</li>
+<li>
+Ficlwin uses multi-system constructs (see ficlthread.c)
+</li>
+</ul>
+</li>
+<li>
+Documentation changes:
+<ul>
+<li>
+Corrected various bugs in docs.
+</li>
+<li>
+Added ficl-ized version of JV Noble's Forth Primer
+</li>
+<li>
+Ficl OO tutorial expanded and revised. Thanks to David McNab for his demo and suggestions.
+</li>
+</ul>
+</li>
+
+</ul>
+
+<h2>Version 3.01</h2>
+<ul>
+<li>
+Major contribs by Larry Hastings (larry@hastings.org):
+<ul>
+<li>
+FILE wordset (fileaccess.c)
+</li>
+<li>
+ficlEvaluate wrapper for ficlExec
+</li>
+<li>
+ficlInitSystemEx makes it possible to bind selectable properties to VMs at create time
+</li>
+<li>
+Python version of softcore builder ficl/softwords/softcore.py
+</li>
+</ul>
+</li>
+<li>
+Environment contains ficl-version (double)
+</li>
+<li>
+?number handles trailing decimal point per DOUBLE wordset spec
+</li>
+<li>
+Fixed broken .env (thanks to Leonid Rosin for spotting this goof)
+</li>
+<li>
+Fixed broken floating point words that depended on evaluation order of stack pops.
+</li>
+<li>
+env-constant
+</li>
+<li>
+env-2constant
+</li>
+<li>
+dictHashSummary is now commented out unless FICL_WANT_FLOAT (thanks to Leonid Rosin again)
+</li>
+<li>
+Thanks to David McNab for pointing out that .( should be IMMEDIATE. Now it is.
+</li>
+</ul>
+
+<h2>Version 3.00a</h2>
+
+<ul>
+<li>
+Fixed broken oo.fr by commenting out vcall stuff using FICL_WANT_VCALL. Vcall is still broken.
+</li>
+</ul>
+
+<h2>Version 3.00</h2>
+
+<ul>
+<li>
+Added pSys parameter to most ficlXXXX functions for multiple system support. Affected functions:
+<ul>
+<li>dictLookupLoc renamed to ficlLookupLoc after addition of pSys param</li>
+<li>ficlInitSystem returns a FICL_SYSTEM*</li>
+<li>ficlTermSystem</li>
+<li>ficlNewVM</li>
+<li>ficlLookup</li>
+<li>ficlGetDict</li>
+<li>ficlGetEnv</li>
+<li>ficlSetEnv</li>
+<li>ficlSetEnvD</li>
+<li>ficlGetLoc</li>
+<li>ficlBuild</li>
+</ul>
+</li>
+
+<li>Fixed off-by-one bug in ficlParsePrefix</li>
+<li>Ficl parse-steps now work correctly - mods to interpret()</li>
+<li>Made tools.c:isAFiclWord more selective</li>
+<li>Tweaked makefiles and code to make gcc happy under linux</li>
+<li>Vetted all instances of LVALUEtoCELL to make sure they're working on CELL sized operands
+(for 64 bit compatibility)</li>
+</ul>
+
+<h2>Version 2.06</h2>
+<ul>
+<li>Debugger changes:</li>
+<ul>
+<li>New debugger command "x" to execute the rest of the command line as ficl</li>
+<li>New debugger command "l" lists the source of the innermost word being debugged</li>
+<li>If you attempt to debug a primitive, it gets executed rather than doing nothing</li>
+<li><code>R.S</code> displays the stack contents symbolically</li>
+<li>Debugger now operates correctly under ficlwin, although ficlwin's key handling leaves a lot to be desired. </li>
+<li><code>SEE</code> listing enhanced for use with the debugger</li>
+</ul>
+<li>Added Guy Carver's changes to oo.fr for VTABLE support</li>
+<li><code>float.c</code> words f&gt; and &gt;f to move floats to and from the param stack, analogous to &gt;r and r&gt;</li>
+<li><code>LOOKUP</code> - Surrogate precompiled parse step for ficlParseWord (this step is hard
+ coded in <code>INTERPRET</code>)</li>
+<li>License text at top of source files changed from LGPL to BSD by request</li>
+<li>Win32 console version now handles exceptions more gracefully rather than crashing - uses win32
+structured exception handling.</li>
+<li>Fixed BASE bug from 2.05 (was returning the value rather than the address) </li>
+<li>Fixed ALLOT bug - feeds address units to dictCheck, which expects Cells. Changed dictCheck
+to expect AU. </li>
+<li>Float stack display word renamed to f.s from .f to be consistent with r.s and .s</li>
+</ul>
+
+<h2>Version 2.05</h2>
+<h3>General</h3>
+
+<ul>
+<li>HTML documentation extensively revised</li>
+<li>Incorporated Alpha (64 bit) patches from the freeBSD team.</li>
+<li>Split SEARCH and SEARCH EXT words from words.c to search.c</li>
+<li><a href="ficl_loc.html">2LOCALS</a> defined in <a href="ficl_loc.html#jhlocal">Johns Hopkins local syntax</a> now lose the first '2:' in their names.</li>
+<li>Simple step <a href="ficl_debug.html">debugger</a> (see tools.c)</li>
+<li>The text interpreter is now extensible - this is accomplished through the use
+of <code>ficlAddParseStep()</code>. <code>FICL_MAX_PARSE_STEPS</code> limits the number of parse steps
+(default: 8). You can write a precompiled parse step (see <code>ficlParseNumber</code>) and
+append it to the chain, or you can write one in ficl and use <code>ADD-PARSE-STEP</code>
+to append it. Default parse steps are initialized in <code>ficlInitSystem</code>. You can list
+the parse steps with <code>parse-order ( -- )</code>.</li>
+<li>There is now a FICL_SYSTEM structure. This is a transitional release - version 3.0
+will alter several API prototypes to take this as a parameter, allowing multiple
+systems per process (and therefore multiple dictionaries). For those who use ficl
+under a virtual memory O/S like Linux or Win NT, you can just create multiple ficl
+processes (not threads) instead and save youself the wait.</li>
+<li>Fixes for improved command line operation in testmain.c (Larry Hastings)</li>
+<li>Numerous extensions to OO facility, including a new allot methods, ability
+to catch method invocations (thanks to Daniel Sobral again)</li>
+<li>Incorporated Alpha (64 bit) patches contributed by Daniel Sobral and the freeBSD team
+Ficl is now 64 bit friendly! UNS32 is now FICL_UNS.</li>
+<li>Split SEARCH and SEARCH EXT words from words.c to search.c</li>
+<li>ABORT" now complies with the ANS (-2 THROWs)</li>
+<li>Floating point support contributed by Guy Carver (Enable FICL_WANT_FLOAT in sysdep.h).</li>
+<li>Win32 vtable model for objects (Guy Carver)</li>
+<li>Win32 dll load/call suport (Larry Hastings)</li>
+<li>Prefix support (Larry Hastings) (prefix.c prefix.fr FICL_EXTENDED_PREFIX) makes it
+easy to extend the parser to recignize prefixes like 0x and act on them. Use show-prefixes
+to see what's defined.</li>
+<li>Cleaned up initialization sequence so that it's all in ficlInitSystem, and so that
+a VM can be created successfully before the dictionary is created</li>
+</ul>
+
+<h3>
+Bug fixes</h3>
+
+<ul>
+<li>
+<a href="http://www.taygeta.com/forth/dpans9.htm#9.6.2.0680">ABORT"</a>
+now works correctly (I promise!)</li>
+
+<li>
+<a href="http://www.taygeta.com/forth/dpans6.htm#6.2.2125">REFILL</a> works
+better</li>
+
+<li>
+<a href="http://www.taygeta.com/forth/dpans6.htm#6.1.0710">ALLOT</a>'s
+use of dictCheck corrected (finally)</li>
+</ul>
+
+<h3>
+New words</h3>
+
+<ul>
+<li>
+<a href="http://www.taygeta.com/forth/dpans6.htm#6.2.0415">2r@</a> <a href="http://www.taygeta.com/forth/dpans6.htm#6.2.0410">2r></a> <a href="http://www.taygeta.com/forth/dpans6.htm#6.2.0340">2>r</a>
+(CORE EXT)</li>
+
+<li>
+<a href="http://www.taygeta.com/forth/dpans8.htm#8.6.1.0440">2VARIABLE</a>
+(DOUBLE)</li>
+
+<li>
+<a href="http://www.taygeta.com/forth/dpans16.htm#16.6.2.1985">ORDER</a>
+now lists wordlists by name</li>
+
+<li>
+<a href="http://www.taygeta.com/forth/dpans15.htm#15.6.1.0220">.S</a> now
+displays all stack entries on one line, like a stack comment</li>
+
+<li>
+<a href="ficl.html#wid-get-name"><tt>wid-get-name</tt>&nbsp;</a>&nbsp;
+given a wid, returns the address and count of its name. If no name, count
+is 0</li>
+
+<li>
+<tt><a href="ficl.html#wid-set-name">wid-set-name</a></tt>&nbsp;
+set optional wid name pointer to the \0 terminated string address specified.</li>
+
+<li>
+<tt><a href="ficl.html#ficlwordlist">ficl-named-wordlist</a></tt> creates
+a ficl-wordlist and names it. This is now used in <tt>vocabulary</tt> and
+<tt><a href="ficl.html#ficlvocabulary">ficl-vocabulary</a></tt>&nbsp;</li>
+
+<li>
+<tt><a href="ficl.html#last-word">last-word</a></tt>&nbsp; returns the
+xt of the word being defined or most recently defined.</li>
+
+<li>
+<tt><a href="ficl.html#qfetch">q@</a></tt> and <tt><a href="ficl.html#qbang">q!</a></tt>
+operate on quadbyte quantities for 64 bit friendliness</li>
+</ul>
+
+<h3>
+New OO stuff</h3>
+
+<ul>
+<li>
+<tt>ALLOT (class method)</tt></li>
+
+<li>
+<tt>ALLOT-ARRAY (class method)</tt></li>
+
+<li>
+<tt>METHOD</tt> define method names globally</li>
+
+<li>
+<tt>MY=></tt> early bind a method call to "this" class</li>
+
+<li>
+<tt>MY=[ ]</tt> early bind a string of method calls to "this" class and
+obj members</li>
+
+<li>
+<tt>C-></tt> late bind method invocation with CATCH</li>
+
+<li>
+Metaclass method <tt>resume-class</tt> and instance word <tt>suspend-class</tt>
+create mutually referring classes. Example in string.fr</li>
+
+<li>
+Early binding words are now in the instance-vars wordlist, not visible
+unless defining a class.</li>
+
+<li>Support for refs to classes with VTABLE methods (contributed by Guy Carver). Guy writes:
+<p>
+My next favorite change is a set of VCALL words that allow me
+to call C++ class virtual methods from my forth classes. This
+is accomplished by interfacing with the VTABLE of the class. The
+class instance currently must be created on the C++ side.
+C++ places methods in the VTABLE in order of declaration in the
+header file. To use this in FICL one only needs to ensure
+that the VCALL: declerations occur in the same order. I use this
+quite a bit to interface with the C++ classes. When I need access
+to a method I make sure it is virtual (Even if it ultimately will
+not be). I use Visual C++ 6.0 and have not tested this under
+any other compiler but I believe VTABLE implementation is standard.
+</p><p>
+Here is an example of how to use VCALL:
+</p>
+<b>C++ class declaration</b>
+<pre>
+class myclass
+{
+public:
+ myclass();
+ virtual ~myclass();
+ virtual void Test( int iParam1 );
+ virtual int Test( int iParam1, char cParam2 );
+ virtual float Test();
+};
+</pre>
+<b>ficl class declaration</b>
+<pre>
+object subclass myfclass hasvtable \ hasvtable adds 4 to the offset to
+ \ accommodate for the VTABLE pointer.
+0 VCALL: Destructor() \ VCALL: ( ParamCount -<MethodName>- )
+1 VCALL: Test(int) \ Test takes 1 int parameter.
+2 VCALLR: iTest(int,char) \ iTest takes 2 parameters and returns an int.
+0 VCALLF: fTest() \ fTest takes no parameters and returns a float.
+end-class
+
+MyCAddress \ Primitive to return a pointer to a "myclass" instance.
+myfclass -> ref dude \ This makes the MyCAddress pointer a myfclass
+ \ instance with the name "dude".
+1234 dude -> Test(int) \ Calls the virtual method Test.
+1234 1 dude -> iTest(int,char) . \ Calls iTest and emits the returned int.
+dude -> fTest() f. \ Calls fTest and emits the returned float.
+</pre>
+</li>
+</ul>
+
+<h2>
+Version 2.04</h2>
+
+<h3>
+ficlwin</h3>
+
+<ul>
+<li>
+Catches exceptions thrown by VM in ficlThread (0 @ for example) rather
+than passing them off to the OS.&nbsp;</li>
+</ul>
+
+<h3>
+ficl bugs vanquished</h3>
+
+<ul>
+<li>
+Fixed leading delimiter bugs in s" ." .( and ( (reported by Reuben Thomas)</li>
+
+<li>
+Makefile tabs restored (thanks to Michael Somos)</li>
+
+<li>
+ABORT" now throws -2 per the DPANS (thanks to Daniel Sobral for sharp eyes
+again)&nbsp;</li>
+
+<li>
+ficlExec does not print the prompt string unless (source-id == 0)</li>
+
+<li>
+Various fixes contributed by the FreeBSD team.</li>
+</ul>
+
+<h3>
+ficl enhancements</h3>
+
+<ul>
+<li>
+Words.c: modified ficlCatch to use vmExecute and vmInnerLoop (request of
+Daniel Sobral) Added vmPop and vmPush functions (by request of Lars Krueger
+) in vm.c These are shortcuts to the param stack. (Use LVALUEtoCELL to
+get things into CELL form)&nbsp;</li>
+
+<li>
+Added function vmGetStringEx with a flag to specify whether or not to skip
+lead delimiters</li>
+
+<li>
+Added non-std word: number?</li>
+
+<li>
+Added CORE EXT word AGAIN (by request of Reuben Thomas)&nbsp;</li>
+
+<li>
+Added double cell local (2local) support</li>
+
+<li>
+Augmented Johns Hopkins local syntax so that locals whose names begin with
+char 2 are treated as 2locals (OK - it's goofy, but handy for OOP)</li>
+
+<li>
+C-string class revised and enhanced - now dynamically sized</li>
+
+<li>
+C-hashstring class derived from c-string computes hashcode too.</li>
+</ul>
+</td>
+</tr>
+
+<tr>
+<td>
+<h2>
+Version 2.03</h2>
+This is the first version of Ficl that includes contributed code. Thanks
+especially to Daniel Sobral, Michael Gauland for contributions and bug
+finding.&nbsp;
+<p>New words&nbsp;
+<ul>
+<li>
+<tt><a href="#clock">clock</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(FICL)</tt></li>
+
+<li>
+<tt><a href="#clockspersec">clocks/sec</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(FICL)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans8.htm#8.6.1.1230">dnegate</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(DOUBLE)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans10.htm#10.6.2.1905">ms</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(FACILITY EXT - replaces MSEC <i>ficlWin only</i>)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans9.htm#9.6.1.2275">throw</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(EXCEPTION)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans9.htm#9.6.1.0875">catch</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(EXCEPTION)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans14.htm#14.6.1.0707">allocate</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(MEMORY)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans14.htm#14.6.1.1605">free</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(MEMORY)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans14.htm#14.6.1.2145">resize</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(MEMORY)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans6.htm#6.2.2440">within</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(CORE EXT)</tt></li>
+
+<li>
+<tt><a href="#alloc">alloc</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(class method)</tt></li>
+
+<li>
+<tt><a href="#allocarray">alloc-array</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(class method)</tt></li>
+
+<li>
+<tt><a href="#oofree">free</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(class method)</tt></li>
+</ul>
+Bugs Fixed&nbsp;
+<ul>
+<li>
+Bug fix in isNumber(): used to treat chars between 'Z' and 'a' as valid
+in base 10... (harmless, but weird)</li>
+
+<li>
+ficlExec pushes the <i>ip</i> and <tt>interpret</tt>s at the right times
+so that nested calls to ficlExec behave the way you'd expect them to.</li>
+
+<li>
+<tt>evaluate</tt> respects count parameter, and also passes exceptional
+return conditions back out to the calling instance of ficlExec.</li>
+
+<li>
+VM_QUIT now clears the locals dictionary in ficlExec.</li>
+</ul>
+Ficlwin Enhancements&nbsp;
+<ul>
+<li>
+File Menu: recent file list and Open now load files.</li>
+
+<li>
+Text ouput function is now faster through use of string caching. Cache
+flushes at the end of each line and each time ficlExec returns.</li>
+
+<li>
+Edit/paste now behaves more reasonably for text. File/open loads the specified
+file.</li>
+
+<li>
+Registry entries specify dictionary and stack sizes, default window placement,
+and whether or not to create a splitter for multiple VMs. See HKEY_CURRENT_USER/Software/CodeLab/ficlwin/Settings</li>
+</ul>
+Ficl Enhancements&nbsp;
+<ul>
+<li>
+This version includes changes to make it <b>64 bit friendly</b>. This unfortunately
+meant that I had to tweak some core data types and structures. I've tried
+to make this transparent to 32 bit code, but a couple of things got renamed.
+INT64 is now DPINT. UNS64 is now DPUNS. FICL_INT and FICL_UNS are synonyms
+for INT32 and UNS32 in 32 bit versions, but a are obsolescent. Please use
+the new data types instead. Typed stack operations on INT32 and UNS32 have
+been renamed because they operate on CELL scalar types, which are 64 bits
+wide on 64 bit systems. Added BITS_PER_CELL, which has legal values of
+32 or 64. Default is 32.</li>
+
+<li>
+ficl.c: Added ficlExecXT() - executes an xt completely before returning,
+passing back any exception codes generated in the process. Normal exit
+code is VM_INNEREXIT.</li>
+
+<li>
+ficl.c: Added ficlExecC() to operate on counted strings as opposed to zero
+terminated ones.</li>
+
+<li>
+ficlExec pushes ip and executes interpret at the right times so that nested
+calls to ficlExec behave the way you'd expect them to.</li>
+
+<li>
+ficlSetStackSize() allows specification of stack size at run-time (affects
+subsequent invocations of ficlNewVM()).</li>
+
+<li>
+vm.c: vmThrow() checks for (pVM->pState != NULL) before longjmping it.
+vmCreate nulls this pointer initially.&nbsp;</li>
+
+<li>
+EXCEPTION wordset contributed by Daniel Sobral of FreeBSD</li>
+
+<li>
+MEMORY-ALLOC wordset contributed by Daniel Sobral, too. Added class methods
+<tt>alloc</tt>
+and <tt>alloc-array</tt> in softwords/oo.fr to allocate objects from the
+heap.</li>
+
+<li>
+Control structure match check upgraded (thanks to Daniel Sobral for this
+suggestion). Control structure mismatches are now errors, not warnings,
+since the check accepts all syntactally legal constructs.</li>
+
+<li>
+Added vmInnerLoop() to vm.h. This function/macro factors the inner&nbsp;
+interpreter out of ficlExec so it can be used in other places. Function/macro
+behavior is conditioned on INLINE_INNER_LOOP in sysdep.h. Default: 1 unless
+_DEBUG is set. In part, this is because VC++ 5 goes apoplectic when trying
+to compile it as a function. See&nbsp;</li>
+
+<br>comments in vm.c
+<li>
+EVALUATE respects the count parameter, and also passes exceptional return
+conditions back out to the calling instance of ficlExec.</li>
+
+<li>
+VM_QUIT clears locals dictionary in ficlExec()</li>
+
+<li>
+Added Michael Gauland's ficlLongMul and ficlLongDiv and support routines
+to math64.c and .h. These routines are coded in C, and are compiled only
+if PORTABLE_LONGMULDIV == 1 (default is 0).</li>
+
+<li>
+Added definition of ficlRealloc to sysdep.c (needed for memory allocation
+wordset). If your target OS supports realloc(), you'll probably want to
+redefine ficlRealloc in those terms. The default version does ficlFree
+followed by ficlMalloc.</li>
+
+<li>
+testmain.c: Changed gets() in testmain to fgets() to appease the security
+gods.</li>
+
+<li>
+testmain: <tt>msec</tt> renamed to <tt><a href="#ficlms">ms</a></tt> in
+line with the ANS</li>
+
+<li>
+softcore.pl now removes comments &amp; spaces at the start and end of lines.
+As a result: sizeof (softWords) == 7663 bytes (used to be 20000)&nbsp;
+and consumes 11384 bytes of dictionary when compiled</li>
+
+<li>
+Deleted license paste-o in readme.txt (oops).</li>
+</ul>
+</td>
+</tr>
+
+<tr>
+<td>
+<h2>
+Version 2.02</h2>
+New words&nbsp;
+<ul>
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans6.htm#6.2.1850">marker</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(CORE EXT)</tt></li>
+
+<li>
+<tt><a href="http://www.taygeta.com/forth/dpans15.htm#15.6.2.1580">forget</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(TOOLS EXT)</tt></li>
+
+<li>
+<tt><a href="#ficlforgetwid">forget-wid</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(FICL)</tt></li>
+
+<li>
+<tt><a href="#ficlwordlist">ficl-wordlist</a>&nbsp;&nbsp;&nbsp;&nbsp; (FICL)</tt></li>
+
+<li>
+<tt><a href="#ficlvocabulary">ficl-vocabulary</a>&nbsp;&nbsp; (FICL)</tt></li>
+
+<li>
+<tt><a href="#ficlhide">hide</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(FICL)</tt></li>
+
+<li>
+<tt><a href="#ficlhidden">hidden</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+(FICL)</tt></li>
+
+<li>
+<a href="#jhlocal">Johns Hopkins local variable syntax</a> (as best I can
+determine)</li>
+</ul>
+Bugs Fixed&nbsp;
+<ul>
+<li>
+<tt>forget</tt> now adjusts the dictionary pointer to remove the name of
+the word being forgotten (name chars come before the word header in ficl's
+dictionary)</li>
+
+<li>
+<tt>:noname</tt> used to push the colon control marker and its execution
+token in the wrong order</li>
+
+<li>
+<tt>source-id</tt> now behaves correctly when loading a file.</li>
+
+<li>
+<tt>refill</tt> returns zero at EOF (Win32 load). Win32 <tt><a href="#ficlload">load</a></tt>
+command continues to be misnamed. Really ought to be called <tt>included</tt>,
+but does not exactly conform to that spec either (because <tt>included</tt>
+expects a string signature on the stack, while Ficl's <tt><a href="#ficlload">load</a></tt>
+expects a filename upon invocation). The "real" <tt>LOAD</tt> is a <tt>BLOCK</tt>
+word.</li>
+</ul>
+Enhancements (IMHO)&nbsp;
+<ul>
+<li>
+dictUnsmudge no longer links anonymous definitions into the dictionary</li>
+
+<li>
+<tt>oop</tt> is no longer the default compile wordlist at startup, nor
+is it in the search order. Execute <b><tt>also oop definitions</tt></b>
+to use Ficl OOP.</li>
+
+<li>
+Revised oo.fr extensively to make more use of early binding</li>
+
+<li>
+Added <tt>meta</tt> - a constant that pushes the address of metaclass.
+See oo.fr for examples of use.</li>
+
+<li>
+Added classes: <tt>c-ptr&nbsp; c-bytePtr&nbsp; c-2bytePtr&nbsp; c-cellPtr
+</tt>These
+classes model pointers to non-object data, but each knows the size of its
+referent.</li>
+</ul>
+</td>
+</tr>
+
+<tr>
+<td>
+<h2>
+Version 2.01</h2>
+
+<ul>
+<li>
+Bug fix: <tt>(local)</tt> used to leave a value on the stack between the
+first and last locals declared. This value is now stored in a static.</li>
+
+<li>
+Added new local syntax with parameter re-ordering. <a href="#newlocal">See
+description below</a>. (No longer compiled in version 2.02, in favor of
+the Johns Hopkins syntax)</li>
+</ul>
+</td>
+</tr>
+
+<tr>
+<td>
+<h2>
+Version 2.0</h2>
+
+<ul>
+<li>
+New ANS Forth words: <tt>TOOLS</tt> and part of <tt>TOOLS EXT, SEARCH</tt>
+and <tt>SEARCH EXT, LOCALS</tt> and <tt>LOCALS EXT</tt> word sets, additional
+words from <tt>CORE EXT, DOUBLE</tt>, and <tt>STRING</tt>. (See the function
+ficlCompileCore in words.c for an alphabetical list by word set).</li>
+
+<li>
+Simple <tt>USER</tt> variable support - a user variable is a virtual machine
+instance variable. User variables behave as <tt>VARIABLE</tt>s in all other
+respects.</li>
+
+<li>
+Object oriented syntax extensions (see below)</li>
+
+<li>
+Optional stack underflow and overflow checking in many CORE words (enabled
+when FICL_ROBUST >= 2)</li>
+
+<li>
+Various bug fixes</li>
+</ul>
+</td>
+</tr>
+</table>
+
+</body>
+</html>
diff --git a/doc/ficl_top.jpg b/doc/ficl_top.jpg
new file mode 100644
index 000000000000..f206d7131b91
--- /dev/null
+++ b/doc/ficl_top.jpg
Binary files differ
diff --git a/doc/ficlddj.PDF b/doc/ficlddj.PDF
new file mode 100644
index 000000000000..f058dbe4166a
--- /dev/null
+++ b/doc/ficlddj.PDF
Binary files differ
diff --git a/doc/ficlheader.js b/doc/ficlheader.js
new file mode 100644
index 000000000000..56ff63529e2b
--- /dev/null
+++ b/doc/ficlheader.js
@@ -0,0 +1,19 @@
+function ficlHeader()
+{
+ document.write("<table BORDER=0 CELLSPACING=3 WIDTH='640' %>");
+ document.write("<tr %>");
+ document.write("<td %> <b %>Forth Inspired Command Language</b %></td %>");
+ document.write("<td ROWSPAN='4' %><a href='http://ficl.sourceforge.net' %><img SRC='ficl_logo.jpg' border='0' alt='The ficl home page' height=64 width=64 %></a %></td %>");
+ document.write("<td ROWSPAN='4' %><A href='http://sourceforge.net' %><IMG src='http://sourceforge.net/sflogo.php?group_id=24441' width='88' height='31' border='0' alt='Sourceforge Logo' %></A %></td %>");
+ document.write("<td ROWSPAN='4' %><a href='http://www.links2go.net/topic/Forth' %><img SRC='skey.gif' ALT='Key Resources -- Forth' BORDER=0 height=81 width=81 %></a %></td %>");
+ document.write("</tr %>");
+ document.write("<tr %><td %><b %>Author: <a href='mailto:john_sadler@alum.mit.edu' %> John Sadler</a %></b %></td %></tr %>");
+ document.write("<tr %><td %><b %>Created: 19 July 1997</b %></td %></tr %>");
+ document.write("<tr %><td %><b %>Current Revision: 3.00a -- July 2001</b %></td %></tr %>");
+ document.write("<tr %><td %><b %>Last Modified " + document.lastModified + "</b %></td %></tr %>");
+ document.write("<tr %><td COLSPAN=4 %><b %><a href='index.html' %>Home</a %>&nbsp;|&nbsp;<a href='http://sourceforge.net/mail/?group_id=24441' %>Join Mailing Lists</a %>&nbsp;|&nbsp;<a href='http://sourceforge.net/projects/ficl' %>Project Page</a %>&nbsp|&nbsp;<a href='http://sourceforge.net/project/showfiles.php?group_id=24441' %>Download</a %></b %></td %></tr %>");
+ document.write("</table %>");
+}
+
+ficlHeader();
+
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 000000000000..850acfd0cd13
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+ <HEAD>
+ <META name="Author" content="john sadler">
+ <META name="Description" content="Ficl - embedded scripting with object oriented programming">
+ <META name="Keywords" content="scripting prototyping tcl OOP Forth interpreter C">
+<STYLE>
+ H1 {font: Arial; color: navy}
+ H2 {font: Arial; color: navy}
+ LI {font: Arial}
+</STYLE>
+ <LINK rel="SHORTCUT ICON" href="ficl.ico">
+ <TITLE>
+ Ficl - Embedded Scripting - Index
+ </TITLE>
+ </HEAD>
+ <BODY>
+ <H1>
+ <B>Ficl Documentation</B>
+ </H1>
+<SCRIPT language="javascript" src="ficlheader.js" type="text/javascript">
+</SCRIPT>
+ <TABLE summary="table of contents" border="0" cellspacing="3" cols="2" width="675">
+ <TR>
+ <TD width="500">
+ <H2>
+ Contents
+ </H2>
+ <UL>
+ <LI>
+ <A href="ficl_rel.html">Release notes</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#whatis">What is ficl?</A>
+ </LI>
+ <LI>
+ <A href="http://sourceforge.net/projects/ficl">Ficl project page on Sourceforge</A>
+ </LI>
+ <LI>
+ <A href="http://sourceforge.net/project/showfiles.php?group_id=24441">Download</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#links">Tutorials and References</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#includesficl">Ficl Inside!</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#lawyerbait">Disclaimer &mp; License</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#features">Ficl features</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#porting">Porting</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#api">Application Programming Interface</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#manifest">Distribution source files</A>
+ </LI>
+ <LI>
+ <A href="ficl_loc.html">Local variables</A>
+ </LI>
+ <LI>
+ <A href="ficl_oop.html">Object Oriented Programming in ficl</A>
+ </LI>
+ <LI>
+ <A href="ficl_debug.html">Ficl Debugger</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#extras">Ficl extras</A>
+ <UL>
+ <LI>
+ <A href="ficl.html#exnumber">Number syntax</A>
+ </LI>
+ <LI>
+ <A href="ficl_parse.html">Parser extensions and prefix support</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#exsearch">Search order words</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#exuser">User variables</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#exmisc">Miscellaneous useful words</A>
+ </LI>
+ <LI>
+ <A href="ficl.html#exficlwin">FiclWin words</A>
+ </LI>
+ </UL>
+ </LI>
+ <LI>
+ <A href="ficl.html#ansinfo">ANS required information</A>
+ </LI>
+ </UL>
+ </TD>
+ <TD width="175">
+ <A href="http://nav.webring.yahoo.com/hub?ring=forth&mp;list"><IMG src="http://www.iidbs.com/images/4ring.gif" width="155" height="140" border="0" alt="Forth Webring Logo"></A>
+ <CENTER>
+ <FONT size="3"><BR>
+ <A href="http://www.webring.org/cgi-bin/webring?ring=forth;id=47;prev5">Previous 5 Sites</A><BR>
+ <A href="http://www.webring.org/cgi-bin/webring?ring=forth;id=47;prev">Previous</A><BR>
+ <A href="http://www.webring.org/cgi-bin/webring?ring=forth;id=47;next">Next</A><BR>
+ <A href="http://www.webring.org/cgi-bin/webring?ring=forth;id=47;next5">Next 5 Sites</A><BR>
+ <A href="http://www.webring.org/cgi-bin/webring?ring=forth;random">Random Site</A><BR>
+ <A href="http://www.webring.org/cgi-bin/webring?ring=forth;list">List Sites</A></FONT>
+ </CENTER>
+ </TD>
+ </TR>
+ </TABLE>
+ </BODY>
+</HTML>
+
diff --git a/doc/jwsforml.PDF b/doc/jwsforml.PDF
new file mode 100644
index 000000000000..b7c8a3d11250
--- /dev/null
+++ b/doc/jwsforml.PDF
Binary files differ
diff --git a/doc/oo_in_c.html b/doc/oo_in_c.html
new file mode 100644
index 000000000000..b483eb37b369
--- /dev/null
+++ b/doc/oo_in_c.html
@@ -0,0 +1,223 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Object Oriented Idioms in C</title>
+</head>
+<body>
+
+<h1>
+<b><font face="Arial"><font size=+1>Object Oriented Idioms in C</font></font></b></h1>
+<font face="Arial"><font size=-1>John Sadler</font></font>
+<br><font face="Arial"><font size=-1>10 Aug 98</font></font>
+<br>&nbsp;
+<br>&nbsp;
+<table COLS=1 WIDTH="675" >
+<tr>
+<td><a NAME="review"></a><b><font face="Arial"><font size=+1>Review of
+Key OO Characteristics</font></font></b>
+<h3>
+<b><i><font face="Arial">Object</font></i></b></h3>
+<a NAME="object-def"></a><font face="Times New Roman,Times"><b>"A package
+of information and descriptions of its manipulation"</b> [<a href="#robson">Robson</a>]</font>
+<p><font face="Times New Roman,Times">Objects separate interface from implementation,
+"what" is wanted (on the outside) from "how" it is accomplished (on the
+inside). This idea of hiding the data inside a package with a fixed set
+of allowed manipulations is called encapsulation. Why? If the object always
+selects how to perform a requested manipulation, you guarantee that the
+procedure and the data it operates on always match.</font>
+<p><font face="Times New Roman,Times">A <b>message</b> denotes an operation
+that can be performed on an <b>object</b>. The code that describes how
+to perform an operation on a specific object type is called a <b>method</b>.
+From the outside, objects receive messages. On the inside, these messages
+are mapped to methods that perform appropriate actions for the specific
+kind of object. An object’s <b>interface</b> is the set of messages to
+which it can respond. For example, several object types may have a "dump"
+method to cause the object to display its state. Each kind of object will
+need a unique method to accomplish this. So the message-method idea separates
+the interface from the implementation. The idea that different kinds of
+objects might invoke different methods to respond to the same message is
+called <b>polymorphism</b>. Methods can be bound to messages as soon as
+the type of the object receiving the message is known (called <b>early
+binding</b>), or this mapping can wait until run-time (<b>late binding</b>).</font>
+<p><font face="Times New Roman,Times">Some languages (notably C++) make
+a syntactic distinction between early and late binding of methods to messages
+(virtual functions are bound late, while all others are bound early – at
+link time). Smalltalk makes no such distinction. The C++ approach has potentially
+dangerous consequences when you manipulate an object through a pointer
+to its parent class. If a method of the superclass is virtual, you get
+late binding to the appropriate function for the object’s class. On the
+other hand, if that method is not virtual, you get early binding to the
+parent class’s definition even if the child class has overridden it. Smalltalk
+adheres rigorously to the idea that the object itself has sole ownership
+of the mapping from methods to messages.</font>
+<br>&nbsp;</td>
+</tr>
+
+<tr>
+<td>
+<h3>
+<b><i><font face="Arial">Class</font></i></b></h3>
+<a NAME="class-def"></a><b><font face="Times New Roman,Times">"A description
+of one or more similar objects"</font></b> [<a href="#robson">Robson</a>]
+<p><a NAME="instance-def"></a>A specific object described by a particular
+class is called an <b>instance</b> of the class.
+<br>(Example: Dog is a class; Poodle is a subclass of Dog; FiFi is an object,
+an instance of Poodle).
+<br>A class is a kind of object that describes the behaviors (methods)
+of its instances, and whose methods provide for creation, initialization,
+and destruction of an instance. All instances of a particular class use
+the same method to respond to a given message. Classes may define other
+members that are shared by all instances of the class. These are called
+class variables. (In C++, these would be static members.)
+<br>&nbsp;</td>
+</tr>
+
+<tr>
+<td>
+<h3>
+<b><i><font face="Arial">Inheritance</font></i></b></h3>
+<font face="Times New Roman,Times">A means for creating a new object or
+class using an existing one as a starting place and defining only what
+changes. C++ only supports class-based inheritance, but some systems also
+allow objects to inherit directly from other objects. At minimum, inheritance
+requires that the child class override its parent’s name. In addition,
+a child class can:</font>
+<ul>
+<li>
+<font face="Times New Roman,Times">add instance variables</font></li>
+
+<li>
+<font face="Times New Roman,Times">add class variables</font></li>
+
+<li>
+<font face="Times New Roman,Times">define methods for new messages</font></li>
+
+<li>
+<font face="Times New Roman,Times">provide methods for (<b>override</b>)
+messages already handled by the parent class</font></li>
+</ul>
+</td>
+</tr>
+
+<tr>
+<td>
+<h3>
+<b><font face="Arial"><font size=+1>What C++ Does for you…</font></font></b></h3>
+<b><i><font face="Arial">Name mangling – separation of name spaces</font></i></b>
+<p><font face="Times New Roman,Times">This mechanism – decorating symbol
+names with extra characters that uniquely identify their class and prototype
+– is the traditional C++ method for operator overloading, method overloading,
+and namespace separation. C provides a much smaller set of namespaces than
+C++ requires, so this strategy allowed C++ to be implemented as a preprocessor
+for C compilers originally.</font>
+<p><b><i><font face="Arial">Rigorous type checking</font></i></b>
+<p>Because of the function prototype requirement and name mangling, a C++
+compiler can provide strict type checking for method invocations.
+<p><b><i><font face="Arial">Automatic lifetime control</font></i></b>
+<p>Guarantees constructor call upon creation and destructor call upon deletion
+<p><b><i><font face="Arial">Multiple Storage classes (same as C)</font></i></b>
+<p>Automatic, static, dynamically allocated
+<p><b><i><font face="Arial">Typed dynamic memory management</font></i></b>
+<p><b><i><font face="Arial">Default constructor, destructor, copy constructor,
+and assignment operator</font></i></b>
+<p><b><i><font face="Arial">Explicit early and late binding support</font></i></b>
+<p><b><i><font face="Arial">And more…</font></i></b>
+<br>&nbsp;</td>
+</tr>
+
+<tr>
+<td>
+<h3>
+<b><font face="Arial"><font size=+1>What other OO languages do (or don’t
+do)</font></font></b></h3>
+Just to make sure we don’t get caught in a C++ centered view of the world,
+here are some ways other OO systems differ.
+<p><b><i><font face="Arial">Run-time type identification</font></i></b>
+<p>This is a Big Deal in C++, but it’s relatively trivial in an interpreted
+language to know the type of a reference at run-time.
+<p><b><i><font face="Arial">Metaclasses</font></i></b>
+<p>Classes are objects, too (Smalltalk, Java?)
+<p><b><i><font face="Arial">Garbage collection</font></i></b>
+<p>Java and smalltalk both manage memory for you, freeing objects when
+they go out of scope or are no longer referenced anywhere.
+<p><b><i><font face="Arial">Single Inheritance only</font></i></b>
+<p>Java, Smalltalk 80
+<p><b><i><font face="Arial">Everything is an object</font></i></b>
+<p>Smalltalk
+<p><b><i><font face="Arial">Operator overloading</font></i></b>
+<p>Smalltalk makes no distinction between operators and other kinds of
+messages. The message syntax is flexible enough that you can define operators
+in the same way as any other kind of message.
+<p><b><i><font face="Arial">Visibility control</font></i></b>
+<p>Smalltalk 80 does not appear to provide options for visibility control
+(based on my quick survey). Instance variables are always private, methods
+are always public as far as I can tell.
+<p><b><i><font face="Arial">Pointers</font></i></b>
+<p>Not in Smalltalk, Java: Both languages deal with objects through implicit
+references. It is still possible to create data structures, but the language
+hides much of the memory management work.
+<p><b><i><font face="Arial">No Casting</font></i></b>
+<p>As far as I can tell, smalltalk has no equivalent of a C/C++ cast.
+<p><b><i><font face="Arial">Late binding</font></i></b>
+<p>Smalltalk makes no syntactic distinction between late and early bound
+methods (unlike C++ "virtual" methods)
+<br>&nbsp;</td>
+</tr>
+
+<tr>
+<td>
+<h3>
+<b><font face="Arial"><font size=+1>OO-C framework options</font></font></b></h3>
+<font face="Times New Roman,Times">Covered: objects (encapsulation, explicit
+construct and destruct), classes, inheritance, polymorphism</font>
+<br><font face="Times New Roman,Times">Not covered: multiple inheritance,
+automatic initialization / destruction,</font>
+<h3>
+<b><font face="Arial"><font size=+1>Strategy 1: message maps and aggregation</font></font></b></h3>
+<font face="Times New Roman,Times">Class: a struct with a pointer to a
+method table (message map). Contructor and destuctor are really initializer
+and destructor, and are invoked manually at beginning and end of lifetime.</font>
+<p><font face="Times New Roman,Times">Messages and methods: mapping table
+(first cut: use macros like MFC to create a mapping between messages and
+methods). Alternative: message map can be built at run-time (hash, tree,
+linked list) – method resolution may be slower.</font>
+<p><font face="Times New Roman,Times">Inheritance: aggregate the parent
+struct (recursively) at the beginning of the derived one, link the child
+method table to the parent and search recursively to resolve messages to
+methods</font>
+<p><font face="Times New Roman,Times">Pros and cons:</font>
+<blockquote><font face="Times New Roman,Times">+ flexible and minimal manual
+steps required</font>
+<br><font face="Times New Roman,Times">+ relatively simple – no need to
+write a preprocessor!</font>
+<br><font face="Times New Roman,Times">- all methods are late bound, run-time
+penalty</font>
+<br><font face="Times New Roman,Times">&shy; defeats compile time type
+checking, may require a single prototype for all methods</font></blockquote>
+
+<p><br><b><font face="Arial"><font size=+1>Strategy 2: manual name mangling</font></font></b>
+<br><font face="Times New Roman,Times">This is how the <a href="#samek">Samek</a>
+article handles encapsulation</font>
+<p><b><font face="Arial"><font size=+1>Strategy 3: preprocessor</font></font></b>
+<br><font face="Times New Roman,Times">This is how C++ started out.</font>
+<br>&nbsp;</td>
+</tr>
+
+<tr>
+<td><b><font face="Arial"><font size=+1>References</font></font></b>
+<ul>
+<li>
+<a NAME="robson"></a><font size=-1>David Robson, <i>Object Oriented Software
+Systems</i>. Byte, August 1981</font></li>
+
+<li>
+<a NAME="samek"></a><font size=-1>Miro Samek, <i>Portable Inheritance and
+Polymorphism in C</i>. Embedded Systems Programming, December 1997</font></li>
+</ul>
+</td>
+</tr>
+</table>
+
+</body>
+</html>
diff --git a/doc/primer.txt b/doc/primer.txt
new file mode 100644
index 000000000000..7de5214dd379
--- /dev/null
+++ b/doc/primer.txt
@@ -0,0 +1,1164 @@
+ A Beginner's Guide to Forth
+
+ by
+
+ J.V. Noble
+
+ Contents
+
+ 0. Preliminaries
+
+
+ 1. Getting started
+
+ The structure of Forth
+
+ 2. Extending the dictionary
+
+ 3. Stacks and reverse Polish notation (RPN)
+ 3.1 Manipulating the parameter stack
+ 3.2 The return stack and its uses
+
+ 4. Fetching and storing
+
+ 5. Comparing and branching
+
+ 6. Documenting and commenting Forth code
+
+ 7. Arithmetic operations
+
+ 8. Looping and structured programming
+
+ 9. CREATE ... DOES> (the pearl of Forth)
+ 9.1 Defining "defining" words
+ 9.2 Run-time vs. compile-time actions
+ 9.3 Dimensioned data (intrinsic units)
+ 9.4 Advanced uses of the compiler
+
+ 10. Floating point arithmetic
+
+
+
+ 0. Introduction
+
+ Forth is an unusual computer language that has probably been applied
+ to more varied projects than any other. It is the obvious choice when
+ the project is exceptionally demanding in terms of completion sched-
+ ule, speed of execution, compactness of code, or any combination of
+ the above.
+
+ It has also been called "...one of the best-kept secrets in the com-
+ puting world." This is no exaggeration: large corporations have pur-
+ chased professional Forth development systems from vendors such as
+ Laboratory Microsystems, Inc., Forth, Inc. or MicroProcessor Engineer-
+ ing, Ltd. and sworn them to secrecy.
+
+ Some speculate (unkindly) that corporate giants prefer to hide their
+ shame at using Forth; but I believe they are actually concealing a
+ secret weapon from their rivals. Whenever Forth has competed directly
+ with a more conventional language like C it has won hands down, pro-
+ ducing smaller, faster, more reliable code in far less time. I have
+ searched for examples with the opposite outcome, but have been unable
+ to find a single instance.
+
+
+
+ 1. Getting started
+
+ We will use Win32Forth for these illustrations. Download the file
+
+ w32for35.exe
+
+ and double-click on it to install on any Windows 95-equipped machine.
+
+
+ The compressed files will then decompress themselves. They should also
+ install a program group on your desktop.
+
+ Now start Win32Forth by opening the program group and clicking on the
+ appropriate icon.
+
+
+ It should respond by opening a window and writing something like
+
+ 32bit Forth for Windows 95, and NT
+ Compiled: July 23rd, 1997, 5:11pm
+ Version: 3.5 Build: 0008 Release Build
+ Platform: Windows 95 Version: 4.0 Build: 16384
+ 491k bytes free
+ 2,719 Words in Application dictionary
+ 1,466 Words in System dictionary
+ 4,185 Words total in dictionaries
+ 8,293 Windows Constants available
+
+ Loading Win32For.CFG
+
+ *** DON'T PANIC, Press: F1 NOW! ***
+
+
+ Win32Forth is case-insensitive.
+
+
+ Now type
+
+ BYE <cr>.
+
+ The Win32Forth window immediately closes.
+
+
+ What just happened? Forth is an interactive programming language con-
+ sisting entirely of subroutines, called "words".
+
+ A word is executed (interactively) by naming it. We have just seen
+ this happen: BYE is a Forth subroutine meaning "exit to the operating
+ system". So when we entered BYE it was executed, and the system re-
+ turned control to Windows.
+
+
+ Click on the Win32Forth icon again to re-start Forth.
+ Now we will try something a little more complicated. Enter
+
+ 2 17 + . <cr> 19 ok
+
+ What happened? Forth is interpretive. An "outer interpreter" continu-
+ ally loops, waiting for input from the keyboard or mass storage de-
+ vice. The input is a sequence of text strings separated from each
+ other by blank spaces --ASCII 32decimal = 20hex-- the standard Forth
+ delimiter.
+
+ When the outer interpreter encounters text it first looks for it in
+ the "dictionary" (a linked list of previously defined subroutine
+ names). If it finds the word, it executes the corresponding code.
+
+ If no dictionary entry exists, the interpreter tries to read the input
+ as a number.
+
+ If the input text string satisfies the rules defining a number, it is
+ converted to a number and stored in a special memory location called
+ "the top of the stack" (TOS).
+
+
+ In the above example, Forth interpreted 2 and 17 as numbers, and
+ pushed them both onto the stack.
+
+ "+" is a pre-defined word as is ".", so they were looked up and exe-
+ cuted.
+
+ "+" added 2 to 17 and left 19 on the stack.
+
+ The word "." (called "emit") removed 19 from the stack and displayed
+ it on the standard output device (in this case, CRT).
+
+
+ We might also have said
+
+ HEX 0A 14 * . <cr> C8 ok
+
+ (Do you understand this? Hint: DECIMAL means "switch to decimal arith-
+ metic", whereas HEX stands for "switch to hexadecimal arithmetic".)
+
+ If the incoming text can neither be located in the dictionary nor in-
+ terpreted as a number, Forth issues an error message. Try it: say X
+ and see
+
+ X
+ Error: X is undefined
+
+ or say THING and see
+
+ THING
+ Error: THING is undefined
+
+
+
+ 2. Extending the dictionary
+
+ The compiler is one of Forth's most endearing features. Unlike
+ all other high-level languages, the Forth compiler is part of the
+ language. (LISP and its dialects also make components of the com-
+ pilation mechanism available to the programmer.) That is, its com-
+ ponents are Forth words available to the programmer, that can be
+ used to solve his problems.
+
+ In this section we discuss how the compiler extends the
+ dictionary.
+
+ Forth uses special words to create new dictionary entries, i.e.,
+ new words. The most important are ":" ("start a new definition")
+ and ";" ("terminate the definition").
+
+ Let's try this out: enter
+
+ : *+ * + ; <cr> ok
+
+ What happened? The word ":" was executed because it was already
+ in the dictionary. The action of ":" is
+
+ > Create a new dictionary entry named "*+" and switch from
+ interpret to compile mode.
+
+ > In compile mode, the interpreter looks up words and
+ --rather than executing them-- installs pointers to
+ their code. (If the text is a number, rather than
+ pushing it on the stack, Forth builds the number
+ into the dictionary space allotted for the new word,
+ following special code that puts it on the stack
+ when the word is executed.)
+
+ > The action of "*+" will be to execute sequentially
+ the previously-defined words "*" and "+".
+
+ > The word ";" is special: when it was defined a bit
+ was turned on in its dictionary entry to mark it as
+ IMMEDIATE. Thus, rather than writing down its
+ address, the compiler executes ";" immediately. The
+ action of ";" is first, to install the code that
+ returns control to the next outer level of the
+ interpreter; and second, to switch back from compile
+ mode to interpret mode.
+
+ Now try out "*+" :
+
+ DECIMAL 5 6 7 *+ . <cr> 47 ok
+
+ This example illustrated two principles of Forth: adding a new word to
+ the dictionary, and trying it out as soon as it was defined.
+
+
+
+ 3. Stacks and reverse Polish notation (RPN)
+
+ We now discuss the stack and the "reverse Polish" or "postfix" arith-
+ metic based on it. (Anyone who has used a Hewlett-Packard calculator
+ should be familiar with the concept.)
+
+ Virtually all modern CPU's are designed around stacks. Forth effi-
+ ciently uses its CPU by reflecting this underlying stack architecture
+ in its syntax.
+
+
+ But what is a stack? As the name implies, a stack is the machine ana-
+ log of a pile of cards with numbers written on them. Numbers are
+ always added to the top of the pile, and removed from the top of the
+ pile. The Forth input line
+
+ 2 5 73 -16 <cr> ok
+
+ leaves the stack in the state
+
+ cell # contents
+
+
+ 0 -16 (TOS)
+
+ 1 73 (NOS)
+
+ 2 5
+
+ 3 2
+
+
+ where TOS stands for "top-of-stack", NOS for "next-on-stack", etc.
+
+ We usually employ zero-based relative numbering in Forth data struct-
+ ures (such as stacks, arrays, tables, etc.) so TOS is given relative
+ #0, NOS #1, etc.
+
+ Suppose we followed the above input line with the line
+
+ + - * . <cr> xxx ok
+
+ what would xxx be? The operations would produce the successive stacks
+
+ cell# initial + - * .
+
+ 0 -16 57 -52 -104
+ 1 73 5 2
+ 2 5 2
+ 3 2 empty
+ stack
+
+ The operation "." (EMIT) displays -104 to the screen, leaving the
+ stack empty. That is, xxx is -104.
+
+
+ 3.1 Manipulating the parameter stack
+
+ Forth systems incorporate (at least) two stacks: the parameter stack
+ and the return stack.
+
+ A stack-based system must provide ways to put numbers on the stack, to
+ remove them, and to rearrange their order. Forth includes standard
+ words for this purpose.
+
+ Putting numbers on the stack is easy: simply type the number (or in-
+ corporate it in the definition of a Forth word).
+
+ The word DROP removes the number from TOS and moves up all the other
+ numbers. (Since the stack usually grows downward in memory, DROP mere-
+ ly increments the pointer to TOS by 1 cell.)
+
+ SWAP exchanges the top 2 numbers.
+
+ DUP duplicates the TOS into NOS.
+
+ ROT rotates the top 3 numbers.
+
+
+ These actions are shown below (we show what each word does to the ini-
+ tial stack)
+
+ cell | initial | DROP SWAP ROT DUP
+
+ 0 | -16 | 73 73 5 -16
+ 1 | 73 | 5 -16 -16 -16
+ 2 | 5 | 2 5 73 73
+ 3 | 2 | 2 2 5
+ 4 | | 2
+
+
+ Forth includes the words OVER, TUCK, PICK and ROLL that act as shown
+ below (note PICK and ROLL must be preceded by an integer that says
+ where on the stack an element gets PICK'ed or ROLL'ed):
+
+ cell | initial | OVER TUCK 4 PICK 4 ROLL
+
+ 0 | -16 | 73 -16 2 2
+ 1 | 73 | -16 73 -16 -16
+ 2 | 5 | 73 -16 73 73
+ 3 | 2 | 5 5 5 5
+ 4 | | 2 2 2
+
+ Clearly, 1 PICK is the same as DUP, 2 PICK is a synonym for OVER, and
+ 2 ROLL means SWAP.
+
+
+ 3.2 The return stack and its uses
+
+ We have remarked above that compilation establishes links from the
+ calling word to the previously-defined word being invoked. The linkage
+ mechanism --during execution-- uses the return stack (rstack): the
+ address of the next word to be invoked is placed on the rstack, so
+ that when the current word is done executing, the system knows to jump
+ to the next word. (This is so in most, but not all Forth implement-
+ ations. But all have a return stack, whether or not they use them for
+ linking subroutines.)
+
+ In addition to serving as a reservoir of return addresses (since words
+ can be nested, the return addresses need a stack to be put on) the
+ rstack is where the limits of a DO...LOOP construct are placed.
+
+ The user can also store/retrieve to/from the rstack. This is an ex-
+ ample of using a component for a purpose other than the one it was
+ designed for. Such use is discouraged for novices since it adds the
+ spice of danger to programming. See "Note of caution" below.
+
+ To store to the rstack we say >R , and to retrieve we say R> . The
+ word R@ copies the top of the rstack to the TOS.
+
+
+ Why use the rstack when we have a perfectly good parameter stack to
+ play with? Sometimes it becomes hard to read code that performs com-
+ plex gymnastics on the stack. The rstack can reduce the complexity.
+
+ Alternatively, VARIABLEs --named locations-- provide a place to store
+ numbers --such as intermediate results in a calculation-- off the
+ stack, again reducing the gymnastics. Try this:
+
+ \ YOU DO THIS \ EXPLANATION
+
+ VARIABLE X <cr> ok \ create a named storage location X;
+ \ X executes by leaving its address
+
+ 3 X ! <cr> ok \ ! ("store") expects a number and
+ \ an address, and stores the number to
+ \ that address
+
+ X @ . <cr> 3 ok \ @ ("fetch") expects an address, and
+ \ places its contents in TOS.
+
+ However, Forth encourages using as few named variables as possible.
+ The reason: since VARIABLEs are typically global --any subroutine can
+ access them-- they can cause unwanted interactions among parts of a
+ large program.
+
+ Although Forth can make variables local to the subroutines that use
+ them (see "headerless words" in FTR), the rstack can often replace
+ local variables:
+
+ > The rstack already exists, so it need not be defined anew.
+
+ > When the numbers placed on it are removed, the rstack shrinks,
+ reclaiming some memory.
+
+
+ A note of caution: since the rstack is critical to execution we mess
+ with it at our peril. If we use the rstack for temporary storage we
+ must restore it to its initial state. A word that places a number on
+ the rstack must remove it --using R> or RDROP (if it has been defined)
+ -- before exiting that word. Since DO...LOOP also uses the rstack,
+ for each >R folowing DO there must be a corresponding R> or RDROP
+ preceding LOOP. Neglecting these precautions will probably crash
+ the system.
+
+
+
+
+ 4. Fetching and storing
+
+ As we just saw, ordinary numbers are fetched from memory to
+ the stack by @ ("fetch"), and stored by ! (store).
+
+ @ expects an address on the stack and replaces that address by
+ its contents using, e.g., the phrase X @
+
+ ! expects a number (NOS) and an address (TOS) to store it in, and
+ places the number in the memory location referred to by the address,
+ consuming both arguments in the process, as in the phrase 3 X !
+
+ Double length numbers can similarly be fetched and stored, by
+ D@ and D!, if the system has these words.
+
+ Positive numbers smaller than 255 can be placed in single bytes of
+ memory using C@ and C!. This is convenient for operations with strings
+ of ASCII text, for example screen and keyboard I/O.
+
+
+
+ 5. Comparing and branching
+
+ Forth lets you compare two numbers on the stack, using relational
+ operators ">", "<", "=" . Ths, e.g., the phrase
+
+ 2 3 > <cr> ok
+
+ leaves 0 ("false") on the stack, because 2 (NOS) is not greater than 3
+ (TOS). Conversely, the phrase
+
+ 2 3 < <cr> ok
+
+ leaves -1 ("true") because 2 is less than 3.
+
+ Notes: In some Forths "true" is +1 rather than -1.
+
+ Relational operators consume both arguments and leave a "flag"
+ to show what happened.
+
+ (Many Forths offer unary relational operators "0=", "0>" and "0<".
+ These, as might be guessed, determine whether the TOS contains an
+ integer that is 0, positive or negative.)
+
+ The relational words are used for branching and control. For example,
+
+ : TEST CR 0 = NOT IF ." Not zero!" THEN ;
+
+ 0 TEST <cr> ok ( no action)
+ -14 TEST <cr>
+ Not zero! ok
+
+ The word CR issues a carriage return (newline). Then TOS is compared
+ with zero, and the logical NOT operator (this flips "true" and
+ "false") applied to the resulting flag. Finally, if TOS is non-zero,
+ IF swallows the flag and executes all the words between itself and the
+ terminating THEN. If TOS is zero, execution jumps to the word
+ following THEN.
+
+ The word ELSE is used in the IF...ELSE...THEN statement: a nonzero
+ value in TOS causes any words between IF and ELSE to be executed, and
+ words between ELSE and THEN to be skipped. A zero value produces the
+ opposite behavior. Thus, e.g.
+
+
+ : TRUTH CR 0 = IF ." false" ELSE ." true" THEN ;
+
+ 1 TRUTH <cr>
+ true ok
+
+ 0 TRUTH <cr>
+ false ok
+
+ Since THEN is used to terminate an IF statement rather than in its
+ usual sense, some Forth writers prefer the name ENDIF.
+
+ 6. Documenting and commenting Forth code
+
+ Forth is sometimes accused of being a "write-only" language, i.e. some
+ complain that Forth is cryptic. This is really a complaint against
+ poor documentation and untelegraphic word names. Unreadability is
+ equally a flaw of poorly written FORTRAN, PASCAL, C, etc.
+
+ Forth offers programmers who take the trouble tools for producing ex-
+ ceptionally clear code.
+
+
+ 6.1 Parenthesized remarks
+
+ The word ( -- a left parenthesis followed by a space -- says "disre-
+ gard all following text until the next right parenthesis in the
+ input stream". Thus we can intersperse explanatory remarks within
+ colon definitions.
+
+
+ 6.2 Stack comments
+
+ A particular form of parenthesized remark describes the effect of a
+ word on the stack. In the example of a recursive loop (GCD below),
+ stack comments are really all the documentation necessary.
+
+ Glossaries generally explain the action of a word with a
+ stack-effect comment. For example,
+
+ ( adr -- n)
+
+ describes the word @ ("fetch"): it says @ expects to find an address
+ (adr) on the stack, and to leave its contents (n) upon completion.
+ The corresponding comment for ! would be
+
+ ( n adr -- ) .
+
+
+
+ 6.3 Drop line (\)
+
+ The word "\" (back-slash followed by space) has recently gained favor
+ as a method for including longer comments. It simply means "drop ev-
+ erything in the input stream until the next carriage return". Instruc-
+ tions to the user, clarifications or usage examples are most naturally
+ expressed in a block of text with each line set off by "\" .
+
+
+ 6.4 Self-documenting code
+
+ By eliminating ungrammatical phrases like CALL or GOSUB, Forth pre-
+ sents the opportunity --via telegraphic names for words-- to make code
+ almost as self-documenting and transparent as a readable English or
+ German sentence. Thus, for example, a robot control program could con-
+ tain a phrase like
+
+ 2 TIMES LEFT EYE WINK
+
+ which is clear (although it sounds like a stage direction for Brun-
+ hilde to vamp Siegfried). It would even be possible without much dif-
+ ficulty to define the words in the program so that the sequence could
+ be made English-like: WINK LEFT EYE 2 TIMES .
+
+
+
+
+ 7. Arithmetic operations
+
+ The 1979 or 1983 standards require that a conforming Forth system con-
+ tain a certain minimum set of pre-defined words. These consist of
+ arithmetic operators + - * / MOD /MOD */ for (usually) 16-bit signed-
+ integer (-32767 to +32767) arithmetic, and equivalents for unsigned (0
+ to 65535), double-length and mixed-mode (16- mixed with 32-bit) arith-
+ metic. The list will be found in the glossary accompanying your
+ system, as well as in SF and FTR.
+
+ Try this example of a non-trivial program that uses arithmetic and
+ branching to compute the greatest common divisor of two integers using
+ Euclid's algorithm:
+
+ : TUCK ( a b -- b a b) SWAP OVER ;
+ : GCD ( a b -- gcd) ?DUP IF TUCK MOD GCD THEN ;
+
+ The word ?DUP duplicates TOS if it is not zero, and leaves it alone
+ otherwise. If the TOS is 0, therefore, GCD consumes it and does
+ nothing else. However, if TOS is unequal to 0, then GCD TUCKs TOS
+ under NOS (to save it); then divides NOS by TOS, keeping the remainder
+ (MOD). There are now two numbers left on the stack, so we again take
+ the GCD of them. That is, GCD calls itself. However, if you try the
+ above code it will fail. A dictionary entry cannot be looked up and
+ found until the terminating ";" has completed it. So in fact we must
+ use the word RECURSE to achieve self-reference, as in
+
+ : TUCK ( a b -- b a b) SWAP OVER ;
+ : GCD ( a b -- gcd) ?DUP IF TUCK MOD RECURSE THEN ;
+
+ Now try
+
+ 784 48 GCD . 16 ok
+
+
+ 8. Looping and structured programming
+
+ Forth has several ways to loop, including the implicit method of re-
+ cursion, illustrated above. Recursion has a bad name as a looping
+ method because in most languages that permit recursion, it imposes
+ unacceptable running time overhead on the program. Worse, recursion
+ can --for reasons beyond the scope of this Introduction to Forth-- be
+ an extremely inefficient method of expressing the problem. In Forth,
+ there is virtually no excess overhead in recursive calls because Forth
+ uses the stack directly. So there is no reason not to recurse if that
+ is the best way to program the algorithm. But for those times when
+ recursion simply isn't enough, here are some more standard methods.
+
+ 8.1 Indefinite loops
+
+ The construct
+
+ BEGIN xxx ( -- flag) UNTIL
+
+ executes the words represented by xxx, leaving TOS (flag) set to TRUE
+ --at which point UNTIL terminates the loop-- or to FALSE --at which
+ point UNTIL jumps back to BEGIN. Try:
+
+ : COUNTDOWN ( n --)
+ BEGIN CR DUP . 1 - DUP 0 = UNTIL DROP ;
+
+ 5 COUNTDOWN
+ 5
+ 4
+ 3
+ 2
+ 1 ok
+
+ A variant of BEGIN...UNTIL is
+
+ BEGIN xxx ( -- flag) WHILE yyy REPEAT
+
+ Here xxx is executed, WHILE tests the flag and if it is FALSE
+ leaves the loop; if the flag is TRUE, yyy is executed; REPEAT then
+ branches back to BEGIN.
+
+ These forms can be used to set up loops that repeat until some
+ external event (pressing a key at the keyboard, e.g.) sets the
+ flag to exit the loop. They can also used to make endless loops
+ (like the outer interpreter of Forth) by forcing the flag
+ to be FALSE in a definition like
+
+
+ : ENDLESS BEGIN xxx FALSE UNTIL ;
+
+
+ 8.2 Definite loops
+
+ Most Forths allow indexed loops using DO...LOOP (or +LOOP or /LOOP).
+ These are permitted only within definitions
+
+ : BY-ONES ( n --) 0 TUCK DO CR DUP . 1 + LOOP DROP ;
+
+ The words CR DUP . 1 + will be executed n times as the lower
+ limit, 0, increases in unit steps to n-1.
+
+ To step by 2's, we use the phrase 2 +LOOP to replace LOOP, as with
+
+ : BY-TWOS ( n --) 0 TUCK
+ DO CR DUP . 2 + 2 +LOOP DROP ;
+
+
+ 8.4 Structured programming
+
+ N. Wirth invented the Pascal language in reaction to program flow
+ charts resembling a plate of spaghetti. Such flow diagrams were
+ often seen in early languages like FORTRAN and assembler. Wirth
+ intended to eliminate line labels and direct jumps (GOTOs), thereby
+ forcing control flow to be clear and direct.
+
+ The ideal was subroutines or functions that performed a single
+ task, with unique entries and exits. Unfortunately, programmers
+ insisted on GOTOs, so many Pascals and other modern languages now have
+ them. Worse, the ideal of short subroutines that do one thing only is
+ unreachable in such languages because the method for calling them and
+ passing arguments imposes a large overhead. Thus execution speed re-
+ quires minimizing calls, which in turn means longer, more complex sub-
+ routines that perform several related tasks. Today structured program-
+ ming seems to mean little more than writing code with nested IFs in-
+ dented by a pretty-printer.
+
+ Paradoxically, Forth is the only truly structured language in common
+ use, although it was not designed with that as its goal. In Forth word
+ definitions are subroutine calls. The language contains no GOTO's so
+ it is impossible to write "spaghetti code". Forth also encourages
+ structure through short definitions. The additional running time
+ incurred in breaking a long procedure into many small ones (this is
+ called "factoring") is typically rather small in Forth. Each Forth sub-
+ routine (word) has one entry and one exit point, and can be written
+ to perform a single job.
+
+
+
+ 8.5 "Top-down" design
+
+ "Top-down" programming is a doctrine that one should design the entire
+ program from the general to the particular:
+
+ > Make an outline, flow chart or whatever, taking a broad overview
+ of the whole problem.
+
+ > Break the problem into small pieces (decompose it).
+
+ > Then code the individual components.
+
+ The natural programming mode in Forth is "bottom-up" rather than "top-
+ down" --the most general word appears last, whereas the definitions
+ must progress from the primitive to the complex. This leads to a some-
+ what different approach from more familiar languages:
+
+ > In Forth, components are specified roughly, and then as they are
+ coded they are immediately tested, debugged, redesigned and
+ improved.
+
+ > The evolution of the components guides the evolution of the outer
+ levels of the program.
+
+
+
+
+ 9. CREATE ... DOES> (the pearl of FORTH)
+
+ Michael Ham has called the word pair CREATE...DOES>, the "pearl of
+ Forth". CREATE is a component of the compiler, whose function is to
+ make a new dictionary entry with a given name (the next name in the
+ input stream) and nothing else. DOES> assigns a specific run-time ac-
+ tion to a newly CREATEd word.
+
+
+ 9.1 Defining "defining" words
+
+ CREATE finds its most important use in extending the powerful class of
+ Forth words called "defining" words. The colon compiler ":" is such
+ a word, as are VARIABLE and CONSTANT.
+
+ The definition of VARIABLE in high-level Forth is simple
+
+ : VARIABLE CREATE 1 CELLS ALLOT ;
+
+ We have already seen how VARIABLE is used in a program. (An altern-
+ ative definition found in some Forths is
+
+ : VARIABLE CREATE 0 , ;
+
+ --these variables are initialized to 0.)
+
+ Forth lets us define words initialized to contain specific values: for
+ example, we might want to define the number 17 to be a word. CREATE
+ and "," ("comma") can do this:
+
+ 17 CREATE SEVENTEEN , <cr> ok
+
+ Now test it via
+
+ SEVENTEEN @ . <cr> 17 ok .
+
+
+ Remarks:
+
+ > The word , ("comma") puts TOS into the next cell of the dic-
+ tionary and increments the dictionary pointer by that number of
+ bytes.
+
+ > A word "C," ("see-comma") exists also -- it puts a character into
+ the next character-length slot of the dictionary and increments
+ the pointer by 1 such slot. (If the character representation is
+ ASCII the slots are 1 byte--Unicode requires 2 bytes.)
+
+
+ 9.2 Run-time vs. compile-time actions
+
+ In the preceding example, we were able to initialize the variable
+ SEVENTEEN to 17 when we CREATEd it, but we still have to fetch it to
+ the stack via SEVENTEEN @ whenever we want it. This is not quite what
+ we had in mind: we would like to find 17 in TOS when SEVENTEEN is
+ named. The word DOES> gives us the tool to do this.
+
+ The function of DOES> is to specify a run-time action for the "child"
+ words of a defining word. Consider the defining word CONSTANT , de-
+ fined in high-level (of course CONSTANT is usually defined in machine
+ code for speed) Forth by
+
+ : CONSTANT CREATE , DOES> @ ;
+
+ and used as
+
+ 53 CONSTANT PRIME <cr> ok
+
+ Now test it:
+
+ PRIME . <cr> 53 ok .
+
+
+ What is happening here?
+
+ > CREATE (hidden in CONSTANT) makes an entry named PRIME (the
+ first word in the input stream following CONSTANT). Then ","
+ places the TOS (the number 53) in the next cell of the dic-
+ tionary.
+
+ > Then DOES> (inside CONSTANT) appends the actions of all words be-
+ tween it and ";" (the end of the definition) --in this case, "@"--
+ to the child word(s) defined by CONSTANT.
+
+
+ 9.3 Dimensioned data (intrinsic units)
+
+ Here is an example of the power of defining words and of the distinc-
+ tion between compile-time and run-time behaviors.
+
+ Physical problems generally involve quantities that have dimensions,
+ usually expressed as mass (M), length (L) and time (T) or products of
+ powers of these. Sometimes there is more than one system of units in
+ common use to describe the same phenomena.
+
+ For example, U.S. or English police reporting accidents might use
+ inches, feet and yards; while Continental police would use centimeters
+ and meters. Rather than write different versions of an accident ana-
+ lysis program it is simpler to write one program and make unit conver-
+ sions part of the grammar. This is easy in Forth.
+
+ The simplest method is to keep all internal lengths in millimeters,
+ say, and convert as follows:
+
+ : INCHES 254 10 */ ;
+ : FEET [ 254 12 * ] LITERAL 10 */ ;
+ : YARDS [ 254 36 * ] LITERAL 10 */ ;
+ : CENTIMETERS 10 * ;
+ : METERS 1000 * ;
+
+ Note: This example is based on integer arithmetic. The word */
+ means "multiply the third number on the stack by NOS, keeping
+ double precision, and divide by TOS". That is, the stack com-
+ ment for */ is ( a b c -- a*b/c).
+
+
+ The usage would be
+
+ 10 FEET . <cr> 3048 ok
+
+
+ The word "[" switches from compile mode to interpret mode while com-
+ piling. (If the system is interpreting it changes nothing.) The word
+ "]" switches from interpret to compile mode.
+
+ Barring some error-checking, the "definition" of the colon compiler
+ ":" is just
+
+ : : CREATE ] DOES> doLIST ;
+
+ and that of ";" is just
+
+ : ; next [ ; IMMEDIATE
+
+ Another use for these switches is to perform arithmetic at compile-
+ time rather than at run-time, both for program clarity and for easy
+ modification, as we did in the first try at dimensioned data (that is,
+ phrases such as
+
+ [ 254 12 * ] LITERAL
+
+ and
+
+ [ 254 36 * ] LITERAL
+
+ which allowed us to incorporate in a clear manner the number of
+ tenths of millimeters in a foot or a yard.
+
+
+ The preceding method of dealing with units required unnecessarily many
+ definitions and generated unnecessary code. A more compact approach
+ uses a defining word, UNITS :
+
+ : D, ( hi lo --) SWAP , , ;
+ : D@ ( adr -- hi lo) DUP @ SWAP 2 + @ ;
+ : UNITS CREATE D, DOES> D@ */ ;
+
+ Then we could make the table
+
+ 254 10 UNITS INCHES
+ 254 12 * 10 UNITS FEET
+ 254 36 * 10 UNITS YARDS
+ 10 1 UNITS CENTIMETERS
+ 1000 1 UNITS METERS
+
+ \ Usage:
+ 10 FEET . <cr> 3048 ok
+ 3 METERS . <cr> 3000 ok
+ \ .......................
+ \ etc.
+
+ This is an improvement, but Forth permits a simple extension that
+ allows conversion back to the input units, for use in output:
+
+ VARIABLE <AS> 0 <AS> !
+ : AS TRUE <AS> ! ;
+ : ~AS FALSE <AS> ! ;
+ : UNITS CREATE D, DOES> D@ <AS> @
+ IF SWAP THEN
+ */ ~AS ;
+
+ \ UNIT DEFINITIONS REMAIN THE SAME.
+ \ Usage:
+ 10 FEET . <cr> 3048 ok
+ 3048 AS FEET . <cr> 10 ok
+
+
+
+ 9.4 Advanced uses of the compiler
+
+ Suppose we have a series of push-buttons numbered 0-3, and a word WHAT
+ to read them. That is, WHAT waits for input from a keypad: when button
+ #3 is pushed, for example, WHAT leaves 3 on the stack.
+
+ We would like to define a word BUTTON to perform the action of pushing
+ the n'th button, so we could just say:
+
+ WHAT BUTTON
+
+ In a conventional language BUTTON would look something like
+
+ : BUTTON DUP 0 = IF RING DROP EXIT THEN
+ DUP 1 = IF OPEN DROP EXIT THEN
+ DUP 2 = IF LAUGH DROP EXIT THEN
+ DUP 3 = IF CRY DROP EXIT THEN
+ ABORT" WRONG BUTTON!" ;
+
+ That is, we would have to go through two decisions on the average.
+
+ Forth makes possible a much neater algorithm, involving a "jump
+ table". The mechanism by which Forth executes a subroutine is to
+ feed its "execution token" (often an address, but not necessarily)
+ to the word EXECUTE. If we have a table of execution tokens we need
+ only look up the one corresponding to an index (offset into the table)
+ fetch it to the stack and say EXECUTE.
+
+ One way to code this is
+
+ CREATE BUTTONS ' RING , ' OPEN , ' LAUGH , ' CRY ,
+ : BUTTON ( nth --) 0 MAX 3 MIN
+ CELLS BUTTONS + @ EXECUTE ;
+
+ Note how the phrase 0 MAX 3 MIN protects against an out-of-range
+ index. Although the Forth philosophy is not to slow the code with un-
+ necessary error checking (because words are checked as they are de-
+ fined), when programming a user interface some form of error handling
+ is vital. It is usually easier to prevent errors as we just did, than
+ to provide for recovery after they are made.
+
+ How does the action-table method work?
+
+ > CREATE BUTTONS makes a dictionary entry BUTTONS.
+
+ > The word ' ("tick") finds the execution token (xt) of the
+ following word, and the word , ("comma") stores it in the
+ data field of the new word BUTTONS. This is repeated until
+ all the subroutines we want to select among have their xt's
+ stored in the table.
+
+ > The table BUTTONS now contains xt's of the various actions of
+ BUTTON.
+
+ > CELLS then multiplies the index by the appropriate number of
+ bytes per cell, to get the offset into the table BUTTONS
+ of the desired xt.
+
+ > BUTTONS + then adds the base address of BUTTONS to get the abso-
+ lute address where the xt is stored.
+
+ > @ fetches the xt for EXECUTE to execute.
+
+ > EXECUTE then executes the word corresponding to the button pushed.
+ Simple!
+
+ If a program needs but one action table the preceding method suffices.
+ However, more complex programs may require many such. In that case
+ it may pay to set up a system for defining action tables, including
+ both error-preventing code and the code that executes the proper
+ choice. One way to code this is
+
+ : ;CASE ; \ do-nothing word
+ : CASE:
+ CREATE HERE -1 >R 0 , \ place for length
+ BEGIN BL WORD FIND \ get next subroutine
+ 0= IF CR COUNT TYPE ." not found" ABORT THEN
+ R> 1+ >R
+ DUP , ['] ;CASE =
+ UNTIL R> 1- SWAP ! \ store length
+ DOES> DUP @ ROT ( -- base_adr len n)
+ MIN 0 MAX \ truncate index
+ CELLS + CELL+ @ EXECUTE ;
+
+ Note the two forms of error checking. At compile-time, CASE:
+ aborts compilation of the new word if we ask it to point to an
+ undefined subroutine:
+
+ case: test1 DUP SWAP X ;case
+ X not found
+
+ and we count how many subroutines are in the table (including
+ the do-nothing one, ;case) so that we can force the index to
+ lie in the range [0,n].
+
+ CASE: TEST * / + - ;CASE ok
+ 15 3 0 TEST . 45 ok
+ 15 3 1 TEST . 5 ok
+ 15 3 2 TEST . 18 ok
+ 15 3 3 TEST . 12 ok
+ 15 3 4 TEST . . 3 15 ok
+
+ Just for a change of pace, here is another way to do it:
+
+ : jtab: ( Nmax --) \ starts compilation
+ CREATE \ make a new dictionary entry
+ 1- , \ store Nmax-1 in its body
+ ; \ for bounds clipping
+
+ : get_xt ( n base_adr -- xt_addr)
+ DUP @ ( -- n base_adr Nmax-1)
+ ROT ( -- base_adr Nmax-1 n)
+ MIN 0 MAX \ bounds-clip for safety
+ 1+ CELLS+ ( -- xt_addr = base + 1_cell + offset)
+ ;
+
+ : | ' , ; \ get an xt and store it in next cell
+
+ : ;jtab DOES> ( n base_adr --) \ ends compilation
+ get_xt @ EXECUTE \ get token and execute it
+ ; \ appends table lookup & execute code
+
+ \ Example:
+ : Snickers ." It's a Snickers Bar!" ; \ stub for test
+
+ \ more stubs
+
+ 5 jtab: CandyMachine
+ | Snickers
+ | Payday
+ | M&Ms
+ | Hershey
+ | AlmondJoy
+ ;jtab
+
+ 3 CandyMachine It's a Hershey Bar! ok
+ 1 CandyMachine It's a Payday! ok
+ 7 CandyMachine It's an Almond Joy! ok
+ 0 CandyMachine It's a Snickers Bar! ok
+ -1 CandyMachine It's a Snickers Bar! ok
+
+
+
+ 10. Floating point arithmetic
+
+ Although Forth at one time eschewed floating point arithmetic
+ (because in the era before math co-processors integer arithmetic
+ was 3x faster), in recent years a standard set of word names has
+ been agreed upon. This permits the exchange of programs that will
+ operate correctly on any computer, as well as the development of
+ a Scientific Subroutine Library in Forth (FSL).
+
+ Although the ANS Standard does not require a separate stack for
+ floating point numbers, most programmers who use Forth for numer-
+ ical analysis employ a separate floating point stack; and most of
+ the routines in the FSL assume such. We shall do so here as well.
+
+ The floating point operators have the following names and perform
+ the actions indicated in the accompanying stack comments:
+
+ F@ ( adr --) ( f: -- x)
+ F! ( adr --) ( f: x --)
+ F+ ( f: x y -- x+y)
+ F- ( f: x y -- x-y)
+ F* ( f: x y -- x*y)
+ F/ ( f: x y -- x/y)
+ FEXP ( f: x -- e^x)
+ FLN ( f: x -- ln[x])
+ FSQRT ( f: x -- x^0.5)
+
+ Additional operators, functions, trigonometric functions, etc. can
+ be found in the FLOATING and FLOATING EXT wordsets. (See dpANS6--
+ available in HTML, PostScript and MS Word formats. The HTML version
+ can be accessed from this homepage.)
+
+ To aid in using floating point arithmetic I have created a simple
+ FORTRAN-like interface for incorporating formulas into Forth words.
+
+ The file ftest.f (included below) illustrates how ftran111.f
+ should be used.
+
+\ Test for ANS FORmula TRANslator
+
+marker -test
+fvariable a
+fvariable b
+fvariable c
+fvariable d
+fvariable x
+fvariable w
+
+: test0 f" b+c" cr fe.
+ f" b-c" cr fe.
+ f" (b-c)/(b+c)" cr fe. ;
+
+3.e0 b f!
+4.e0 c f!
+see test0
+test0
+
+: test1 f" a=b*c-3.17e-5/tanh(w)+abs(x)" a f@ cr fe. ;
+1.e-3 w f!
+-2.5e0 x f!
+cr cr
+see test1
+test1
+
+cr cr
+: test2 f" c^3.75" cr fe.
+ f" b^4" cr fe. ;
+see test2
+test2
+
+\ Baden's test case
+
+: quadroot c f! b f! a f!
+ f" d = sqrt(b^2-4*a*c) "
+ f" (-b+d)/(2*a) " f" (-b-d)/(2*a) "
+;
+cr cr
+see quadroot
+
+: goldenratio f" max(quad root(1,-1,-1)) " ;
+cr cr
+see goldenratio
+cr cr
+goldenratio f.
+
+
+
+0 [IF]
+Output should look like:
+
+: test0
+ c f@ b f@ f+ cr fe. c f@ fnegate b f@ f+ cr fe. c f@ fnegate b f@
+ f+ c f@ b f@ f+ f/ cr fe. ;
+7.00000000000000E0
+-1.00000000000000E0
+-142.857142857143E-3
+
+
+: test1
+ x f@ fabs 3.17000000000000E-5 w f@ ftanh f/ fnegate b f@ c f@ f* f+
+ f+ a f! a f@ cr fe. ;
+14.4682999894333E0 ok
+
+: test2
+ c f@ noop 3.75000000000000E0 f** cr fe. b f@ f^4 cr fe. ;
+181.019335983756E0
+81.0000000000000E0 ok
+
+: QUADROOT C F! B F! A F! B F@ F^2 flit 4.00000 A F@
+ C F@ F* F* F- FSQRT D F! B F@ FNEGATE D
+ F@ F+ flit 2.00000 A F@ F* F/ B F@ FNEGATE
+ D F@ F- flit 2.00000 A F@ F* F/ ;
+
+
+: GOLDENRATIO flit 1.00000 flit -1.00000 flit -1.00000
+ QUADROOT FMAX ;
+
+1.61803 ok
+
+with more or fewer places.
+
+[THEN]
+
+
+
+
diff --git a/doc/sigplan9906.doc b/doc/sigplan9906.doc
new file mode 100644
index 000000000000..1f4cea092e58
--- /dev/null
+++ b/doc/sigplan9906.doc
Binary files differ
diff --git a/doc/skey.gif b/doc/skey.gif
new file mode 100644
index 000000000000..7878ccc3431e
--- /dev/null
+++ b/doc/skey.gif
Binary files differ
diff --git a/ficl.c b/ficl.c
new file mode 100644
index 000000000000..a9b4029f6438
--- /dev/null
+++ b/ficl.c
@@ -0,0 +1,691 @@
+/*******************************************************************
+** f i c l . c
+** Forth Inspired Command Language - external interface
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: ficl.c,v 1.17 2001-12-04 17:58:11-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** This is an ANS Forth interpreter written in C.
+** Ficl uses Forth syntax for its commands, but turns the Forth
+** model on its head in other respects.
+** Ficl provides facilities for interoperating
+** with programs written in C: C functions can be exported to Ficl,
+** and Ficl commands can be executed via a C calling interface. The
+** interpreter is re-entrant, so it can be used in multiple instances
+** in a multitasking system. Unlike Forth, Ficl's outer interpreter
+** expects a text block as input, and returns to the caller after each
+** text block, so the data pump is somewhere in external code in the
+** style of TCL.
+**
+** Code is written in ANSI C for portability.
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "ficl.h"
+
+
+/*
+** System statics
+** Each FICL_SYSTEM builds a global dictionary during its start
+** sequence. This is shared by all virtual machines of that system.
+** Therefore only one VM can update the dictionary
+** at a time. The system imports a locking function that
+** you can override in order to control update access to
+** the dictionary. The function is stubbed out by default,
+** but you can insert one: #define FICL_MULTITHREAD 1
+** and supply your own version of ficlLockDictionary.
+*/
+static int defaultStack = FICL_DEFAULT_STACK;
+
+
+static void ficlSetVersionEnv(FICL_SYSTEM *pSys);
+
+
+/**************************************************************************
+ f i c l I n i t S y s t e m
+** Binds a global dictionary to the interpreter system.
+** You specify the address and size of the allocated area.
+** After that, ficl manages it.
+** First step is to set up the static pointers to the area.
+** Then write the "precompiled" portion of the dictionary in.
+** The dictionary needs to be at least large enough to hold the
+** precompiled part. Try 1K cells minimum. Use "words" to find
+** out how much of the dictionary is used at any time.
+**************************************************************************/
+FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi)
+{
+ int nDictCells;
+ int nEnvCells;
+ FICL_SYSTEM *pSys = ficlMalloc(sizeof (FICL_SYSTEM));
+
+ assert(pSys);
+ assert(fsi->size == sizeof (FICL_SYSTEM_INFO));
+
+ memset(pSys, 0, sizeof (FICL_SYSTEM));
+
+ nDictCells = fsi->nDictCells;
+ if (nDictCells <= 0)
+ nDictCells = FICL_DEFAULT_DICT;
+
+ nEnvCells = fsi->nEnvCells;
+ if (nEnvCells <= 0)
+ nEnvCells = FICL_DEFAULT_DICT;
+
+ pSys->dp = dictCreateHashed((unsigned)nDictCells, HASHSIZE);
+ pSys->dp->pForthWords->name = "forth-wordlist";
+
+ pSys->envp = dictCreate((unsigned)nEnvCells);
+ pSys->envp->pForthWords->name = "environment";
+
+ pSys->textOut = fsi->textOut;
+ pSys->pExtend = fsi->pExtend;
+
+#if FICL_WANT_LOCALS
+ /*
+ ** The locals dictionary is only searched while compiling,
+ ** but this is where speed is most important. On the other
+ ** hand, the dictionary gets emptied after each use of locals
+ ** The need to balance search speed with the cost of the 'empty'
+ ** operation led me to select a single-threaded list...
+ */
+ pSys->localp = dictCreate((unsigned)FICL_MAX_LOCALS * CELLS_PER_WORD);
+#endif
+
+ /*
+ ** Build the precompiled dictionary and load softwords. We need a temporary
+ ** VM to do this - ficlNewVM links one to the head of the system VM list.
+ ** ficlCompilePlatform (defined in win32.c, for example) adds platform specific words.
+ */
+ ficlCompileCore(pSys);
+ ficlCompilePrefix(pSys);
+#if FICL_WANT_FLOAT
+ ficlCompileFloat(pSys);
+#endif
+#if FICL_PLATFORM_EXTEND
+ ficlCompilePlatform(pSys);
+#endif
+ ficlSetVersionEnv(pSys);
+
+ /*
+ ** Establish the parse order. Note that prefixes precede numbers -
+ ** this allows constructs like "0b101010" which might parse as a
+ ** hex value otherwise.
+ */
+ ficlAddPrecompiledParseStep(pSys, "?prefix", ficlParsePrefix);
+ ficlAddPrecompiledParseStep(pSys, "?number", ficlParseNumber);
+#if FICL_WANT_FLOAT
+ ficlAddPrecompiledParseStep(pSys, ">float", ficlParseFloatNumber);
+#endif
+
+ /*
+ ** Now create a temporary VM to compile the softwords. Since all VMs are
+ ** linked into the vmList of FICL_SYSTEM, we don't have to pass the VM
+ ** to ficlCompileSoftCore -- it just hijacks whatever it finds in the VM list.
+ ** ficl 2.05: vmCreate no longer depends on the presence of INTERPRET in the
+ ** dictionary, so a VM can be created before the dictionary is built. It just
+ ** can't do much...
+ */
+ ficlNewVM(pSys);
+ ficlCompileSoftCore(pSys);
+ ficlFreeVM(pSys->vmList);
+
+
+ return pSys;
+}
+
+
+FICL_SYSTEM *ficlInitSystem(int nDictCells)
+{
+ FICL_SYSTEM_INFO fsi;
+ ficlInitInfo(&fsi);
+ fsi.nDictCells = nDictCells;
+ return ficlInitSystemEx(&fsi);
+}
+
+
+/**************************************************************************
+ f i c l A d d P a r s e S t e p
+** Appends a parse step function to the end of the parse list (see
+** FICL_PARSE_STEP notes in ficl.h for details). Returns 0 if successful,
+** nonzero if there's no more room in the list.
+**************************************************************************/
+int ficlAddParseStep(FICL_SYSTEM *pSys, FICL_WORD *pFW)
+{
+ int i;
+ for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
+ {
+ if (pSys->parseList[i] == NULL)
+ {
+ pSys->parseList[i] = pFW;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+** Compile a word into the dictionary that invokes the specified FICL_PARSE_STEP
+** function. It is up to the user (as usual in Forth) to make sure the stack
+** preconditions are valid (there needs to be a counted string on top of the stack)
+** before using the resulting word.
+*/
+void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep)
+{
+ FICL_DICT *dp = pSys->dp;
+ FICL_WORD *pFW = dictAppendWord(dp, name, parseStepParen, FW_DEFAULT);
+ dictAppendCell(dp, LVALUEtoCELL(pStep));
+ ficlAddParseStep(pSys, pFW);
+}
+
+
+/*
+** This word lists the parse steps in order
+*/
+void ficlListParseSteps(FICL_VM *pVM)
+{
+ int i;
+ FICL_SYSTEM *pSys = pVM->pSys;
+ assert(pSys);
+
+ vmTextOut(pVM, "Parse steps:", 1);
+ vmTextOut(pVM, "lookup", 1);
+
+ for (i = 0; i < FICL_MAX_PARSE_STEPS; i++)
+ {
+ if (pSys->parseList[i] != NULL)
+ {
+ vmTextOut(pVM, pSys->parseList[i]->name, 1);
+ }
+ else break;
+ }
+ return;
+}
+
+
+/**************************************************************************
+ f i c l N e w V M
+** Create a new virtual machine and link it into the system list
+** of VMs for later cleanup by ficlTermSystem.
+**************************************************************************/
+FICL_VM *ficlNewVM(FICL_SYSTEM *pSys)
+{
+ FICL_VM *pVM = vmCreate(NULL, defaultStack, defaultStack);
+ pVM->link = pSys->vmList;
+ pVM->pSys = pSys;
+ pVM->pExtend = pSys->pExtend;
+ vmSetTextOut(pVM, pSys->textOut);
+
+ pSys->vmList = pVM;
+ return pVM;
+}
+
+
+/**************************************************************************
+ f i c l F r e e V M
+** Removes the VM in question from the system VM list and deletes the
+** memory allocated to it. This is an optional call, since ficlTermSystem
+** will do this cleanup for you. This function is handy if you're going to
+** do a lot of dynamic creation of VMs.
+**************************************************************************/
+void ficlFreeVM(FICL_VM *pVM)
+{
+ FICL_SYSTEM *pSys = pVM->pSys;
+ FICL_VM *pList = pSys->vmList;
+
+ assert(pVM != 0);
+
+ if (pSys->vmList == pVM)
+ {
+ pSys->vmList = pSys->vmList->link;
+ }
+ else for (; pList != NULL; pList = pList->link)
+ {
+ if (pList->link == pVM)
+ {
+ pList->link = pVM->link;
+ break;
+ }
+ }
+
+ if (pList)
+ vmDelete(pVM);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l B u i l d
+** Builds a word into the dictionary.
+** Preconditions: system must be initialized, and there must
+** be enough space for the new word's header! Operation is
+** controlled by ficlLockDictionary, so any initialization
+** required by your version of the function (if you overrode
+** it) must be complete at this point.
+** Parameters:
+** name -- duh, the name of the word
+** code -- code to execute when the word is invoked - must take a single param
+** pointer to a FICL_VM
+** flags -- 0 or more of F_IMMEDIATE, F_COMPILE, use bitwise OR!
+**
+**************************************************************************/
+int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags)
+{
+#if FICL_MULTITHREAD
+ int err = ficlLockDictionary(TRUE);
+ if (err) return err;
+#endif /* FICL_MULTITHREAD */
+
+ assert(dictCellsAvail(pSys->dp) > sizeof (FICL_WORD) / sizeof (CELL));
+ dictAppendWord(pSys->dp, name, code, flags);
+
+ ficlLockDictionary(FALSE);
+ return 0;
+}
+
+
+/**************************************************************************
+ f i c l E v a l u a t e
+** Wrapper for ficlExec() which sets SOURCE-ID to -1.
+**************************************************************************/
+int ficlEvaluate(FICL_VM *pVM, char *pText)
+{
+ int returnValue;
+ CELL id = pVM->sourceID;
+ pVM->sourceID.i = -1;
+ returnValue = ficlExecC(pVM, pText, -1);
+ pVM->sourceID = id;
+ return returnValue;
+}
+
+
+/**************************************************************************
+ f i c l E x e c
+** Evaluates a block of input text in the context of the
+** specified interpreter. Emits any requested output to the
+** interpreter's output function.
+**
+** Contains the "inner interpreter" code in a tight loop
+**
+** Returns one of the VM_XXXX codes defined in ficl.h:
+** VM_OUTOFTEXT is the normal exit condition
+** VM_ERREXIT means that the interp encountered a syntax error
+** and the vm has been reset to recover (some or all
+** of the text block got ignored
+** VM_USEREXIT means that the user executed the "bye" command
+** to shut down the interpreter. This would be a good
+** time to delete the vm, etc -- or you can ignore this
+** signal.
+**************************************************************************/
+int ficlExec(FICL_VM *pVM, char *pText)
+{
+ return ficlExecC(pVM, pText, -1);
+}
+
+int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT size)
+{
+ FICL_SYSTEM *pSys = pVM->pSys;
+ FICL_DICT *dp = pSys->dp;
+
+ int except;
+ jmp_buf vmState;
+ jmp_buf *oldState;
+ TIB saveTib;
+
+ assert(pVM);
+ assert(pSys->pInterp[0]);
+
+ if (size < 0)
+ size = strlen(pText);
+
+ vmPushTib(pVM, pText, size, &saveTib);
+
+ /*
+ ** Save and restore VM's jmp_buf to enable nested calls to ficlExec
+ */
+ oldState = pVM->pState;
+ pVM->pState = &vmState; /* This has to come before the setjmp! */
+ except = setjmp(vmState);
+
+ switch (except)
+ {
+ case 0:
+ if (pVM->fRestart)
+ {
+ pVM->runningWord->code(pVM);
+ pVM->fRestart = 0;
+ }
+ else
+ { /* set VM up to interpret text */
+ vmPushIP(pVM, &(pSys->pInterp[0]));
+ }
+
+ vmInnerLoop(pVM);
+ break;
+
+ case VM_RESTART:
+ pVM->fRestart = 1;
+ except = VM_OUTOFTEXT;
+ break;
+
+ case VM_OUTOFTEXT:
+ vmPopIP(pVM);
+ if ((pVM->state != COMPILE) && (pVM->sourceID.i == 0))
+ ficlTextOut(pVM, FICL_PROMPT, 0);
+ break;
+
+ case VM_USEREXIT:
+ case VM_INNEREXIT:
+ case VM_BREAK:
+ break;
+
+ case VM_QUIT:
+ if (pVM->state == COMPILE)
+ {
+ dictAbortDefinition(dp);
+#if FICL_WANT_LOCALS
+ dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
+#endif
+ }
+ vmQuit(pVM);
+ break;
+
+ case VM_ERREXIT:
+ case VM_ABORT:
+ case VM_ABORTQ:
+ default: /* user defined exit code?? */
+ if (pVM->state == COMPILE)
+ {
+ dictAbortDefinition(dp);
+#if FICL_WANT_LOCALS
+ dictEmpty(pSys->localp, pSys->localp->pForthWords->size);
+#endif
+ }
+ dictResetSearchOrder(dp);
+ vmReset(pVM);
+ break;
+ }
+
+ pVM->pState = oldState;
+ vmPopTib(pVM, &saveTib);
+ return (except);
+}
+
+
+/**************************************************************************
+ f i c l E x e c X T
+** Given a pointer to a FICL_WORD, push an inner interpreter and
+** execute the word to completion. This is in contrast with vmExecute,
+** which does not guarantee that the word will have completed when
+** the function returns (ie in the case of colon definitions, which
+** need an inner interpreter to finish)
+**
+** Returns one of the VM_XXXX exception codes listed in ficl.h. Normal
+** exit condition is VM_INNEREXIT, ficl's private signal to exit the
+** inner loop under normal circumstances. If another code is thrown to
+** exit the loop, this function will re-throw it if it's nested under
+** itself or ficlExec.
+**
+** NOTE: this function is intended so that C code can execute ficlWords
+** given their address in the dictionary (xt).
+**************************************************************************/
+int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord)
+{
+ int except;
+ jmp_buf vmState;
+ jmp_buf *oldState;
+ FICL_WORD *oldRunningWord;
+
+ assert(pVM);
+ assert(pVM->pSys->pExitInner);
+
+ /*
+ ** Save the runningword so that RESTART behaves correctly
+ ** over nested calls.
+ */
+ oldRunningWord = pVM->runningWord;
+ /*
+ ** Save and restore VM's jmp_buf to enable nested calls
+ */
+ oldState = pVM->pState;
+ pVM->pState = &vmState; /* This has to come before the setjmp! */
+ except = setjmp(vmState);
+
+ if (except)
+ vmPopIP(pVM);
+ else
+ vmPushIP(pVM, &(pVM->pSys->pExitInner));
+
+ switch (except)
+ {
+ case 0:
+ vmExecute(pVM, pWord);
+ vmInnerLoop(pVM);
+ break;
+
+ case VM_INNEREXIT:
+ case VM_BREAK:
+ break;
+
+ case VM_RESTART:
+ case VM_OUTOFTEXT:
+ case VM_USEREXIT:
+ case VM_QUIT:
+ case VM_ERREXIT:
+ case VM_ABORT:
+ case VM_ABORTQ:
+ default: /* user defined exit code?? */
+ if (oldState)
+ {
+ pVM->pState = oldState;
+ vmThrow(pVM, except);
+ }
+ break;
+ }
+
+ pVM->pState = oldState;
+ pVM->runningWord = oldRunningWord;
+ return (except);
+}
+
+
+/**************************************************************************
+ f i c l L o o k u p
+** Look in the system dictionary for a match to the given name. If
+** found, return the address of the corresponding FICL_WORD. Otherwise
+** return NULL.
+**************************************************************************/
+FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name)
+{
+ STRINGINFO si;
+ SI_PSZ(si, name);
+ return dictLookup(pSys->dp, si);
+}
+
+
+/**************************************************************************
+ f i c l G e t D i c t
+** Returns the address of the system dictionary
+**************************************************************************/
+FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys)
+{
+ return pSys->dp;
+}
+
+
+/**************************************************************************
+ f i c l G e t E n v
+** Returns the address of the system environment space
+**************************************************************************/
+FICL_DICT *ficlGetEnv(FICL_SYSTEM *pSys)
+{
+ return pSys->envp;
+}
+
+
+/**************************************************************************
+ f i c l S e t E n v
+** Create an environment variable with a one-CELL payload. ficlSetEnvD
+** makes one with a two-CELL payload.
+**************************************************************************/
+void ficlSetEnv(FICL_SYSTEM *pSys, char *name, FICL_UNS value)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW;
+ FICL_DICT *envp = pSys->envp;
+
+ SI_PSZ(si, name);
+ pFW = dictLookup(envp, si);
+
+ if (pFW == NULL)
+ {
+ dictAppendWord(envp, name, constantParen, FW_DEFAULT);
+ dictAppendCell(envp, LVALUEtoCELL(value));
+ }
+ else
+ {
+ pFW->param[0] = LVALUEtoCELL(value);
+ }
+
+ return;
+}
+
+void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, FICL_UNS hi, FICL_UNS lo)
+{
+ FICL_WORD *pFW;
+ STRINGINFO si;
+ FICL_DICT *envp = pSys->envp;
+ SI_PSZ(si, name);
+ pFW = dictLookup(envp, si);
+
+ if (pFW == NULL)
+ {
+ dictAppendWord(envp, name, twoConstParen, FW_DEFAULT);
+ dictAppendCell(envp, LVALUEtoCELL(lo));
+ dictAppendCell(envp, LVALUEtoCELL(hi));
+ }
+ else
+ {
+ pFW->param[0] = LVALUEtoCELL(lo);
+ pFW->param[1] = LVALUEtoCELL(hi);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ f i c l G e t L o c
+** Returns the address of the system locals dictionary. This dict is
+** only used during compilation, and is shared by all VMs.
+**************************************************************************/
+#if FICL_WANT_LOCALS
+FICL_DICT *ficlGetLoc(FICL_SYSTEM *pSys)
+{
+ return pSys->localp;
+}
+#endif
+
+
+
+/**************************************************************************
+ f i c l S e t S t a c k S i z e
+** Set the stack sizes (return and parameter) to be used for all
+** subsequently created VMs. Returns actual stack size to be used.
+**************************************************************************/
+int ficlSetStackSize(int nStackCells)
+{
+ if (nStackCells >= FICL_DEFAULT_STACK)
+ defaultStack = nStackCells;
+ else
+ defaultStack = FICL_DEFAULT_STACK;
+
+ return defaultStack;
+}
+
+
+/**************************************************************************
+ f i c l T e r m S y s t e m
+** Tear the system down by deleting the dictionaries and all VMs.
+** This saves you from having to keep track of all that stuff.
+**************************************************************************/
+void ficlTermSystem(FICL_SYSTEM *pSys)
+{
+ if (pSys->dp)
+ dictDelete(pSys->dp);
+ pSys->dp = NULL;
+
+ if (pSys->envp)
+ dictDelete(pSys->envp);
+ pSys->envp = NULL;
+
+#if FICL_WANT_LOCALS
+ if (pSys->localp)
+ dictDelete(pSys->localp);
+ pSys->localp = NULL;
+#endif
+
+ while (pSys->vmList != NULL)
+ {
+ FICL_VM *pVM = pSys->vmList;
+ pSys->vmList = pSys->vmList->link;
+ vmDelete(pVM);
+ }
+
+ ficlFree(pSys);
+ pSys = NULL;
+ return;
+}
+
+
+/**************************************************************************
+ f i c l S e t V e r s i o n E n v
+** Create a double cell environment constant for the version ID
+**************************************************************************/
+static void ficlSetVersionEnv(FICL_SYSTEM *pSys)
+{
+ int major = 0;
+ int minor = 0;
+ sscanf(FICL_VER, "%d.%d", &major, &minor);
+ ficlSetEnvD(pSys, "ficl-version", major, minor);
+ ficlSetEnv (pSys, "ficl-robust", FICL_ROBUST);
+ return;
+}
+
diff --git a/ficl.dsp b/ficl.dsp
new file mode 100644
index 000000000000..ec661b830928
--- /dev/null
+++ b/ficl.dsp
@@ -0,0 +1,301 @@
+# Microsoft Developer Studio Project File - Name="ficl" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=ficl - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "ficl.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "ficl.mak" CFG="ficl - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ficl - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "ficl - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ficl - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "release"
+# PROP Intermediate_Dir "release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W4 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# SUBTRACT CPP /Fr /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "ficl - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "debug"
+# PROP Intermediate_Dir "debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /Za /W4 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "ficl - Win32 Release"
+# Name "ficl - Win32 Debug"
+# Begin Group "Sources"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\dict.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ficl.c
+
+!IF "$(CFG)" == "ficl - Win32 Release"
+
+# ADD CPP /FAcs
+
+!ELSEIF "$(CFG)" == "ficl - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\fileaccess.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\float.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\math64.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\prefix.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\search.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\softcore.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\stack.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sysdep.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tools.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\vm.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32.c
+
+!IF "$(CFG)" == "ficl - Win32 Release"
+
+!ELSEIF "$(CFG)" == "ficl - Win32 Debug"
+
+# ADD CPP /Ze
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\words.c
+# End Source File
+# End Group
+# Begin Group "Headers"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\ficl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\math64.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\sysdep.h
+# End Source File
+# End Group
+# Begin Group "softcore"
+
+# PROP Default_Filter ".fr"
+# Begin Source File
+
+SOURCE=.\softwords\classes.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\ficlclass.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\ficllocal.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\fileaccess.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\forml.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\ifbrack.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\jhlocal.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\marker.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\oo.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\prefix.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\softcore.bat
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\softcore.fr
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\softcore.pl
+# End Source File
+# Begin Source File
+
+SOURCE=.\softwords\string.fr
+# End Source File
+# End Group
+# Begin Group "doc"
+
+# PROP Default_Filter "*.html. *.txt"
+# Begin Source File
+
+SOURCE=.\doc\ficl.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\ficl_ans.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\ficl_debug.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\ficl_guts.htm
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\ficl_loc.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\ficl_oop.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\ficl_parse.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\ficl_rel.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\ficlheader.js
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\index.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\oo_in_c.html
+# End Source File
+# Begin Source File
+
+SOURCE=.\doc\primer.txt
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# End Target
+# End Project
diff --git a/ficl.dsw b/ficl.dsw
new file mode 100644
index 000000000000..dfa2a266742e
--- /dev/null
+++ b/ficl.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "ficl"=.\ficl.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/ficl.h b/ficl.h
new file mode 100644
index 000000000000..387259d0a287
--- /dev/null
+++ b/ficl.h
@@ -0,0 +1,1117 @@
+/*******************************************************************
+** f i c l . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** Dedicated to RHS, in loving memory
+** $Id: ficl.h,v 1.19 2001-12-04 17:58:07-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#if !defined (__FICL_H__)
+#define __FICL_H__
+/*
+** Ficl (Forth-inspired command language) is an ANS Forth
+** interpreter written in C. Unlike traditional Forths, this
+** interpreter is designed to be embedded into other systems
+** as a command/macro/development prototype language.
+**
+** Where Forths usually view themselves as the center of the system
+** and expect the rest of the system to be coded in Forth, Ficl
+** acts as a component of the system. It is easy to export
+** code written in C or ASM to Ficl in the style of TCL, or to invoke
+** Ficl code from a compiled module. This allows you to do incremental
+** development in a way that combines the best features of threaded
+** languages (rapid development, quick code/test/debug cycle,
+** reasonably fast) with the best features of C (everyone knows it,
+** easier to support large blocks of code, efficient, type checking).
+**
+** Ficl provides facilities for interoperating
+** with programs written in C: C functions can be exported to Ficl,
+** and Ficl commands can be executed via a C calling interface. The
+** interpreter is re-entrant, so it can be used in multiple instances
+** in a multitasking system. Unlike Forth, Ficl's outer interpreter
+** expects a text block as input, and returns to the caller after each
+** text block, so the "data pump" is somewhere in external code. This
+** is more like TCL than Forth, which usually expcets to be at the center
+** of the system, requesting input at its convenience. Each Ficl virtual
+** machine can be bound to a different I/O channel, and is independent
+** of all others in in the same address space except that all virtual
+** machines share a common dictionary (a sort or open symbol table that
+** defines all of the elements of the language).
+**
+** Code is written in ANSI C for portability.
+**
+** Summary of Ficl features and constraints:
+** - Standard: Implements the ANSI Forth CORE word set and part
+** of the CORE EXT word-set, SEARCH and SEARCH EXT, TOOLS and
+** TOOLS EXT, LOCAL and LOCAL ext and various extras.
+** - Extensible: you can export code written in Forth, C,
+** or asm in a straightforward way. Ficl provides open
+** facilities for extending the language in an application
+** specific way. You can even add new control structures!
+** - Ficl and C can interact in two ways: Ficl can encapsulate
+** C code, or C code can invoke Ficl code.
+** - Thread-safe, re-entrant: The shared system dictionary
+** uses a locking mechanism that you can either supply
+** or stub out to provide exclusive access. Each Ficl
+** virtual machine has an otherwise complete state, and
+** each can be bound to a separate I/O channel (or none at all).
+** - Simple encapsulation into existing systems: a basic implementation
+** requires three function calls (see the example program in testmain.c).
+** - ROMable: Ficl is designed to work in RAM-based and ROM code / RAM data
+** environments. It does require somewhat more memory than a pure
+** ROM implementation because it builds its system dictionary in
+** RAM at startup time.
+** - Written an ANSI C to be as simple as I can make it to understand,
+** support, debug, and port. Compiles without complaint at /Az /W4
+** (require ANSI C, max warnings) under Microsoft VC++ 5.
+** - Does full 32 bit math (but you need to implement
+** two mixed precision math primitives (see sysdep.c))
+** - Indirect threaded interpreter is not the fastest kind of
+** Forth there is (see pForth 68K for a really fast subroutine
+** threaded interpreter), but it's the cleanest match to a
+** pure C implementation.
+**
+** P O R T I N G F i c l
+**
+** To install Ficl on your target system, you need an ANSI C compiler
+** and its runtime library. Inspect the system dependent macros and
+** functions in sysdep.h and sysdep.c and edit them to suit your
+** system. For example, INT16 is a short on some compilers and an
+** int on others. Check the default CELL alignment controlled by
+** FICL_ALIGN. If necessary, add new definitions of ficlMalloc, ficlFree,
+** ficlLockDictionary, and ficlTextOut to work with your operating system.
+** Finally, use testmain.c as a guide to installing the Ficl system and
+** one or more virtual machines into your code. You do not need to include
+** testmain.c in your build.
+**
+** T o D o L i s t
+**
+** 1. Unimplemented system dependent CORE word: key
+** 2. Ficl uses the PAD in some CORE words - this violates the standard,
+** but it's cleaner for a multithreaded system. I'll have to make a
+** second pad for reference by the word PAD to fix this.
+**
+** F o r M o r e I n f o r m a t i o n
+**
+** Web home of ficl
+** http://ficl.sourceforge.net
+** Check this website for Forth literature (including the ANSI standard)
+** http://www.taygeta.com/forthlit.html
+** and here for software and more links
+** http://www.taygeta.com/forth.html
+**
+** Obvious Performance enhancement opportunities
+** Compile speed
+** - work on interpret speed
+** - turn off locals (FICL_WANT_LOCALS)
+** Interpret speed
+** - Change inner interpreter (and everything else)
+** so that a definition is a list of pointers to functions
+** and inline data rather than pointers to words. This gets
+** rid of vm->runningWord and a level of indirection in the
+** inner loop. I'll look at it for ficl 3.0
+** - Make the main hash table a bigger prime (HASHSIZE)
+** - FORGET about twiddling the hash function - my experience is
+** that that is a waste of time.
+** - Eliminate the need to pass the pVM parameter on the stack
+** by dedicating a register to it. Most words need access to the
+** vm, but the parameter passing overhead can be reduced. One way
+** requires that the host OS have a task switch callout. Create
+** a global variable for the running VM and refer to it in words
+** that need VM access. Alternative: use thread local storage.
+** For single threaded implementations, you can just use a global.
+** The first two solutions create portability problems, so I
+** haven't considered doing them. Another possibility is to
+** declare the pVm parameter to be "register", and hope the compiler
+** pays attention.
+**
+*/
+
+/*
+** Revision History:
+**
+** 15 Apr 1999 (sadler) Merged FreeBSD changes for exception wordset and
+** counted strings in ficlExec.
+** 12 Jan 1999 (sobral) Corrected EVALUATE behavior. Now TIB has an
+** "end" field, and all words respect this. ficlExec is passed a "size"
+** of TIB, as well as vmPushTib. This size is used to calculate the "end"
+** of the string, ie, base+size. If the size is not known, pass -1.
+**
+** 10 Jan 1999 (sobral) EXCEPTION word set has been added, and existing
+** words has been modified to conform to EXCEPTION EXT word set.
+**
+** 27 Aug 1998 (sadler) testing and corrections for LOCALS, LOCALS EXT,
+** SEARCH / SEARCH EXT, TOOLS / TOOLS EXT.
+** Added .X to display in hex, PARSE and PARSE-WORD to supplement WORD,
+** EMPTY to clear stack.
+**
+** 29 jun 1998 (sadler) added variable sized hash table support
+** and ANS Forth optional SEARCH & SEARCH EXT word set.
+** 26 May 1998 (sadler)
+** FICL_PROMPT macro
+** 14 April 1998 (sadler) V1.04
+** Ficlwin: Windows version, Skip Carter's Linux port
+** 5 March 1998 (sadler) V1.03
+** Bug fixes -- passes John Ryan's ANS test suite "core.fr"
+**
+** 24 February 1998 (sadler) V1.02
+** -Fixed bugs in <# # #>
+** -Changed FICL_WORD so that storage for the name characters
+** can be allocated from the dictionary as needed rather than
+** reserving 32 bytes in each word whether needed or not -
+** this saved 50% of the dictionary storage requirement.
+** -Added words in testmain for Win32 functions system,chdir,cwd,
+** also added a word that loads and evaluates a file.
+**
+** December 1997 (sadler)
+** -Added VM_RESTART exception handling in ficlExec -- this lets words
+** that require additional text to succeed (like :, create, variable...)
+** recover gracefully from an empty input buffer rather than emitting
+** an error message. Definitions can span multiple input blocks with
+** no restrictions.
+** -Changed #include order so that <assert.h> is included in sysdep.h,
+** and sysdep is included in all other files. This lets you define
+** NDEBUG in sysdep.h to disable assertions if you want to.
+** -Make PC specific system dependent code conditional on _M_IX86
+** defined so that ports can coexist in sysdep.h/sysdep.c
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "sysdep.h"
+#include <limits.h> /* UCHAR_MAX */
+#include <stdio.h>
+
+/*
+** Forward declarations... read on.
+*/
+struct ficl_word;
+typedef struct ficl_word FICL_WORD;
+struct vm;
+typedef struct vm FICL_VM;
+struct ficl_dict;
+typedef struct ficl_dict FICL_DICT;
+struct ficl_system;
+typedef struct ficl_system FICL_SYSTEM;
+struct ficl_system_info;
+typedef struct ficl_system_info FICL_SYSTEM_INFO;
+
+/*
+** the Good Stuff starts here...
+*/
+#define FICL_VER "3.03"
+#if !defined (FICL_PROMPT)
+#define FICL_PROMPT "ok> "
+#endif
+
+/*
+** ANS Forth requires false to be zero, and true to be the ones
+** complement of false... that unifies logical and bitwise operations
+** nicely.
+*/
+#define FICL_TRUE ((unsigned long)~(0L))
+#define FICL_FALSE (0)
+#define FICL_BOOL(x) ((x) ? FICL_TRUE : FICL_FALSE)
+
+
+/*
+** A CELL is the main storage type. It must be large enough
+** to contain a pointer or a scalar. In order to accommodate
+** 32 bit and 64 bit processors, use abstract types for int,
+** unsigned, and float.
+*/
+typedef union _cell
+{
+ FICL_INT i;
+ FICL_UNS u;
+#if (FICL_WANT_FLOAT)
+ FICL_FLOAT f;
+#endif
+ void *p;
+ void (*fn)(void);
+} CELL;
+
+/*
+** LVALUEtoCELL does a little pointer trickery to cast any CELL sized
+** lvalue (informal definition: an expression whose result has an
+** address) to CELL. Remember that constants and casts are NOT
+** themselves lvalues!
+*/
+#define LVALUEtoCELL(v) (*(CELL *)&v)
+
+/*
+** PTRtoCELL is a cast through void * intended to satisfy the
+** most outrageously pedantic compiler... (I won't mention
+** its name)
+*/
+#define PTRtoCELL (CELL *)(void *)
+#define PTRtoSTRING (FICL_STRING *)(void *)
+
+/*
+** Strings in FICL are stored in Pascal style - with a count
+** preceding the text. We'll also NULL-terminate them so that
+** they work with the usual C lib string functions. (Belt &
+** suspenders? You decide.)
+** STRINGINFO hides the implementation with a couple of
+** macros for use in internal routines.
+*/
+
+typedef unsigned char FICL_COUNT;
+#define FICL_STRING_MAX UCHAR_MAX
+typedef struct _ficl_string
+{
+ FICL_COUNT count;
+ char text[1];
+} FICL_STRING;
+
+typedef struct
+{
+ FICL_UNS count;
+ char *cp;
+} STRINGINFO;
+
+#define SI_COUNT(si) (si.count)
+#define SI_PTR(si) (si.cp)
+#define SI_SETLEN(si, len) (si.count = (FICL_UNS)(len))
+#define SI_SETPTR(si, ptr) (si.cp = (char *)(ptr))
+/*
+** Init a STRINGINFO from a pointer to NULL-terminated string
+*/
+#define SI_PSZ(si, psz) \
+ {si.cp = psz; si.count = (FICL_COUNT)strlen(psz);}
+/*
+** Init a STRINGINFO from a pointer to FICL_STRING
+*/
+#define SI_PFS(si, pfs) \
+ {si.cp = pfs->text; si.count = pfs->count;}
+
+/*
+** Ficl uses this little structure to hold the address of
+** the block of text it's working on and an index to the next
+** unconsumed character in the string. Traditionally, this is
+** done by a Text Input Buffer, so I've called this struct TIB.
+**
+** Since this structure also holds the size of the input buffer,
+** and since evaluate requires that, let's put the size here.
+** The size is stored as an end-pointer because that is what the
+** null-terminated string aware functions find most easy to deal
+** with.
+** Notice, though, that nobody really uses this except evaluate,
+** so it might just be moved to FICL_VM instead. (sobral)
+*/
+typedef struct
+{
+ FICL_INT index;
+ char *end;
+ char *cp;
+} TIB;
+
+
+/*
+** Stacks get heavy use in Ficl and Forth...
+** Each virtual machine implements two of them:
+** one holds parameters (data), and the other holds return
+** addresses and control flow information for the virtual
+** machine. (Note: C's automatic stack is implicitly used,
+** but not modeled because it doesn't need to be...)
+** Here's an abstract type for a stack
+*/
+typedef struct _ficlStack
+{
+ FICL_UNS nCells; /* size of the stack */
+ CELL *pFrame; /* link reg for stack frame */
+ CELL *sp; /* stack pointer */
+ CELL base[1]; /* Top of stack */
+} FICL_STACK;
+
+/*
+** Stack methods... many map closely to required Forth words.
+*/
+FICL_STACK *stackCreate (unsigned nCells);
+void stackDelete (FICL_STACK *pStack);
+int stackDepth (FICL_STACK *pStack);
+void stackDrop (FICL_STACK *pStack, int n);
+CELL stackFetch (FICL_STACK *pStack, int n);
+CELL stackGetTop (FICL_STACK *pStack);
+void stackLink (FICL_STACK *pStack, int nCells);
+void stackPick (FICL_STACK *pStack, int n);
+CELL stackPop (FICL_STACK *pStack);
+void *stackPopPtr (FICL_STACK *pStack);
+FICL_UNS stackPopUNS (FICL_STACK *pStack);
+FICL_INT stackPopINT (FICL_STACK *pStack);
+void stackPush (FICL_STACK *pStack, CELL c);
+void stackPushPtr (FICL_STACK *pStack, void *ptr);
+void stackPushUNS (FICL_STACK *pStack, FICL_UNS u);
+void stackPushINT (FICL_STACK *pStack, FICL_INT i);
+void stackReset (FICL_STACK *pStack);
+void stackRoll (FICL_STACK *pStack, int n);
+void stackSetTop (FICL_STACK *pStack, CELL c);
+void stackStore (FICL_STACK *pStack, int n, CELL c);
+void stackUnlink (FICL_STACK *pStack);
+
+#if (FICL_WANT_FLOAT)
+float stackPopFloat (FICL_STACK *pStack);
+void stackPushFloat(FICL_STACK *pStack, FICL_FLOAT f);
+#endif
+
+/*
+** Shortcuts (Guy Carver)
+*/
+#define PUSHPTR(p) stackPushPtr(pVM->pStack,p)
+#define PUSHUNS(u) stackPushUNS(pVM->pStack,u)
+#define PUSHINT(i) stackPushINT(pVM->pStack,i)
+#define PUSHFLOAT(f) stackPushFloat(pVM->fStack,f)
+#define PUSH(c) stackPush(pVM->pStack,c)
+#define POPPTR() stackPopPtr(pVM->pStack)
+#define POPUNS() stackPopUNS(pVM->pStack)
+#define POPINT() stackPopINT(pVM->pStack)
+#define POPFLOAT() stackPopFloat(pVM->fStack)
+#define POP() stackPop(pVM->pStack)
+#define GETTOP() stackGetTop(pVM->pStack)
+#define SETTOP(c) stackSetTop(pVM->pStack,LVALUEtoCELL(c))
+#define GETTOPF() stackGetTop(pVM->fStack)
+#define SETTOPF(c) stackSetTop(pVM->fStack,LVALUEtoCELL(c))
+#define STORE(n,c) stackStore(pVM->pStack,n,LVALUEtoCELL(c))
+#define DEPTH() stackDepth(pVM->pStack)
+#define DROP(n) stackDrop(pVM->pStack,n)
+#define DROPF(n) stackDrop(pVM->fStack,n)
+#define FETCH(n) stackFetch(pVM->pStack,n)
+#define PICK(n) stackPick(pVM->pStack,n)
+#define PICKF(n) stackPick(pVM->fStack,n)
+#define ROLL(n) stackRoll(pVM->pStack,n)
+#define ROLLF(n) stackRoll(pVM->fStack,n)
+
+/*
+** The virtual machine (VM) contains the state for one interpreter.
+** Defined operations include:
+** Create & initialize
+** Delete
+** Execute a block of text
+** Parse a word out of the input stream
+** Call return, and branch
+** Text output
+** Throw an exception
+*/
+
+typedef FICL_WORD ** IPTYPE; /* the VM's instruction pointer */
+
+/*
+** Each VM has a placeholder for an output function -
+** this makes it possible to have each VM do I/O
+** through a different device. If you specify no
+** OUTFUNC, it defaults to ficlTextOut.
+*/
+typedef void (*OUTFUNC)(FICL_VM *pVM, char *text, int fNewline);
+
+/*
+** Each VM operates in one of two non-error states: interpreting
+** or compiling. When interpreting, words are simply executed.
+** When compiling, most words in the input stream have their
+** addresses inserted into the word under construction. Some words
+** (known as IMMEDIATE) are executed in the compile state, too.
+*/
+/* values of STATE */
+#define INTERPRET 0
+#define COMPILE 1
+
+/*
+** The pad is a small scratch area for text manipulation. ANS Forth
+** requires it to hold at least 84 characters.
+*/
+#if !defined nPAD
+#define nPAD 256
+#endif
+
+/*
+** ANS Forth requires that a word's name contain {1..31} characters.
+*/
+#if !defined nFICLNAME
+#define nFICLNAME 31
+#endif
+
+/*
+** OK - now we can really define the VM...
+*/
+struct vm
+{
+ FICL_SYSTEM *pSys; /* Which system this VM belongs to */
+ FICL_VM *link; /* Ficl keeps a VM list for simple teardown */
+ jmp_buf *pState; /* crude exception mechanism... */
+ OUTFUNC textOut; /* Output callback - see sysdep.c */
+ void * pExtend; /* vm extension pointer for app use - initialized from FICL_SYSTEM */
+ short fRestart; /* Set TRUE to restart runningWord */
+ IPTYPE ip; /* instruction pointer */
+ FICL_WORD *runningWord;/* address of currently running word (often just *(ip-1) ) */
+ FICL_UNS state; /* compiling or interpreting */
+ FICL_UNS base; /* number conversion base */
+ FICL_STACK *pStack; /* param stack */
+ FICL_STACK *rStack; /* return stack */
+#if FICL_WANT_FLOAT
+ FICL_STACK *fStack; /* float stack (optional) */
+#endif
+ CELL sourceID; /* -1 if EVALUATE, 0 if normal input */
+ TIB tib; /* address of incoming text string */
+#if FICL_WANT_USER
+ CELL user[FICL_USER_CELLS];
+#endif
+ char pad[nPAD]; /* the scratch area (see above) */
+};
+
+/*
+** A FICL_CODE points to a function that gets called to help execute
+** a word in the dictionary. It always gets passed a pointer to the
+** running virtual machine, and from there it can get the address
+** of the parameter area of the word it's supposed to operate on.
+** For precompiled words, the code is all there is. For user defined
+** words, the code assumes that the word's parameter area is a list
+** of pointers to the code fields of other words to execute, and
+** may also contain inline data. The first parameter is always
+** a pointer to a code field.
+*/
+typedef void (*FICL_CODE)(FICL_VM *pVm);
+
+#if 0
+#define VM_ASSERT(pVM) assert((*(pVM->ip - 1)) == pVM->runningWord)
+#else
+#define VM_ASSERT(pVM)
+#endif
+
+/*
+** Ficl models memory as a contiguous space divided into
+** words in a linked list called the dictionary.
+** A FICL_WORD starts each entry in the list.
+** Version 1.02: space for the name characters is allotted from
+** the dictionary ahead of the word struct, rather than using
+** a fixed size array for each name.
+*/
+struct ficl_word
+{
+ struct ficl_word *link; /* Previous word in the dictionary */
+ UNS16 hash;
+ UNS8 flags; /* Immediate, Smudge, Compile-only */
+ FICL_COUNT nName; /* Number of chars in word name */
+ char *name; /* First nFICLNAME chars of word name */
+ FICL_CODE code; /* Native code to execute the word */
+ CELL param[1]; /* First data cell of the word */
+};
+
+/*
+** Worst-case size of a word header: nFICLNAME chars in name
+*/
+#define CELLS_PER_WORD \
+ ( (sizeof (FICL_WORD) + nFICLNAME + sizeof (CELL)) \
+ / (sizeof (CELL)) )
+
+int wordIsImmediate(FICL_WORD *pFW);
+int wordIsCompileOnly(FICL_WORD *pFW);
+
+/* flag values for word header */
+#define FW_IMMEDIATE 1 /* execute me even if compiling */
+#define FW_COMPILE 2 /* error if executed when not compiling */
+#define FW_SMUDGE 4 /* definition in progress - hide me */
+#define FW_ISOBJECT 8 /* word is an object or object member variable */
+
+#define FW_COMPIMMED (FW_IMMEDIATE | FW_COMPILE)
+#define FW_DEFAULT 0
+
+
+/*
+** Exit codes for vmThrow
+*/
+#define VM_INNEREXIT -256 /* tell ficlExecXT to exit inner loop */
+#define VM_OUTOFTEXT -257 /* hungry - normal exit */
+#define VM_RESTART -258 /* word needs more text to succeed - re-run it */
+#define VM_USEREXIT -259 /* user wants to quit */
+#define VM_ERREXIT -260 /* interp found an error */
+#define VM_BREAK -261 /* debugger breakpoint */
+#define VM_ABORT -1 /* like errexit -- abort */
+#define VM_ABORTQ -2 /* like errexit -- abort" */
+#define VM_QUIT -56 /* like errexit, but leave pStack & base alone */
+
+
+void vmBranchRelative(FICL_VM *pVM, int offset);
+FICL_VM * vmCreate (FICL_VM *pVM, unsigned nPStack, unsigned nRStack);
+void vmDelete (FICL_VM *pVM);
+void vmExecute (FICL_VM *pVM, FICL_WORD *pWord);
+FICL_DICT *vmGetDict (FICL_VM *pVM);
+char * vmGetString (FICL_VM *pVM, FICL_STRING *spDest, char delimiter);
+STRINGINFO vmGetWord (FICL_VM *pVM);
+STRINGINFO vmGetWord0 (FICL_VM *pVM);
+int vmGetWordToPad (FICL_VM *pVM);
+STRINGINFO vmParseString (FICL_VM *pVM, char delimiter);
+STRINGINFO vmParseStringEx(FICL_VM *pVM, char delimiter, char fSkipLeading);
+CELL vmPop (FICL_VM *pVM);
+void vmPush (FICL_VM *pVM, CELL c);
+void vmPopIP (FICL_VM *pVM);
+void vmPushIP (FICL_VM *pVM, IPTYPE newIP);
+void vmQuit (FICL_VM *pVM);
+void vmReset (FICL_VM *pVM);
+void vmSetTextOut (FICL_VM *pVM, OUTFUNC textOut);
+void vmTextOut (FICL_VM *pVM, char *text, int fNewline);
+void vmTextOut (FICL_VM *pVM, char *text, int fNewline);
+void vmThrow (FICL_VM *pVM, int except);
+void vmThrowErr (FICL_VM *pVM, char *fmt, ...);
+
+#define vmGetRunningWord(pVM) ((pVM)->runningWord)
+
+
+/*
+** The inner interpreter - coded as a macro (see note for
+** INLINE_INNER_LOOP in sysdep.h for complaints about VC++ 5
+*/
+#define M_VM_STEP(pVM) \
+ FICL_WORD *tempFW = *(pVM)->ip++; \
+ (pVM)->runningWord = tempFW; \
+ tempFW->code(pVM);
+
+#define M_INNER_LOOP(pVM) \
+ for (;;) { M_VM_STEP(pVM) }
+
+
+#if INLINE_INNER_LOOP != 0
+#define vmInnerLoop(pVM) M_INNER_LOOP(pVM)
+#else
+void vmInnerLoop(FICL_VM *pVM);
+#endif
+
+/*
+** vmCheckStack needs a vm pointer because it might have to say
+** something if it finds a problem. Parms popCells and pushCells
+** correspond to the number of parameters on the left and right of
+** a word's stack effect comment.
+*/
+void vmCheckStack(FICL_VM *pVM, int popCells, int pushCells);
+#if FICL_WANT_FLOAT
+void vmCheckFStack(FICL_VM *pVM, int popCells, int pushCells);
+#endif
+
+/*
+** TIB access routines...
+** ANS forth seems to require the input buffer to be represented
+** as a pointer to the start of the buffer, and an index to the
+** next character to read.
+** PushTib points the VM to a new input string and optionally
+** returns a copy of the current state
+** PopTib restores the TIB state given a saved TIB from PushTib
+** GetInBuf returns a pointer to the next unused char of the TIB
+*/
+void vmPushTib (FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib);
+void vmPopTib (FICL_VM *pVM, TIB *pTib);
+#define vmGetInBuf(pVM) ((pVM)->tib.cp + (pVM)->tib.index)
+#define vmGetInBufLen(pVM) ((pVM)->tib.end - (pVM)->tib.cp)
+#define vmGetInBufEnd(pVM) ((pVM)->tib.end)
+#define vmGetTibIndex(pVM) (pVM)->tib.index
+#define vmSetTibIndex(pVM, i) (pVM)->tib.index = i
+#define vmUpdateTib(pVM, str) (pVM)->tib.index = (str) - (pVM)->tib.cp
+
+/*
+** Generally useful string manipulators omitted by ANSI C...
+** ltoa complements strtol
+*/
+#if defined(_WIN32) && !FICL_MAIN
+/* #SHEESH
+** Why do Microsoft Meatballs insist on contaminating
+** my namespace with their string functions???
+*/
+#pragma warning(disable: 4273)
+#endif
+
+int isPowerOfTwo(FICL_UNS u);
+
+char *ltoa( FICL_INT value, char *string, int radix );
+char *ultoa(FICL_UNS value, char *string, int radix );
+char digit_to_char(int value);
+char *strrev( char *string );
+char *skipSpace(char *cp, char *end);
+char *caseFold(char *cp);
+int strincmp(char *cp1, char *cp2, FICL_UNS count);
+
+#if defined(_WIN32) && !FICL_MAIN
+#pragma warning(default: 4273)
+#endif
+
+/*
+** Ficl hash table - variable size.
+** assert(size > 0)
+** If size is 1, the table degenerates into a linked list.
+** A WORDLIST (see the search order word set in DPANS) is
+** just a pointer to a FICL_HASH in this implementation.
+*/
+#if !defined HASHSIZE /* Default size of hash table. For most uniform */
+#define HASHSIZE 241 /* performance, use a prime number! */
+#endif
+
+typedef struct ficl_hash
+{
+ struct ficl_hash *link; /* link to parent class wordlist for OO */
+ char *name; /* optional pointer to \0 terminated wordlist name */
+ unsigned size; /* number of buckets in the hash */
+ FICL_WORD *table[1];
+} FICL_HASH;
+
+void hashForget (FICL_HASH *pHash, void *where);
+UNS16 hashHashCode (STRINGINFO si);
+void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW);
+FICL_WORD *hashLookup (FICL_HASH *pHash, STRINGINFO si, UNS16 hashCode);
+void hashReset (FICL_HASH *pHash);
+
+/*
+** A Dictionary is a linked list of FICL_WORDs. It is also Ficl's
+** memory model. Description of fields:
+**
+** here -- points to the next free byte in the dictionary. This
+** pointer is forced to be CELL-aligned before a definition is added.
+** Do not assume any specific alignment otherwise - Use dictAlign().
+**
+** smudge -- pointer to word currently being defined (or last defined word)
+** If the definition completes successfully, the word will be
+** linked into the hash table. If unsuccessful, dictUnsmudge
+** uses this pointer to restore the previous state of the dictionary.
+** Smudge prevents unintentional recursion as a side-effect: the
+** dictionary search algo examines only completed definitions, so a
+** word cannot invoke itself by name. See the ficl word "recurse".
+** NOTE: smudge always points to the last word defined. IMMEDIATE
+** makes use of this fact. Smudge is initially NULL.
+**
+** pForthWords -- pointer to the default wordlist (FICL_HASH).
+** This is the initial compilation list, and contains all
+** ficl's precompiled words.
+**
+** pCompile -- compilation wordlist - initially equal to pForthWords
+** pSearch -- array of pointers to wordlists. Managed as a stack.
+** Highest index is the first list in the search order.
+** nLists -- number of lists in pSearch. nLists-1 is the highest
+** filled slot in pSearch, and points to the first wordlist
+** in the search order
+** size -- number of cells in the dictionary (total)
+** dict -- start of data area. Must be at the end of the struct.
+*/
+struct ficl_dict
+{
+ CELL *here;
+ FICL_WORD *smudge;
+ FICL_HASH *pForthWords;
+ FICL_HASH *pCompile;
+ FICL_HASH *pSearch[FICL_DEFAULT_VOCS];
+ int nLists;
+ unsigned size; /* Number of cells in dict (total)*/
+ CELL dict[1]; /* Base of dictionary memory */
+};
+
+void *alignPtr(void *ptr);
+void dictAbortDefinition(FICL_DICT *pDict);
+void dictAlign (FICL_DICT *pDict);
+int dictAllot (FICL_DICT *pDict, int n);
+int dictAllotCells (FICL_DICT *pDict, int nCells);
+void dictAppendCell (FICL_DICT *pDict, CELL c);
+void dictAppendChar (FICL_DICT *pDict, char c);
+FICL_WORD *dictAppendWord (FICL_DICT *pDict,
+ char *name,
+ FICL_CODE pCode,
+ UNS8 flags);
+FICL_WORD *dictAppendWord2(FICL_DICT *pDict,
+ STRINGINFO si,
+ FICL_CODE pCode,
+ UNS8 flags);
+void dictAppendUNS (FICL_DICT *pDict, FICL_UNS u);
+int dictCellsAvail (FICL_DICT *pDict);
+int dictCellsUsed (FICL_DICT *pDict);
+void dictCheck (FICL_DICT *pDict, FICL_VM *pVM, int n);
+FICL_DICT *dictCreate(unsigned nCELLS);
+FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash);
+FICL_HASH *dictCreateWordlist(FICL_DICT *dp, int nBuckets);
+void dictDelete (FICL_DICT *pDict);
+void dictEmpty (FICL_DICT *pDict, unsigned nHash);
+#if FICL_WANT_FLOAT
+void dictHashSummary(FICL_VM *pVM);
+#endif
+int dictIncludes (FICL_DICT *pDict, void *p);
+FICL_WORD *dictLookup (FICL_DICT *pDict, STRINGINFO si);
+#if FICL_WANT_LOCALS
+FICL_WORD *ficlLookupLoc (FICL_SYSTEM *pSys, STRINGINFO si);
+#endif
+void dictResetSearchOrder(FICL_DICT *pDict);
+void dictSetFlags (FICL_DICT *pDict, UNS8 set, UNS8 clr);
+void dictSetImmediate(FICL_DICT *pDict);
+void dictUnsmudge (FICL_DICT *pDict);
+CELL *dictWhere (FICL_DICT *pDict);
+
+
+/*
+** P A R S E S T E P
+** (New for 2.05)
+** See words.c: interpWord
+** By default, ficl goes through two attempts to parse each token from its input
+** stream: it first attempts to match it with a word in the dictionary, and
+** if that fails, it attempts to convert it into a number. This mechanism is now
+** extensible by additional steps. This allows extensions like floating point and
+** double number support to be factored cleanly.
+**
+** Each parse step is a function that receives the next input token as a STRINGINFO.
+** If the parse step matches the token, it must apply semantics to the token appropriate
+** to the present value of VM.state (compiling or interpreting), and return FICL_TRUE.
+** Otherwise it returns FICL_FALSE. See words.c: isNumber for an example
+**
+** Note: for the sake of efficiency, it's a good idea both to limit the number
+** of parse steps and to code each parse step so that it rejects tokens that
+** do not match as quickly as possible.
+*/
+
+typedef int (*FICL_PARSE_STEP)(FICL_VM *pVM, STRINGINFO si);
+
+/*
+** Appends a parse step function to the end of the parse list (see
+** FICL_PARSE_STEP notes in ficl.h for details). Returns 0 if successful,
+** nonzero if there's no more room in the list. Each parse step is a word in
+** the dictionary. Precompiled parse steps can use (PARSE-STEP) as their
+** CFA - see parenParseStep in words.c.
+*/
+int ficlAddParseStep(FICL_SYSTEM *pSys, FICL_WORD *pFW); /* ficl.c */
+void ficlAddPrecompiledParseStep(FICL_SYSTEM *pSys, char *name, FICL_PARSE_STEP pStep);
+void ficlListParseSteps(FICL_VM *pVM);
+
+/*
+** FICL_BREAKPOINT record.
+** origXT - if NULL, this breakpoint is unused. Otherwise it stores the xt
+** that the breakpoint overwrote. This is restored to the dictionary when the
+** BP executes or gets cleared
+** address - the location of the breakpoint (address of the instruction that
+** has been replaced with the breakpoint trap
+** origXT - The original contents of the location with the breakpoint
+** Note: address is NULL when this breakpoint is empty
+*/
+typedef struct FICL_BREAKPOINT
+{
+ void *address;
+ FICL_WORD *origXT;
+} FICL_BREAKPOINT;
+
+
+/*
+** F I C L _ S Y S T E M
+** The top level data structure of the system - ficl_system ties a list of
+** virtual machines with their corresponding dictionaries. Ficl 3.0 will
+** support multiple Ficl systems, allowing multiple concurrent sessions
+** to separate dictionaries with some constraints.
+** The present model allows multiple sessions to one dictionary provided
+** you implement ficlLockDictionary() as specified in sysdep.h
+** Note: the pExtend pointer is there to provide context for applications. It is copied
+** to each VM's pExtend field as that VM is created.
+*/
+struct ficl_system
+{
+ FICL_SYSTEM *link;
+ void *pExtend; /* Initializes VM's pExtend pointer (for application use) */
+ FICL_VM *vmList;
+ FICL_DICT *dp;
+ FICL_DICT *envp;
+#ifdef FICL_WANT_LOCALS
+ FICL_DICT *localp;
+#endif
+ FICL_WORD *pInterp[3];
+ FICL_WORD *parseList[FICL_MAX_PARSE_STEPS];
+ OUTFUNC textOut;
+
+ FICL_WORD *pBranchParen;
+ FICL_WORD *pDoParen;
+ FICL_WORD *pDoesParen;
+ FICL_WORD *pExitInner;
+ FICL_WORD *pExitParen;
+ FICL_WORD *pBranch0;
+ FICL_WORD *pInterpret;
+ FICL_WORD *pLitParen;
+ FICL_WORD *pTwoLitParen;
+ FICL_WORD *pLoopParen;
+ FICL_WORD *pPLoopParen;
+ FICL_WORD *pQDoParen;
+ FICL_WORD *pSemiParen;
+ FICL_WORD *pOfParen;
+ FICL_WORD *pStore;
+ FICL_WORD *pDrop;
+ FICL_WORD *pCStringLit;
+ FICL_WORD *pStringLit;
+
+#if FICL_WANT_LOCALS
+ FICL_WORD *pGetLocalParen;
+ FICL_WORD *pGet2LocalParen;
+ FICL_WORD *pGetLocal0;
+ FICL_WORD *pGetLocal1;
+ FICL_WORD *pToLocalParen;
+ FICL_WORD *pTo2LocalParen;
+ FICL_WORD *pToLocal0;
+ FICL_WORD *pToLocal1;
+ FICL_WORD *pLinkParen;
+ FICL_WORD *pUnLinkParen;
+ FICL_INT nLocals;
+ CELL *pMarkLocals;
+#endif
+
+ FICL_BREAKPOINT bpStep;
+};
+
+struct ficl_system_info
+{
+ int size; /* structure size tag for versioning */
+ int nDictCells; /* Size of system's Dictionary */
+ OUTFUNC textOut; /* default textOut function */
+ void *pExtend; /* Initializes VM's pExtend pointer - for application use */
+ int nEnvCells; /* Size of Environment dictionary */
+};
+
+
+#define ficlInitInfo(x) { memset((x), 0, sizeof(FICL_SYSTEM_INFO)); \
+ (x)->size = sizeof(FICL_SYSTEM_INFO); }
+
+/*
+** External interface to FICL...
+*/
+/*
+** f i c l I n i t S y s t e m
+** Binds a global dictionary to the interpreter system and initializes
+** the dict to contain the ANSI CORE wordset.
+** You can specify the address and size of the allocated area.
+** Using ficlInitSystemEx you can also specify the text output function.
+** After that, ficl manages it.
+** First step is to set up the static pointers to the area.
+** Then write the "precompiled" portion of the dictionary in.
+** The dictionary needs to be at least large enough to hold the
+** precompiled part. Try 1K cells minimum. Use "words" to find
+** out how much of the dictionary is used at any time.
+*/
+FICL_SYSTEM *ficlInitSystemEx(FICL_SYSTEM_INFO *fsi);
+
+/* Deprecated call */
+FICL_SYSTEM *ficlInitSystem(int nDictCells);
+
+/*
+** f i c l T e r m S y s t e m
+** Deletes the system dictionary and all virtual machines that
+** were created with ficlNewVM (see below). Call this function to
+** reclaim all memory used by the dictionary and VMs.
+*/
+void ficlTermSystem(FICL_SYSTEM *pSys);
+
+/*
+** f i c l E v a l u a t e
+** Evaluates a block of input text in the context of the
+** specified interpreter. Also sets SOURCE-ID properly.
+**
+** PLEASE USE THIS FUNCTION when throwing a hard-coded
+** string to the FICL interpreter.
+*/
+int ficlEvaluate(FICL_VM *pVM, char *pText);
+
+/*
+** f i c l E x e c
+** Evaluates a block of input text in the context of the
+** specified interpreter. Emits any requested output to the
+** interpreter's output function. If the input string is NULL
+** terminated, you can pass -1 as nChars rather than count it.
+** Execution returns when the text block has been executed,
+** or an error occurs.
+** Returns one of the VM_XXXX codes defined in ficl.h:
+** VM_OUTOFTEXT is the normal exit condition
+** VM_ERREXIT means that the interp encountered a syntax error
+** and the vm has been reset to recover (some or all
+** of the text block got ignored
+** VM_USEREXIT means that the user executed the "bye" command
+** to shut down the interpreter. This would be a good
+** time to delete the vm, etc -- or you can ignore this
+** signal.
+** VM_ABORT and VM_ABORTQ are generated by 'abort' and 'abort"'
+** commands.
+** Preconditions: successful execution of ficlInitSystem,
+** Successful creation and init of the VM by ficlNewVM (or equiv)
+**
+** If you call ficlExec() or one of its brothers, you MUST
+** ensure pVM->sourceID was set to a sensible value.
+** ficlExec() explicitly DOES NOT manage SOURCE-ID for you.
+*/
+int ficlExec (FICL_VM *pVM, char *pText);
+int ficlExecC(FICL_VM *pVM, char *pText, FICL_INT nChars);
+int ficlExecXT(FICL_VM *pVM, FICL_WORD *pWord);
+
+/*
+** Create a new VM from the heap, and link it into the system VM list.
+** Initializes the VM and binds default sized stacks to it. Returns the
+** address of the VM, or NULL if an error occurs.
+** Precondition: successful execution of ficlInitSystem
+*/
+FICL_VM *ficlNewVM(FICL_SYSTEM *pSys);
+
+/*
+** Force deletion of a VM. You do not need to do this
+** unless you're creating and discarding a lot of VMs.
+** For systems that use a constant pool of VMs for the life
+** of the system, ficltermSystem takes care of VM cleanup
+** automatically.
+*/
+void ficlFreeVM(FICL_VM *pVM);
+
+
+/*
+** Set the stack sizes (return and parameter) to be used for all
+** subsequently created VMs. Returns actual stack size to be used.
+*/
+int ficlSetStackSize(int nStackCells);
+
+/*
+** Returns the address of the most recently defined word in the system
+** dictionary with the given name, or NULL if no match.
+** Precondition: successful execution of ficlInitSystem
+*/
+FICL_WORD *ficlLookup(FICL_SYSTEM *pSys, char *name);
+
+/*
+** f i c l G e t D i c t
+** Utility function - returns the address of the system dictionary.
+** Precondition: successful execution of ficlInitSystem
+*/
+FICL_DICT *ficlGetDict(FICL_SYSTEM *pSys);
+FICL_DICT *ficlGetEnv (FICL_SYSTEM *pSys);
+void ficlSetEnv (FICL_SYSTEM *pSys, char *name, FICL_UNS value);
+void ficlSetEnvD(FICL_SYSTEM *pSys, char *name, FICL_UNS hi, FICL_UNS lo);
+#if FICL_WANT_LOCALS
+FICL_DICT *ficlGetLoc (FICL_SYSTEM *pSys);
+#endif
+/*
+** f i c l B u i l d
+** Builds a word into the system default dictionary in a thread-safe way.
+** Preconditions: system must be initialized, and there must
+** be enough space for the new word's header! Operation is
+** controlled by ficlLockDictionary, so any initialization
+** required by your version of the function (if you "overrode"
+** it) must be complete at this point.
+** Parameters:
+** name -- the name of the word to be built
+** code -- code to execute when the word is invoked - must take a single param
+** pointer to a FICL_VM
+** flags -- 0 or more of FW_IMMEDIATE, FW_COMPILE, use bitwise OR!
+** Most words can use FW_DEFAULT.
+** nAllot - number of extra cells to allocate in the parameter area (usually zero)
+*/
+int ficlBuild(FICL_SYSTEM *pSys, char *name, FICL_CODE code, char flags);
+
+/*
+** f i c l C o m p i l e C o r e
+** Builds the ANS CORE wordset into the dictionary - called by
+** ficlInitSystem - no need to waste dict space by doing it again.
+*/
+void ficlCompileCore(FICL_SYSTEM *pSys);
+void ficlCompilePrefix(FICL_SYSTEM *pSys);
+void ficlCompileSearch(FICL_SYSTEM *pSys);
+void ficlCompileSoftCore(FICL_SYSTEM *pSys);
+void ficlCompileTools(FICL_SYSTEM *pSys);
+void ficlCompileFile(FICL_SYSTEM *pSys);
+#if FICL_WANT_FLOAT
+void ficlCompileFloat(FICL_SYSTEM *pSys);
+int ficlParseFloatNumber( FICL_VM *pVM, STRINGINFO si ); /* float.c */
+#endif
+#if FICL_PLATFORM_EXTEND
+void ficlCompilePlatform(FICL_SYSTEM *pSys);
+#endif
+int ficlParsePrefix(FICL_VM *pVM, STRINGINFO si);
+
+/*
+** from words.c...
+*/
+void constantParen(FICL_VM *pVM);
+void twoConstParen(FICL_VM *pVM);
+int ficlParseNumber(FICL_VM *pVM, STRINGINFO si);
+void ficlTick(FICL_VM *pVM);
+void parseStepParen(FICL_VM *pVM);
+
+/*
+** From tools.c
+*/
+int isAFiclWord(FICL_DICT *pd, FICL_WORD *pFW);
+
+/*
+** The following supports SEE and the debugger.
+*/
+typedef enum
+{
+ BRANCH,
+ COLON,
+ CONSTANT,
+ CREATE,
+ DO,
+ DOES,
+ IF,
+ LITERAL,
+ LOOP,
+ OF,
+ PLOOP,
+ PRIMITIVE,
+ QDO,
+ STRINGLIT,
+ CSTRINGLIT,
+#if FICL_WANT_USER
+ USER,
+#endif
+ VARIABLE,
+} WORDKIND;
+
+WORDKIND ficlWordClassify(FICL_WORD *pFW);
+
+
+
+/*
+** Used with File-Access wordset.
+*/
+#define FICL_FAM_READ 1
+#define FICL_FAM_WRITE 2
+#define FICL_FAM_APPEND 4
+#define FICL_FAM_BINARY 8
+
+#define FICL_FAM_OPEN_MODE(fam) ((fam) & (FICL_FAM_READ | FICL_FAM_WRITE | FICL_FAM_APPEND))
+
+
+typedef struct ficlFILE
+{
+ FILE *f;
+ char filename[256];
+} ficlFILE;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FICL_H__ */
diff --git a/fileaccess.c b/fileaccess.c
new file mode 100644
index 000000000000..98cf986ed1c1
--- /dev/null
+++ b/fileaccess.c
@@ -0,0 +1,423 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include "ficl.h"
+
+#if FICL_WANT_FILE
+/*
+**
+** fileaccess.c
+**
+** Implements all of the File Access word set that can be implemented in portable C.
+**
+*/
+
+static void pushIor(FICL_VM *pVM, int success)
+{
+ int ior;
+ if (success)
+ ior = 0;
+ else
+ ior = errno;
+ stackPushINT(pVM->pStack, ior);
+}
+
+
+
+static void ficlFopen(FICL_VM *pVM, char *writeMode) /* ( c-addr u fam -- fileid ior ) */
+{
+ int fam = stackPopINT(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+ char mode[4];
+ FILE *f;
+
+ char *filename = (char *)alloca(length + 1);
+ memcpy(filename, address, length);
+ filename[length] = 0;
+
+ *mode = 0;
+
+ switch (FICL_FAM_OPEN_MODE(fam))
+ {
+ case 0:
+ stackPushPtr(pVM->pStack, NULL);
+ stackPushINT(pVM->pStack, EINVAL);
+ return;
+ case FICL_FAM_READ:
+ strcat(mode, "r");
+ break;
+ case FICL_FAM_WRITE:
+ strcat(mode, writeMode);
+ break;
+ case FICL_FAM_READ | FICL_FAM_WRITE:
+ strcat(mode, writeMode);
+ strcat(mode, "+");
+ break;
+ }
+
+ strcat(mode, (fam & FICL_FAM_BINARY) ? "b" : "t");
+
+ f = fopen(filename, mode);
+ if (f == NULL)
+ stackPushPtr(pVM->pStack, NULL);
+ else
+ {
+ ficlFILE *ff = (ficlFILE *)malloc(sizeof(ficlFILE));
+ strcpy(ff->filename, filename);
+ ff->f = f;
+ stackPushPtr(pVM->pStack, ff);
+
+ fseek(f, 0, SEEK_SET);
+ }
+ pushIor(pVM, f != NULL);
+}
+
+
+
+static void ficlOpenFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
+{
+ ficlFopen(pVM, "a");
+}
+
+
+static void ficlCreateFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
+{
+ ficlFopen(pVM, "w");
+}
+
+
+static int closeFiclFILE(ficlFILE *ff) /* ( fileid -- ior ) */
+{
+ FILE *f = ff->f;
+ free(ff);
+ return !fclose(f);
+}
+
+static void ficlCloseFile(FICL_VM *pVM) /* ( fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ pushIor(pVM, closeFiclFILE(ff));
+}
+
+static void ficlDeleteFile(FICL_VM *pVM) /* ( c-addr u -- ior ) */
+{
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *filename = (char *)alloca(length + 1);
+ memcpy(filename, address, length);
+ filename[length] = 0;
+
+ pushIor(pVM, !unlink(filename));
+}
+
+static void ficlRenameFile(FICL_VM *pVM) /* ( c-addr1 u1 c-addr2 u2 -- ior ) */
+{
+ int length;
+ void *address;
+ char *from;
+ char *to;
+
+ length = stackPopINT(pVM->pStack);
+ address = (void *)stackPopPtr(pVM->pStack);
+ to = (char *)alloca(length + 1);
+ memcpy(to, address, length);
+ to[length] = 0;
+
+ length = stackPopINT(pVM->pStack);
+ address = (void *)stackPopPtr(pVM->pStack);
+
+ from = (char *)alloca(length + 1);
+ memcpy(from, address, length);
+ from[length] = 0;
+
+ pushIor(pVM, !rename(from, to));
+}
+
+static void ficlFileStatus(FICL_VM *pVM) /* ( c-addr u -- x ior ) */
+{
+ struct stat statbuf;
+
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *filename = (char *)alloca(length + 1);
+ memcpy(filename, address, length);
+ filename[length] = 0;
+
+ if (stat(filename, &statbuf) == 0)
+ {
+ /*
+ ** the "x" left on the stack is implementation-defined.
+ ** I push the file's access mode (readable, writeable, is directory, etc)
+ ** as defined by ANSI C.
+ */
+ stackPushINT(pVM->pStack, statbuf.st_mode);
+ stackPushINT(pVM->pStack, 0);
+ }
+ else
+ {
+ stackPushINT(pVM->pStack, -1);
+ stackPushINT(pVM->pStack, ENOENT);
+ }
+}
+
+
+static void ficlFilePosition(FICL_VM *pVM) /* ( fileid -- ud ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ long ud = ftell(ff->f);
+ stackPushINT(pVM->pStack, ud);
+ pushIor(pVM, ud != -1);
+}
+
+
+
+static long fileSize(FILE *f)
+{
+ struct stat statbuf;
+ statbuf.st_size = -1;
+ if (fstat(fileno(f), &statbuf) != 0)
+ return -1;
+ return statbuf.st_size;
+}
+
+
+
+static void ficlFileSize(FICL_VM *pVM) /* ( fileid -- ud ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ long ud = fileSize(ff->f);
+ stackPushINT(pVM->pStack, ud);
+ pushIor(pVM, ud != -1);
+}
+
+
+
+#define nLINEBUF 256
+static void ficlIncludeFile(FICL_VM *pVM) /* ( i*x fileid -- j*x ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ CELL id = pVM->sourceID;
+ int result = VM_OUTOFTEXT;
+ long currentPosition, totalSize;
+ long size;
+ pVM->sourceID.p = (void *)ff;
+
+ currentPosition = ftell(ff->f);
+ totalSize = fileSize(ff->f);
+ size = totalSize - currentPosition;
+
+ if ((totalSize != -1) && (currentPosition != -1) && (size > 0))
+ {
+ char *buffer = (char *)malloc(size);
+ long got = fread(buffer, 1, size, ff->f);
+ if (got == size)
+ result = ficlExecC(pVM, buffer, size);
+ }
+
+#if 0
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ CELL id = pVM->sourceID;
+ char cp[nLINEBUF];
+ int nLine = 0;
+ int keepGoing;
+ int result;
+ pVM->sourceID.p = (void *)ff;
+
+ /* feed each line to ficlExec */
+ keepGoing = TRUE;
+ while (keepGoing && fgets(cp, nLINEBUF, ff->f))
+ {
+ int len = strlen(cp) - 1;
+
+ nLine++;
+ if (len <= 0)
+ continue;
+
+ if (cp[len] == '\n')
+ cp[len] = '\0';
+
+ result = ficlExec(pVM, cp);
+
+ switch (result)
+ {
+ case VM_OUTOFTEXT:
+ case VM_USEREXIT:
+ break;
+
+ default:
+ pVM->sourceID = id;
+ keepGoing = FALSE;
+ break;
+ }
+ }
+#endif /* 0 */
+ /*
+ ** Pass an empty line with SOURCE-ID == -1 to flush
+ ** any pending REFILLs (as required by FILE wordset)
+ */
+ pVM->sourceID.i = -1;
+ ficlExec(pVM, "");
+
+ pVM->sourceID = id;
+ closeFiclFILE(ff);
+}
+
+
+
+static void ficlReadFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+ int result;
+
+ clearerr(ff->f);
+ result = fread(address, 1, length, ff->f);
+
+ stackPushINT(pVM->pStack, result);
+ pushIor(pVM, ferror(ff->f) == 0);
+}
+
+
+
+static void ficlReadLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 flag ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ char *address = (char *)stackPopPtr(pVM->pStack);
+ int error;
+ int flag;
+
+ if (feof(ff->f))
+ {
+ stackPushINT(pVM->pStack, -1);
+ stackPushINT(pVM->pStack, 0);
+ stackPushINT(pVM->pStack, 0);
+ return;
+ }
+
+ clearerr(ff->f);
+ *address = 0;
+ fgets(address, length, ff->f);
+
+ error = ferror(ff->f);
+ if (error != 0)
+ {
+ stackPushINT(pVM->pStack, -1);
+ stackPushINT(pVM->pStack, 0);
+ stackPushINT(pVM->pStack, error);
+ return;
+ }
+
+ length = strlen(address);
+ flag = (length > 0);
+ if (length && ((address[length - 1] == '\r') || (address[length - 1] == '\n')))
+ length--;
+
+ stackPushINT(pVM->pStack, length);
+ stackPushINT(pVM->pStack, flag);
+ stackPushINT(pVM->pStack, 0); /* ior */
+}
+
+
+
+static void ficlWriteFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ clearerr(ff->f);
+ fwrite(address, 1, length, ff->f);
+ pushIor(pVM, ferror(ff->f) == 0);
+}
+
+
+
+static void ficlWriteLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ size_t length = (size_t)stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ clearerr(ff->f);
+ if (fwrite(address, 1, length, ff->f) == length)
+ fwrite("\n", 1, 1, ff->f);
+ pushIor(pVM, ferror(ff->f) == 0);
+}
+
+
+
+static void ficlRepositionFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ size_t ud = (size_t)stackPopINT(pVM->pStack);
+
+ pushIor(pVM, fseek(ff->f, ud, SEEK_SET) == 0);
+}
+
+
+
+static void ficlFlushFile(FICL_VM *pVM) /* ( fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ pushIor(pVM, fflush(ff->f) == 0);
+}
+
+
+
+#if FICL_HAVE_FTRUNCATE
+
+static void ficlResizeFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
+{
+ ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
+ size_t ud = (size_t)stackPopINT(pVM->pStack);
+
+ pushIor(pVM, ftruncate(fileno(ff->f), ud) == 0);
+}
+
+#endif /* FICL_HAVE_FTRUNCATE */
+
+#endif /* FICL_WANT_FILE */
+
+
+
+void ficlCompileFile(FICL_SYSTEM *pSys)
+{
+#if FICL_WANT_FILE
+ FICL_DICT *dp = pSys->dp;
+ assert(dp);
+
+ dictAppendWord(dp, "create-file", ficlCreateFile, FW_DEFAULT);
+ dictAppendWord(dp, "open-file", ficlOpenFile, FW_DEFAULT);
+ dictAppendWord(dp, "close-file", ficlCloseFile, FW_DEFAULT);
+ dictAppendWord(dp, "include-file", ficlIncludeFile, FW_DEFAULT);
+ dictAppendWord(dp, "read-file", ficlReadFile, FW_DEFAULT);
+ dictAppendWord(dp, "read-line", ficlReadLine, FW_DEFAULT);
+ dictAppendWord(dp, "write-file", ficlWriteFile, FW_DEFAULT);
+ dictAppendWord(dp, "write-line", ficlWriteLine, FW_DEFAULT);
+ dictAppendWord(dp, "file-position", ficlFilePosition, FW_DEFAULT);
+ dictAppendWord(dp, "file-size", ficlFileSize, FW_DEFAULT);
+ dictAppendWord(dp, "reposition-file", ficlRepositionFile, FW_DEFAULT);
+ dictAppendWord(dp, "file-status", ficlFileStatus, FW_DEFAULT);
+ dictAppendWord(dp, "flush-file", ficlFlushFile, FW_DEFAULT);
+
+ dictAppendWord(dp, "delete-file", ficlDeleteFile, FW_DEFAULT);
+ dictAppendWord(dp, "rename-file", ficlRenameFile, FW_DEFAULT);
+
+#ifdef FICL_HAVE_FTRUNCATE
+ dictAppendWord(dp, "resize-file", ficlResizeFile, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "file", FICL_TRUE);
+ ficlSetEnv(pSys, "file-ext", FICL_TRUE);
+#endif /* FICL_HAVE_FTRUNCATE */
+#else
+ &pSys;
+#endif /* FICL_WANT_FILE */
+}
diff --git a/float.c b/float.c
new file mode 100644
index 000000000000..106eb8c68964
--- /dev/null
+++ b/float.c
@@ -0,0 +1,1065 @@
+/*******************************************************************
+** f l o a t . c
+** Forth Inspired Command Language
+** ANS Forth FLOAT word-set written in C
+** Author: Guy Carver & John Sadler (john_sadler@alum.mit.edu)
+** Created: Apr 2001
+** $Id: float.c,v 1.8 2001-12-04 17:58:16-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include "ficl.h"
+
+#if FICL_WANT_FLOAT
+
+/*******************************************************************
+** Do float addition r1 + r2.
+** f+ ( r1 r2 -- r )
+*******************************************************************/
+static void Fadd(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f += GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float subtraction r1 - r2.
+** f- ( r1 r2 -- r )
+*******************************************************************/
+static void Fsub(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f = GETTOPF().f - f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float multiplication r1 * r2.
+** f* ( r1 r2 -- r )
+*******************************************************************/
+static void Fmul(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f *= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float negation.
+** fnegate ( r -- r )
+*******************************************************************/
+static void Fnegate(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+#endif
+
+ f = -GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float division r1 / r2.
+** f/ ( r1 r2 -- r )
+*******************************************************************/
+static void Fdiv(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 1);
+#endif
+
+ f = POPFLOAT();
+ f = GETTOPF().f / f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float + integer r + n.
+** f+i ( r n -- r )
+*******************************************************************/
+static void Faddi(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f += GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float - integer r - n.
+** f-i ( r n -- r )
+*******************************************************************/
+static void Fsubi(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = GETTOPF().f;
+ f -= (FICL_FLOAT)POPINT();
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float * integer r * n.
+** f*i ( r n -- r )
+*******************************************************************/
+static void Fmuli(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f *= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do float / integer r / n.
+** f/i ( r n -- r )
+*******************************************************************/
+static void Fdivi(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = GETTOPF().f;
+ f /= (FICL_FLOAT)POPINT();
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do integer - float n - r.
+** i-f ( n r -- r )
+*******************************************************************/
+static void isubf(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f -= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do integer / float n / r.
+** i/f ( n r -- r )
+*******************************************************************/
+static void idivf(FICL_VM *pVM)
+{
+ FICL_FLOAT f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1,1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ f = (FICL_FLOAT)POPINT();
+ f /= GETTOPF().f;
+ SETTOPF(f);
+}
+
+/*******************************************************************
+** Do integer to float conversion.
+** int>float ( n -- r )
+*******************************************************************/
+static void itof(FICL_VM *pVM)
+{
+ float f;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+ vmCheckFStack(pVM, 0, 1);
+#endif
+
+ f = (float)POPINT();
+ PUSHFLOAT(f);
+}
+
+/*******************************************************************
+** Do float to integer conversion.
+** float>int ( r -- n )
+*******************************************************************/
+static void Ftoi(FICL_VM *pVM)
+{
+ FICL_INT i;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ i = (FICL_INT)POPFLOAT();
+ PUSHINT(i);
+}
+
+/*******************************************************************
+** Floating point constant execution word.
+*******************************************************************/
+void FconstantParen(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = pVM->runningWord;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+#endif
+
+ PUSHFLOAT(pFW->param[0].f);
+}
+
+/*******************************************************************
+** Create a floating point constant.
+** fconstant ( r -"name"- )
+*******************************************************************/
+static void Fconstant(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ dictAppendWord2(dp, si, FconstantParen, FW_DEFAULT);
+ dictAppendCell(dp, stackPop(pVM->fStack));
+}
+
+/*******************************************************************
+** Display a float in decimal format.
+** f. ( r -- )
+*******************************************************************/
+static void FDot(FICL_VM *pVM)
+{
+ float f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ f = POPFLOAT();
+ sprintf(pVM->pad,"%#f ",f);
+ vmTextOut(pVM, pVM->pad, 0);
+}
+
+/*******************************************************************
+** Display a float in engineering format.
+** fe. ( r -- )
+*******************************************************************/
+static void EDot(FICL_VM *pVM)
+{
+ float f;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ f = POPFLOAT();
+ sprintf(pVM->pad,"%#e ",f);
+ vmTextOut(pVM, pVM->pad, 0);
+}
+
+/**************************************************************************
+ d i s p l a y FS t a c k
+** Display the parameter stack (code for "f.s")
+** f.s ( -- )
+**************************************************************************/
+static void displayFStack(FICL_VM *pVM)
+{
+ int d = stackDepth(pVM->fStack);
+ int i;
+ CELL *pCell;
+
+ vmCheckFStack(pVM, 0, 0);
+
+ vmTextOut(pVM, "F:", 0);
+
+ if (d == 0)
+ vmTextOut(pVM, "[0]", 0);
+ else
+ {
+ ltoa(d, &pVM->pad[1], pVM->base);
+ pVM->pad[0] = '[';
+ strcat(pVM->pad,"] ");
+ vmTextOut(pVM,pVM->pad,0);
+
+ pCell = pVM->fStack->sp - d;
+ for (i = 0; i < d; i++)
+ {
+ sprintf(pVM->pad,"%#f ",(*pCell++).f);
+ vmTextOut(pVM,pVM->pad,0);
+ }
+ }
+}
+
+/*******************************************************************
+** Do float stack depth.
+** fdepth ( -- n )
+*******************************************************************/
+static void Fdepth(FICL_VM *pVM)
+{
+ int i;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ i = stackDepth(pVM->fStack);
+ PUSHINT(i);
+}
+
+/*******************************************************************
+** Do float stack drop.
+** fdrop ( r -- )
+*******************************************************************/
+static void Fdrop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ DROPF(1);
+}
+
+/*******************************************************************
+** Do float stack 2drop.
+** f2drop ( r r -- )
+*******************************************************************/
+static void FtwoDrop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+#endif
+
+ DROPF(2);
+}
+
+/*******************************************************************
+** Do float stack dup.
+** fdup ( r -- r r )
+*******************************************************************/
+static void Fdup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 2);
+#endif
+
+ PICKF(0);
+}
+
+/*******************************************************************
+** Do float stack 2dup.
+** f2dup ( r1 r2 -- r1 r2 r1 r2 )
+*******************************************************************/
+static void FtwoDup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 4);
+#endif
+
+ PICKF(1);
+ PICKF(1);
+}
+
+/*******************************************************************
+** Do float stack over.
+** fover ( r1 r2 -- r1 r2 r1 )
+*******************************************************************/
+static void Fover(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 3);
+#endif
+
+ PICKF(1);
+}
+
+/*******************************************************************
+** Do float stack 2over.
+** f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
+*******************************************************************/
+static void FtwoOver(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 4, 6);
+#endif
+
+ PICKF(3);
+ PICKF(3);
+}
+
+/*******************************************************************
+** Do float stack pick.
+** fpick ( n -- r )
+*******************************************************************/
+static void Fpick(FICL_VM *pVM)
+{
+ CELL c = POP();
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, c.i+1, c.i+2);
+#endif
+
+ PICKF(c.i);
+}
+
+/*******************************************************************
+** Do float stack ?dup.
+** f?dup ( r -- r )
+*******************************************************************/
+static void FquestionDup(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 2);
+#endif
+
+ c = GETTOPF();
+ if (c.f != 0)
+ PICKF(0);
+}
+
+/*******************************************************************
+** Do float stack roll.
+** froll ( n -- )
+*******************************************************************/
+static void Froll(FICL_VM *pVM)
+{
+ int i = POP().i;
+ i = (i > 0) ? i : 0;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, i+1, i+1);
+#endif
+
+ ROLLF(i);
+}
+
+/*******************************************************************
+** Do float stack -roll.
+** f-roll ( n -- )
+*******************************************************************/
+static void FminusRoll(FICL_VM *pVM)
+{
+ int i = POP().i;
+ i = (i > 0) ? i : 0;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, i+1, i+1);
+#endif
+
+ ROLLF(-i);
+}
+
+/*******************************************************************
+** Do float stack rot.
+** frot ( r1 r2 r3 -- r2 r3 r1 )
+*******************************************************************/
+static void Frot(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 3, 3);
+#endif
+
+ ROLLF(2);
+}
+
+/*******************************************************************
+** Do float stack -rot.
+** f-rot ( r1 r2 r3 -- r3 r1 r2 )
+*******************************************************************/
+static void Fminusrot(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 3, 3);
+#endif
+
+ ROLLF(-2);
+}
+
+/*******************************************************************
+** Do float stack swap.
+** fswap ( r1 r2 -- r2 r1 )
+*******************************************************************/
+static void Fswap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 2);
+#endif
+
+ ROLLF(1);
+}
+
+/*******************************************************************
+** Do float stack 2swap
+** f2swap ( r1 r2 r3 r4 -- r3 r4 r1 r2 )
+*******************************************************************/
+static void FtwoSwap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 4, 4);
+#endif
+
+ ROLLF(3);
+ ROLLF(3);
+}
+
+/*******************************************************************
+** Get a floating point number from a variable.
+** f@ ( n -- r )
+*******************************************************************/
+static void Ffetch(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ pCell = (CELL *)POPPTR();
+ PUSHFLOAT(pCell->f);
+}
+
+/*******************************************************************
+** Store a floating point number into a variable.
+** f! ( r n -- )
+*******************************************************************/
+static void Fstore(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ pCell = (CELL *)POPPTR();
+ pCell->f = POPFLOAT();
+}
+
+/*******************************************************************
+** Add a floating point number to contents of a variable.
+** f+! ( r n -- )
+*******************************************************************/
+static void FplusStore(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ pCell = (CELL *)POPPTR();
+ pCell->f += POPFLOAT();
+}
+
+/*******************************************************************
+** Floating point literal execution word.
+*******************************************************************/
+static void fliteralParen(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ PUSHFLOAT(*(float*)(pVM->ip));
+ vmBranchRelative(pVM, 1);
+}
+
+/*******************************************************************
+** Compile a floating point literal.
+*******************************************************************/
+static void fliteralIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pfLitParen = ficlLookup(pVM->pSys, "(fliteral)");
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+#endif
+
+ dictAppendCell(dp, LVALUEtoCELL(pfLitParen));
+ dictAppendCell(dp, stackPop(pVM->fStack));
+}
+
+/*******************************************************************
+** Do float 0= comparison r = 0.0.
+** f0= ( r -- T/F )
+*******************************************************************/
+static void FzeroEquals(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0); /* Make sure something on float stack. */
+ vmCheckStack(pVM, 0, 1); /* Make sure room for result. */
+#endif
+
+ c.i = FICL_BOOL(POPFLOAT() == 0);
+ PUSH(c);
+}
+
+/*******************************************************************
+** Do float 0< comparison r < 0.0.
+** f0< ( r -- T/F )
+*******************************************************************/
+static void FzeroLess(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0); /* Make sure something on float stack. */
+ vmCheckStack(pVM, 0, 1); /* Make sure room for result. */
+#endif
+
+ c.i = FICL_BOOL(POPFLOAT() < 0);
+ PUSH(c);
+}
+
+/*******************************************************************
+** Do float 0> comparison r > 0.0.
+** f0> ( r -- T/F )
+*******************************************************************/
+static void FzeroGreater(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ c.i = FICL_BOOL(POPFLOAT() > 0);
+ PUSH(c);
+}
+
+/*******************************************************************
+** Do float = comparison r1 = r2.
+** f= ( r1 r2 -- T/F )
+*******************************************************************/
+static void FisEqual(FICL_VM *pVM)
+{
+ float x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ x = POPFLOAT();
+ y = POPFLOAT();
+ PUSHINT(FICL_BOOL(x == y));
+}
+
+/*******************************************************************
+** Do float < comparison r1 < r2.
+** f< ( r1 r2 -- T/F )
+*******************************************************************/
+static void FisLess(FICL_VM *pVM)
+{
+ float x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ y = POPFLOAT();
+ x = POPFLOAT();
+ PUSHINT(FICL_BOOL(x < y));
+}
+
+/*******************************************************************
+** Do float > comparison r1 > r2.
+** f> ( r1 r2 -- T/F )
+*******************************************************************/
+static void FisGreater(FICL_VM *pVM)
+{
+ float x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 2, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ y = POPFLOAT();
+ x = POPFLOAT();
+ PUSHINT(FICL_BOOL(x > y));
+}
+
+
+/*******************************************************************
+** Move float to param stack (assumes they both fit in a single CELL)
+** f>s
+*******************************************************************/
+static void FFrom(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 1, 0);
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ c = stackPop(pVM->fStack);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void ToF(FICL_VM *pVM)
+{
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ c = stackPop(pVM->pStack);
+ stackPush(pVM->fStack, c);
+ return;
+}
+
+
+/**************************************************************************
+ F l o a t P a r s e S t a t e
+** Enum to determine the current segement of a floating point number
+** being parsed.
+**************************************************************************/
+#define NUMISNEG 1
+#define EXPISNEG 2
+
+typedef enum _floatParseState
+{
+ FPS_START,
+ FPS_ININT,
+ FPS_INMANT,
+ FPS_STARTEXP,
+ FPS_INEXP
+} FloatParseState;
+
+/**************************************************************************
+ f i c l P a r s e F l o a t N u m b e r
+** pVM -- Virtual Machine pointer.
+** si -- String to parse.
+** Returns 1 if successful, 0 if not.
+**************************************************************************/
+int ficlParseFloatNumber( FICL_VM *pVM, STRINGINFO si )
+{
+ unsigned char ch, digit;
+ char *cp;
+ FICL_COUNT count;
+ float power;
+ float accum = 0.0f;
+ float mant = 0.1f;
+ FICL_INT exponent = 0;
+ char flag = 0;
+ FloatParseState estate = FPS_START;
+
+#if FICL_ROBUST > 1
+ vmCheckFStack(pVM, 0, 1);
+#endif
+
+ /*
+ ** floating point numbers only allowed in base 10
+ */
+ if (pVM->base != 10)
+ return(0);
+
+
+ cp = SI_PTR(si);
+ count = (FICL_COUNT)SI_COUNT(si);
+
+ /* Loop through the string's characters. */
+ while ((count--) && ((ch = *cp++) != 0))
+ {
+ switch (estate)
+ {
+ /* At start of the number so look for a sign. */
+ case FPS_START:
+ {
+ estate = FPS_ININT;
+ if (ch == '-')
+ {
+ flag |= NUMISNEG;
+ break;
+ }
+ if (ch == '+')
+ {
+ break;
+ }
+ } /* Note! Drop through to FPS_ININT */
+ /*
+ **Converting integer part of number.
+ ** Only allow digits, decimal and 'E'.
+ */
+ case FPS_ININT:
+ {
+ if (ch == '.')
+ {
+ estate = FPS_INMANT;
+ }
+ else if ((ch == 'e') || (ch == 'E'))
+ {
+ estate = FPS_STARTEXP;
+ }
+ else
+ {
+ digit = (unsigned char)(ch - '0');
+ if (digit > 9)
+ return(0);
+
+ accum = accum * 10 + digit;
+
+ }
+ break;
+ }
+ /*
+ ** Processing the fraction part of number.
+ ** Only allow digits and 'E'
+ */
+ case FPS_INMANT:
+ {
+ if ((ch == 'e') || (ch == 'E'))
+ {
+ estate = FPS_STARTEXP;
+ }
+ else
+ {
+ digit = (unsigned char)(ch - '0');
+ if (digit > 9)
+ return(0);
+
+ accum += digit * mant;
+ mant *= 0.1f;
+ }
+ break;
+ }
+ /* Start processing the exponent part of number. */
+ /* Look for sign. */
+ case FPS_STARTEXP:
+ {
+ estate = FPS_INEXP;
+
+ if (ch == '-')
+ {
+ flag |= EXPISNEG;
+ break;
+ }
+ else if (ch == '+')
+ {
+ break;
+ }
+ } /* Note! Drop through to FPS_INEXP */
+ /*
+ ** Processing the exponent part of number.
+ ** Only allow digits.
+ */
+ case FPS_INEXP:
+ {
+ digit = (unsigned char)(ch - '0');
+ if (digit > 9)
+ return(0);
+
+ exponent = exponent * 10 + digit;
+
+ break;
+ }
+ }
+ }
+
+ /* If parser never made it to the exponent this is not a float. */
+ if (estate < FPS_STARTEXP)
+ return(0);
+
+ /* Set the sign of the number. */
+ if (flag & NUMISNEG)
+ accum = -accum;
+
+ /* If exponent is not 0 then adjust number by it. */
+ if (exponent != 0)
+ {
+ /* Determine if exponent is negative. */
+ if (flag & EXPISNEG)
+ {
+ exponent = -exponent;
+ }
+ /* power = 10^x */
+ power = (float)pow(10.0, exponent);
+ accum *= power;
+ }
+
+ PUSHFLOAT(accum);
+ if (pVM->state == COMPILE)
+ fliteralIm(pVM);
+
+ return(1);
+}
+
+#endif /* FICL_WANT_FLOAT */
+
+/**************************************************************************
+** Add float words to a system's dictionary.
+** pSys -- Pointer to the FICL sytem to add float words to.
+**************************************************************************/
+void ficlCompileFloat(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert(dp);
+
+#if FICL_WANT_FLOAT
+ dictAppendWord(dp, ">float", ToF, FW_DEFAULT);
+ /* d>f */
+ dictAppendWord(dp, "f!", Fstore, FW_DEFAULT);
+ dictAppendWord(dp, "f*", Fmul, FW_DEFAULT);
+ dictAppendWord(dp, "f+", Fadd, FW_DEFAULT);
+ dictAppendWord(dp, "f-", Fsub, FW_DEFAULT);
+ dictAppendWord(dp, "f/", Fdiv, FW_DEFAULT);
+ dictAppendWord(dp, "f0<", FzeroLess, FW_DEFAULT);
+ dictAppendWord(dp, "f0=", FzeroEquals, FW_DEFAULT);
+ dictAppendWord(dp, "f<", FisLess, FW_DEFAULT);
+ /*
+ f>d
+ */
+ dictAppendWord(dp, "f@", Ffetch, FW_DEFAULT);
+ /*
+ falign
+ faligned
+ */
+ dictAppendWord(dp, "fconstant", Fconstant, FW_DEFAULT);
+ dictAppendWord(dp, "fdepth", Fdepth, FW_DEFAULT);
+ dictAppendWord(dp, "fdrop", Fdrop, FW_DEFAULT);
+ dictAppendWord(dp, "fdup", Fdup, FW_DEFAULT);
+ dictAppendWord(dp, "fliteral", fliteralIm, FW_IMMEDIATE);
+/*
+ float+
+ floats
+ floor
+ fmax
+ fmin
+*/
+ dictAppendWord(dp, "f?dup", FquestionDup, FW_DEFAULT);
+ dictAppendWord(dp, "f=", FisEqual, FW_DEFAULT);
+ dictAppendWord(dp, "f>", FisGreater, FW_DEFAULT);
+ dictAppendWord(dp, "f0>", FzeroGreater, FW_DEFAULT);
+ dictAppendWord(dp, "f2drop", FtwoDrop, FW_DEFAULT);
+ dictAppendWord(dp, "f2dup", FtwoDup, FW_DEFAULT);
+ dictAppendWord(dp, "f2over", FtwoOver, FW_DEFAULT);
+ dictAppendWord(dp, "f2swap", FtwoSwap, FW_DEFAULT);
+ dictAppendWord(dp, "f+!", FplusStore, FW_DEFAULT);
+ dictAppendWord(dp, "f+i", Faddi, FW_DEFAULT);
+ dictAppendWord(dp, "f-i", Fsubi, FW_DEFAULT);
+ dictAppendWord(dp, "f*i", Fmuli, FW_DEFAULT);
+ dictAppendWord(dp, "f/i", Fdivi, FW_DEFAULT);
+ dictAppendWord(dp, "int>float", itof, FW_DEFAULT);
+ dictAppendWord(dp, "float>int", Ftoi, FW_DEFAULT);
+ dictAppendWord(dp, "f.", FDot, FW_DEFAULT);
+ dictAppendWord(dp, "f.s", displayFStack, FW_DEFAULT);
+ dictAppendWord(dp, "fe.", EDot, FW_DEFAULT);
+ dictAppendWord(dp, "fover", Fover, FW_DEFAULT);
+ dictAppendWord(dp, "fnegate", Fnegate, FW_DEFAULT);
+ dictAppendWord(dp, "fpick", Fpick, FW_DEFAULT);
+ dictAppendWord(dp, "froll", Froll, FW_DEFAULT);
+ dictAppendWord(dp, "frot", Frot, FW_DEFAULT);
+ dictAppendWord(dp, "fswap", Fswap, FW_DEFAULT);
+ dictAppendWord(dp, "i-f", isubf, FW_DEFAULT);
+ dictAppendWord(dp, "i/f", idivf, FW_DEFAULT);
+
+ dictAppendWord(dp, "float>", FFrom, FW_DEFAULT);
+
+ dictAppendWord(dp, "f-roll", FminusRoll, FW_DEFAULT);
+ dictAppendWord(dp, "f-rot", Fminusrot, FW_DEFAULT);
+ dictAppendWord(dp, "(fliteral)", fliteralParen, FW_COMPILE);
+
+ ficlSetEnv(pSys, "floating", FICL_FALSE); /* not all required words are present */
+ ficlSetEnv(pSys, "floating-ext", FICL_FALSE);
+ ficlSetEnv(pSys, "floating-stack", FICL_DEFAULT_STACK);
+#endif
+ return;
+}
+
diff --git a/math64.c b/math64.c
new file mode 100644
index 000000000000..016541d998cd
--- /dev/null
+++ b/math64.c
@@ -0,0 +1,559 @@
+/*******************************************************************
+** m a t h 6 4 . c
+** Forth Inspired Command Language - 64 bit math support routines
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 25 January 1998
+** Rev 2.03: Support for 128 bit DP math. This file really ouught to
+** be renamed!
+** $Id: math64.c,v 1.6 2001-05-16 07:56:16-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include "ficl.h"
+#include "math64.h"
+
+
+/**************************************************************************
+ m 6 4 A b s
+** Returns the absolute value of an DPINT
+**************************************************************************/
+DPINT m64Abs(DPINT x)
+{
+ if (m64IsNegative(x))
+ x = m64Negate(x);
+
+ return x;
+}
+
+
+/**************************************************************************
+ m 6 4 F l o o r e d D i v I
+**
+** FROM THE FORTH ANS...
+** Floored division is integer division in which the remainder carries
+** the sign of the divisor or is zero, and the quotient is rounded to
+** its arithmetic floor. Symmetric division is integer division in which
+** the remainder carries the sign of the dividend or is zero and the
+** quotient is the mathematical quotient rounded towards zero or
+** truncated. Examples of each are shown in tables 3.3 and 3.4.
+**
+** Table 3.3 - Floored Division Example
+** Dividend Divisor Remainder Quotient
+** -------- ------- --------- --------
+** 10 7 3 1
+** -10 7 4 -2
+** 10 -7 -4 -2
+** -10 -7 -3 1
+**
+**
+** Table 3.4 - Symmetric Division Example
+** Dividend Divisor Remainder Quotient
+** -------- ------- --------- --------
+** 10 7 3 1
+** -10 7 -3 -1
+** 10 -7 3 -1
+** -10 -7 -3 1
+**************************************************************************/
+INTQR m64FlooredDivI(DPINT num, FICL_INT den)
+{
+ INTQR qr;
+ UNSQR uqr;
+ int signRem = 1;
+ int signQuot = 1;
+
+ if (m64IsNegative(num))
+ {
+ num = m64Negate(num);
+ signQuot = -signQuot;
+ }
+
+ if (den < 0)
+ {
+ den = -den;
+ signRem = -signRem;
+ signQuot = -signQuot;
+ }
+
+ uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
+ qr = m64CastQRUI(uqr);
+ if (signQuot < 0)
+ {
+ qr.quot = -qr.quot;
+ if (qr.rem != 0)
+ {
+ qr.quot--;
+ qr.rem = den - qr.rem;
+ }
+ }
+
+ if (signRem < 0)
+ qr.rem = -qr.rem;
+
+ return qr;
+}
+
+
+/**************************************************************************
+ m 6 4 I s N e g a t i v e
+** Returns TRUE if the specified DPINT has its sign bit set.
+**************************************************************************/
+int m64IsNegative(DPINT x)
+{
+ return (x.hi < 0);
+}
+
+
+/**************************************************************************
+ m 6 4 M a c
+** Mixed precision multiply and accumulate primitive for number building.
+** Multiplies DPUNS u by FICL_UNS mul and adds FICL_UNS add. Mul is typically
+** the numeric base, and add represents a digit to be appended to the
+** growing number.
+** Returns the result of the operation
+**************************************************************************/
+DPUNS m64Mac(DPUNS u, FICL_UNS mul, FICL_UNS add)
+{
+ DPUNS resultLo = ficlLongMul(u.lo, mul);
+ DPUNS resultHi = ficlLongMul(u.hi, mul);
+ resultLo.hi += resultHi.lo;
+ resultHi.lo = resultLo.lo + add;
+
+ if (resultHi.lo < resultLo.lo)
+ resultLo.hi++;
+
+ resultLo.lo = resultHi.lo;
+
+ return resultLo;
+}
+
+
+/**************************************************************************
+ m 6 4 M u l I
+** Multiplies a pair of FICL_INTs and returns an DPINT result.
+**************************************************************************/
+DPINT m64MulI(FICL_INT x, FICL_INT y)
+{
+ DPUNS prod;
+ int sign = 1;
+
+ if (x < 0)
+ {
+ sign = -sign;
+ x = -x;
+ }
+
+ if (y < 0)
+ {
+ sign = -sign;
+ y = -y;
+ }
+
+ prod = ficlLongMul(x, y);
+ if (sign > 0)
+ return m64CastUI(prod);
+ else
+ return m64Negate(m64CastUI(prod));
+}
+
+
+/**************************************************************************
+ m 6 4 N e g a t e
+** Negates an DPINT by complementing and incrementing.
+**************************************************************************/
+DPINT m64Negate(DPINT x)
+{
+ x.hi = ~x.hi;
+ x.lo = ~x.lo;
+ x.lo ++;
+ if (x.lo == 0)
+ x.hi++;
+
+ return x;
+}
+
+
+/**************************************************************************
+ m 6 4 P u s h
+** Push an DPINT onto the specified stack in the order required
+** by ANS Forth (most significant cell on top)
+** These should probably be macros...
+**************************************************************************/
+void i64Push(FICL_STACK *pStack, DPINT i64)
+{
+ stackPushINT(pStack, i64.lo);
+ stackPushINT(pStack, i64.hi);
+ return;
+}
+
+void u64Push(FICL_STACK *pStack, DPUNS u64)
+{
+ stackPushINT(pStack, u64.lo);
+ stackPushINT(pStack, u64.hi);
+ return;
+}
+
+
+/**************************************************************************
+ m 6 4 P o p
+** Pops an DPINT off the stack in the order required by ANS Forth
+** (most significant cell on top)
+** These should probably be macros...
+**************************************************************************/
+DPINT i64Pop(FICL_STACK *pStack)
+{
+ DPINT ret;
+ ret.hi = stackPopINT(pStack);
+ ret.lo = stackPopINT(pStack);
+ return ret;
+}
+
+DPUNS u64Pop(FICL_STACK *pStack)
+{
+ DPUNS ret;
+ ret.hi = stackPopINT(pStack);
+ ret.lo = stackPopINT(pStack);
+ return ret;
+}
+
+
+/**************************************************************************
+ m 6 4 S y m m e t r i c D i v
+** Divide an DPINT by a FICL_INT and return a FICL_INT quotient and a
+** FICL_INT remainder. The absolute values of quotient and remainder are not
+** affected by the signs of the numerator and denominator (the operation
+** is symmetric on the number line)
+**************************************************************************/
+INTQR m64SymmetricDivI(DPINT num, FICL_INT den)
+{
+ INTQR qr;
+ UNSQR uqr;
+ int signRem = 1;
+ int signQuot = 1;
+
+ if (m64IsNegative(num))
+ {
+ num = m64Negate(num);
+ signRem = -signRem;
+ signQuot = -signQuot;
+ }
+
+ if (den < 0)
+ {
+ den = -den;
+ signQuot = -signQuot;
+ }
+
+ uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);
+ qr = m64CastQRUI(uqr);
+ if (signRem < 0)
+ qr.rem = -qr.rem;
+
+ if (signQuot < 0)
+ qr.quot = -qr.quot;
+
+ return qr;
+}
+
+
+/**************************************************************************
+ m 6 4 U M o d
+** Divides a DPUNS by base (an UNS16) and returns an UNS16 remainder.
+** Writes the quotient back to the original DPUNS as a side effect.
+** This operation is typically used to convert an DPUNS to a text string
+** in any base. See words.c:numberSignS, for example.
+** Mechanics: performs 4 ficlLongDivs, each of which produces 16 bits
+** of the quotient. C does not provide a way to divide an FICL_UNS by an
+** UNS16 and get an FICL_UNS quotient (ldiv is closest, but it's signed,
+** unfortunately), so I've used ficlLongDiv.
+**************************************************************************/
+#if (BITS_PER_CELL == 32)
+
+#define UMOD_SHIFT 16
+#define UMOD_MASK 0x0000ffff
+
+#elif (BITS_PER_CELL == 64)
+
+#define UMOD_SHIFT 32
+#define UMOD_MASK 0x00000000ffffffff
+
+#endif
+
+UNS16 m64UMod(DPUNS *pUD, UNS16 base)
+{
+ DPUNS ud;
+ UNSQR qr;
+ DPUNS result;
+
+ result.hi = result.lo = 0;
+
+ ud.hi = 0;
+ ud.lo = pUD->hi >> UMOD_SHIFT;
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.hi = qr.quot << UMOD_SHIFT;
+
+ ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->hi & UMOD_MASK);
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.hi |= qr.quot & UMOD_MASK;
+
+ ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo >> UMOD_SHIFT);
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.lo = qr.quot << UMOD_SHIFT;
+
+ ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo & UMOD_MASK);
+ qr = ficlLongDiv(ud, (FICL_UNS)base);
+ result.lo |= qr.quot & UMOD_MASK;
+
+ *pUD = result;
+
+ return (UNS16)(qr.rem);
+}
+
+
+/**************************************************************************
+** Contributed by
+** Michael A. Gauland gaulandm@mdhost.cse.tek.com
+**************************************************************************/
+#if PORTABLE_LONGMULDIV != 0
+/**************************************************************************
+ m 6 4 A d d
+**
+**************************************************************************/
+DPUNS m64Add(DPUNS x, DPUNS y)
+{
+ DPUNS result;
+ int carry;
+
+ result.hi = x.hi + y.hi;
+ result.lo = x.lo + y.lo;
+
+
+ carry = ((x.lo | y.lo) & CELL_HI_BIT) && !(result.lo & CELL_HI_BIT);
+ carry |= ((x.lo & y.lo) & CELL_HI_BIT);
+
+ if (carry)
+ {
+ result.hi++;
+ }
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 S u b
+**
+**************************************************************************/
+DPUNS m64Sub(DPUNS x, DPUNS y)
+{
+ DPUNS result;
+
+ result.hi = x.hi - y.hi;
+ result.lo = x.lo - y.lo;
+
+ if (x.lo < y.lo)
+ {
+ result.hi--;
+ }
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 A S L
+** 64 bit left shift
+**************************************************************************/
+DPUNS m64ASL( DPUNS x )
+{
+ DPUNS result;
+
+ result.hi = x.hi << 1;
+ if (x.lo & CELL_HI_BIT)
+ {
+ result.hi++;
+ }
+
+ result.lo = x.lo << 1;
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 A S R
+** 64 bit right shift (unsigned - no sign extend)
+**************************************************************************/
+DPUNS m64ASR( DPUNS x )
+{
+ DPUNS result;
+
+ result.lo = x.lo >> 1;
+ if (x.hi & 1)
+ {
+ result.lo |= CELL_HI_BIT;
+ }
+
+ result.hi = x.hi >> 1;
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 O r
+** 64 bit bitwise OR
+**************************************************************************/
+DPUNS m64Or( DPUNS x, DPUNS y )
+{
+ DPUNS result;
+
+ result.hi = x.hi | y.hi;
+ result.lo = x.lo | y.lo;
+
+ return result;
+}
+
+
+/**************************************************************************
+ m 6 4 C o m p a r e
+** Return -1 if x < y; 0 if x==y, and 1 if x > y.
+**************************************************************************/
+int m64Compare(DPUNS x, DPUNS y)
+{
+ int result;
+
+ if (x.hi > y.hi)
+ {
+ result = +1;
+ }
+ else if (x.hi < y.hi)
+ {
+ result = -1;
+ }
+ else
+ {
+ /* High parts are equal */
+ if (x.lo > y.lo)
+ {
+ result = +1;
+ }
+ else if (x.lo < y.lo)
+ {
+ result = -1;
+ }
+ else
+ {
+ result = 0;
+ }
+ }
+
+ return result;
+}
+
+
+/**************************************************************************
+ f i c l L o n g M u l
+** Portable versions of ficlLongMul and ficlLongDiv in C
+** Contributed by:
+** Michael A. Gauland gaulandm@mdhost.cse.tek.com
+**************************************************************************/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS result = { 0, 0 };
+ DPUNS addend;
+
+ addend.lo = y;
+ addend.hi = 0; /* No sign extension--arguments are unsigned */
+
+ while (x != 0)
+ {
+ if ( x & 1)
+ {
+ result = m64Add(result, addend);
+ }
+ x >>= 1;
+ addend = m64ASL(addend);
+ }
+ return result;
+}
+
+
+/**************************************************************************
+ f i c l L o n g D i v
+** Portable versions of ficlLongMul and ficlLongDiv in C
+** Contributed by:
+** Michael A. Gauland gaulandm@mdhost.cse.tek.com
+**************************************************************************/
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ DPUNS quotient;
+ DPUNS subtrahend;
+ DPUNS mask;
+
+ quotient.lo = 0;
+ quotient.hi = 0;
+
+ subtrahend.lo = y;
+ subtrahend.hi = 0;
+
+ mask.lo = 1;
+ mask.hi = 0;
+
+ while ((m64Compare(subtrahend, q) < 0) &&
+ (subtrahend.hi & CELL_HI_BIT) == 0)
+ {
+ mask = m64ASL(mask);
+ subtrahend = m64ASL(subtrahend);
+ }
+
+ while (mask.lo != 0 || mask.hi != 0)
+ {
+ if (m64Compare(subtrahend, q) <= 0)
+ {
+ q = m64Sub( q, subtrahend);
+ quotient = m64Or(quotient, mask);
+ }
+ mask = m64ASR(mask);
+ subtrahend = m64ASR(subtrahend);
+ }
+
+ result.quot = quotient.lo;
+ result.rem = q.lo;
+ return result;
+}
+
+#endif
+
diff --git a/math64.h b/math64.h
new file mode 100644
index 000000000000..19e874107843
--- /dev/null
+++ b/math64.h
@@ -0,0 +1,86 @@
+/*******************************************************************
+** m a t h 6 4 . h
+** Forth Inspired Command Language - 64 bit math support routines
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 25 January 1998
+** $Id: math64.h,v 1.6 2001-05-16 07:56:19-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#if !defined (__MATH64_H__)
+#define __MATH64_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DPINT m64Abs(DPINT x);
+int m64IsNegative(DPINT x);
+DPUNS m64Mac(DPUNS u, FICL_UNS mul, FICL_UNS add);
+DPINT m64MulI(FICL_INT x, FICL_INT y);
+DPINT m64Negate(DPINT x);
+INTQR m64FlooredDivI(DPINT num, FICL_INT den);
+void i64Push(FICL_STACK *pStack, DPINT i64);
+DPINT i64Pop(FICL_STACK *pStack);
+void u64Push(FICL_STACK *pStack, DPUNS u64);
+DPUNS u64Pop(FICL_STACK *pStack);
+INTQR m64SymmetricDivI(DPINT num, FICL_INT den);
+UNS16 m64UMod(DPUNS *pUD, UNS16 base);
+
+
+#if PORTABLE_LONGMULDIV != 0 /* see sysdep.h */
+DPUNS m64Add(DPUNS x, DPUNS y);
+DPUNS m64ASL( DPUNS x );
+DPUNS m64ASR( DPUNS x );
+int m64Compare(DPUNS x, DPUNS y);
+DPUNS m64Or( DPUNS x, DPUNS y );
+DPUNS m64Sub(DPUNS x, DPUNS y);
+#endif
+
+#define i64Extend(i64) (i64).hi = ((i64).lo < 0) ? -1L : 0
+#define m64CastIU(i64) (*(DPUNS *)(&(i64)))
+#define m64CastUI(u64) (*(DPINT *)(&(u64)))
+#define m64CastQRIU(iqr) (*(UNSQR *)(&(iqr)))
+#define m64CastQRUI(uqr) (*(INTQR *)(&(uqr)))
+
+#define CELL_HI_BIT (1L << (BITS_PER_CELL-1))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/prefix.c b/prefix.c
new file mode 100644
index 000000000000..ee3472a6ed46
--- /dev/null
+++ b/prefix.c
@@ -0,0 +1,197 @@
+/*******************************************************************
+** p r e f i x . c
+** Forth Inspired Command Language
+** Parser extensions for Ficl
+** Authors: Larry Hastings & John Sadler (john_sadler@alum.mit.edu)
+** Created: April 2001
+** $Id: prefix.c,v 1.5 2001-12-04 17:58:13-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <string.h>
+#include <ctype.h>
+#include "ficl.h"
+#include "math64.h"
+
+/*
+** (jws) revisions:
+** A prefix is a word in a dedicated wordlist (name stored in list_name below)
+** that is searched in a special way by the prefix parse step. When a prefix
+** matches the beginning of an incoming token, push the non-prefix part of the
+** token back onto the input stream and execute the prefix code.
+**
+** The parse step is called ficlParsePrefix.
+** Storing prefix entries in the dictionary greatly simplifies
+** the process of matching and dispatching prefixes, avoids the
+** need to clean up a dynamically allocated prefix list when the system
+** goes away, but still allows prefixes to be allocated at runtime.
+*/
+
+static char list_name[] = "<prefixes>";
+
+/**************************************************************************
+ f i c l P a r s e P r e f i x
+** This is the parse step for prefixes - it checks an incoming word
+** to see if it starts with a prefix, and if so runs the corrseponding
+** code against the remainder of the word and returns true.
+**************************************************************************/
+int ficlParsePrefix(FICL_VM *pVM, STRINGINFO si)
+{
+ int i;
+ FICL_HASH *pHash;
+ FICL_WORD *pFW = ficlLookup(pVM->pSys, list_name);
+
+ /*
+ ** Make sure we found the prefix dictionary - otherwise silently fail
+ ** If forth-wordlist is not in the search order, we won't find the prefixes.
+ */
+ if (!pFW)
+ return FICL_FALSE;
+
+ pHash = (FICL_HASH *)(pFW->param[0].p);
+ /*
+ ** Walk the list looking for a match with the beginning of the incoming token
+ */
+ for (i = 0; i < (int)pHash->size; i++)
+ {
+ pFW = pHash->table[i];
+ while (pFW != NULL)
+ {
+ int n;
+ n = pFW->nName;
+ /*
+ ** If we find a match, adjust the TIB to give back the non-prefix characters
+ ** and execute the prefix word.
+ */
+ if (!strincmp(SI_PTR(si), pFW->name, (FICL_UNS)n))
+ {
+ /* (sadler) fixed off-by-one error when the token has no trailing space in the TIB */
+ vmSetTibIndex(pVM, si.cp + n - pVM->tib.cp );
+ vmExecute(pVM, pFW);
+
+ return FICL_TRUE;
+ }
+ pFW = pFW->link;
+ }
+ }
+
+ return FICL_FALSE;
+}
+
+
+static void tempBase(FICL_VM *pVM, int base)
+{
+ int oldbase = pVM->base;
+ STRINGINFO si = vmGetWord0(pVM);
+
+ pVM->base = base;
+ if (!ficlParseNumber(pVM, si))
+ {
+ int i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not recognized", i, SI_PTR(si));
+ }
+
+ pVM->base = oldbase;
+ return;
+}
+
+static void fTempBase(FICL_VM *pVM)
+{
+ int base = stackPopINT(pVM->pStack);
+ tempBase(pVM, base);
+ return;
+}
+
+static void prefixHex(FICL_VM *pVM)
+{
+ tempBase(pVM, 16);
+}
+
+static void prefixTen(FICL_VM *pVM)
+{
+ tempBase(pVM, 10);
+}
+
+
+/**************************************************************************
+ f i c l C o m p i l e P r e f i x
+** Build prefix support into the dictionary and the parser
+** Note: since prefixes always execute, they are effectively IMMEDIATE.
+** If they need to generate code in compile state you must add
+** this code explicitly.
+**************************************************************************/
+void ficlCompilePrefix(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ FICL_HASH *pHash;
+ FICL_HASH *pPrevCompile = dp->pCompile;
+#if (FICL_EXTENDED_PREFIX)
+ FICL_WORD *pFW;
+#endif
+
+ /*
+ ** Create a named wordlist for prefixes to reside in...
+ ** Since we're doing a special kind of search, make it
+ ** a single bucket hashtable - hashing does not help here.
+ */
+ pHash = dictCreateWordlist(dp, 1);
+ pHash->name = list_name;
+ dictAppendWord(dp, list_name, constantParen, FW_DEFAULT);
+ dictAppendCell(dp, LVALUEtoCELL(pHash));
+
+ /*
+ ** Put __tempbase in the forth-wordlist
+ */
+ dictAppendWord(dp, "__tempbase", fTempBase, FW_DEFAULT);
+
+ /*
+ ** Temporarily make the prefix list the compile wordlist so that
+ ** we can create some precompiled prefixes.
+ */
+ dp->pCompile = pHash;
+ dictAppendWord(dp, "0x", prefixHex, FW_DEFAULT);
+ dictAppendWord(dp, "0d", prefixTen, FW_DEFAULT);
+#if (FICL_EXTENDED_PREFIX)
+ pFW = ficlLookup(pSys, "\\");
+ if (pFW)
+ {
+ dictAppendWord(dp, "//", pFW->code, FW_DEFAULT);
+ }
+#endif
+ dp->pCompile = pPrevCompile;
+
+ return;
+}
diff --git a/search.c b/search.c
new file mode 100644
index 000000000000..74ea37569e77
--- /dev/null
+++ b/search.c
@@ -0,0 +1,391 @@
+/*******************************************************************
+** s e a r c h . c
+** Forth Inspired Command Language
+** ANS Forth SEARCH and SEARCH-EXT word-set written in C
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 6 June 2000
+** $Id: search.c,v 1.6 2001-06-12 01:24:34-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <string.h>
+#include "ficl.h"
+#include "math64.h"
+
+/**************************************************************************
+ d e f i n i t i o n s
+** SEARCH ( -- )
+** Make the compilation word list the same as the first word list in the
+** search order. Specifies that the names of subsequent definitions will
+** be placed in the compilation word list. Subsequent changes in the search
+** order will not affect the compilation word list.
+**************************************************************************/
+static void definitions(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+
+ assert(pDict);
+ if (pDict->nLists < 1)
+ {
+ vmThrowErr(pVM, "DEFINITIONS error - empty search order");
+ }
+
+ pDict->pCompile = pDict->pSearch[pDict->nLists-1];
+ return;
+}
+
+
+/**************************************************************************
+ f o r t h - w o r d l i s t
+** SEARCH ( -- wid )
+** Return wid, the identifier of the word list that includes all standard
+** words provided by the implementation. This word list is initially the
+** compilation word list and is part of the initial search order.
+**************************************************************************/
+static void forthWordlist(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = vmGetDict(pVM)->pForthWords;
+ stackPushPtr(pVM->pStack, pHash);
+ return;
+}
+
+
+/**************************************************************************
+ g e t - c u r r e n t
+** SEARCH ( -- wid )
+** Return wid, the identifier of the compilation word list.
+**************************************************************************/
+static void getCurrent(FICL_VM *pVM)
+{
+ ficlLockDictionary(TRUE);
+ stackPushPtr(pVM->pStack, vmGetDict(pVM)->pCompile);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ g e t - o r d e r
+** SEARCH ( -- widn ... wid1 n )
+** Returns the number of word lists n in the search order and the word list
+** identifiers widn ... wid1 identifying these word lists. wid1 identifies
+** the word list that is searched first, and widn the word list that is
+** searched last. The search order is unaffected.
+**************************************************************************/
+static void getOrder(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ int nLists = pDict->nLists;
+ int i;
+
+ ficlLockDictionary(TRUE);
+ for (i = 0; i < nLists; i++)
+ {
+ stackPushPtr(pVM->pStack, pDict->pSearch[i]);
+ }
+
+ stackPushUNS(pVM->pStack, nLists);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ s e a r c h - w o r d l i s t
+** SEARCH ( c-addr u wid -- 0 | xt 1 | xt -1 )
+** Find the definition identified by the string c-addr u in the word list
+** identified by wid. If the definition is not found, return zero. If the
+** definition is found, return its execution token xt and one (1) if the
+** definition is immediate, minus-one (-1) otherwise.
+**************************************************************************/
+static void searchWordlist(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ UNS16 hashCode;
+ FICL_WORD *pFW;
+ FICL_HASH *pHash = stackPopPtr(pVM->pStack);
+
+ si.count = (FICL_COUNT)stackPopUNS(pVM->pStack);
+ si.cp = stackPopPtr(pVM->pStack);
+ hashCode = hashHashCode(si);
+
+ ficlLockDictionary(TRUE);
+ pFW = hashLookup(pHash, si, hashCode);
+ ficlLockDictionary(FALSE);
+
+ if (pFW)
+ {
+ stackPushPtr(pVM->pStack, pFW);
+ stackPushINT(pVM->pStack, (wordIsImmediate(pFW) ? 1 : -1));
+ }
+ else
+ {
+ stackPushUNS(pVM->pStack, 0);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ s e t - c u r r e n t
+** SEARCH ( wid -- )
+** Set the compilation word list to the word list identified by wid.
+**************************************************************************/
+static void setCurrent(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = stackPopPtr(pVM->pStack);
+ FICL_DICT *pDict = vmGetDict(pVM);
+ ficlLockDictionary(TRUE);
+ pDict->pCompile = pHash;
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ s e t - o r d e r
+** SEARCH ( widn ... wid1 n -- )
+** Set the search order to the word lists identified by widn ... wid1.
+** Subsequently, word list wid1 will be searched first, and word list
+** widn searched last. If n is zero, empty the search order. If n is minus
+** one, set the search order to the implementation-defined minimum
+** search order. The minimum search order shall include the words
+** FORTH-WORDLIST and SET-ORDER. A system shall allow n to
+** be at least eight.
+**************************************************************************/
+static void setOrder(FICL_VM *pVM)
+{
+ int i;
+ int nLists = stackPopINT(pVM->pStack);
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ if (nLists > FICL_DEFAULT_VOCS)
+ {
+ vmThrowErr(pVM, "set-order error: list would be too large");
+ }
+
+ ficlLockDictionary(TRUE);
+
+ if (nLists >= 0)
+ {
+ dp->nLists = nLists;
+ for (i = nLists-1; i >= 0; --i)
+ {
+ dp->pSearch[i] = stackPopPtr(pVM->pStack);
+ }
+ }
+ else
+ {
+ dictResetSearchOrder(dp);
+ }
+
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l - w o r d l i s t
+** SEARCH ( -- wid )
+** Create a new empty word list, returning its word list identifier wid.
+** The new word list may be returned from a pool of preallocated word
+** lists or may be dynamically allocated in data space. A system shall
+** allow the creation of at least 8 new word lists in addition to any
+** provided as part of the system.
+** Notes:
+** 1. ficl creates a new single-list hash in the dictionary and returns
+** its address.
+** 2. ficl-wordlist takes an arg off the stack indicating the number of
+** hash entries in the wordlist. Ficl 2.02 and later define WORDLIST as
+** : wordlist 1 ficl-wordlist ;
+**************************************************************************/
+static void ficlWordlist(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_HASH *pHash;
+ FICL_UNS nBuckets;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ nBuckets = stackPopUNS(pVM->pStack);
+ pHash = dictCreateWordlist(dp, nBuckets);
+ stackPushPtr(pVM->pStack, pHash);
+ return;
+}
+
+
+/**************************************************************************
+ S E A R C H >
+** ficl ( -- wid )
+** Pop wid off the search order. Error if the search order is empty
+**************************************************************************/
+static void searchPop(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ int nLists;
+
+ ficlLockDictionary(TRUE);
+ nLists = dp->nLists;
+ if (nLists == 0)
+ {
+ vmThrowErr(pVM, "search> error: empty search order");
+ }
+ stackPushPtr(pVM->pStack, dp->pSearch[--dp->nLists]);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ > S E A R C H
+** ficl ( wid -- )
+** Push wid onto the search order. Error if the search order is full.
+**************************************************************************/
+static void searchPush(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ ficlLockDictionary(TRUE);
+ if (dp->nLists > FICL_DEFAULT_VOCS)
+ {
+ vmThrowErr(pVM, ">search error: search order overflow");
+ }
+ dp->pSearch[dp->nLists++] = stackPopPtr(pVM->pStack);
+ ficlLockDictionary(FALSE);
+ return;
+}
+
+
+/**************************************************************************
+ W I D - G E T - N A M E
+** ficl ( wid -- c-addr u )
+** Get wid's (optional) name and push onto stack as a counted string
+**************************************************************************/
+static void widGetName(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = vmPop(pVM).p;
+ char *cp = pHash->name;
+ FICL_INT len = 0;
+
+ if (cp)
+ len = strlen(cp);
+
+ vmPush(pVM, LVALUEtoCELL(cp));
+ vmPush(pVM, LVALUEtoCELL(len));
+ return;
+}
+
+/**************************************************************************
+ W I D - S E T - N A M E
+** ficl ( wid c-addr -- )
+** Set wid's name pointer to the \0 terminated string address supplied
+**************************************************************************/
+static void widSetName(FICL_VM *pVM)
+{
+ char *cp = (char *)vmPop(pVM).p;
+ FICL_HASH *pHash = vmPop(pVM).p;
+ pHash->name = cp;
+ return;
+}
+
+
+/**************************************************************************
+ setParentWid
+** FICL
+** setparentwid ( parent-wid wid -- )
+** Set WID's link field to the parent-wid. search-wordlist will
+** iterate through all the links when finding words in the child wid.
+**************************************************************************/
+static void setParentWid(FICL_VM *pVM)
+{
+ FICL_HASH *parent, *child;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ child = (FICL_HASH *)stackPopPtr(pVM->pStack);
+ parent = (FICL_HASH *)stackPopPtr(pVM->pStack);
+
+ child->link = parent;
+ return;
+}
+
+
+/**************************************************************************
+ f i c l C o m p i l e S e a r c h
+** Builds the primitive wordset and the environment-query namespace.
+**************************************************************************/
+
+void ficlCompileSearch(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ /*
+ ** optional SEARCH-ORDER word set
+ */
+ dictAppendWord(dp, ">search", searchPush, FW_DEFAULT);
+ dictAppendWord(dp, "search>", searchPop, FW_DEFAULT);
+ dictAppendWord(dp, "definitions",
+ definitions, FW_DEFAULT);
+ dictAppendWord(dp, "forth-wordlist",
+ forthWordlist, FW_DEFAULT);
+ dictAppendWord(dp, "get-current",
+ getCurrent, FW_DEFAULT);
+ dictAppendWord(dp, "get-order", getOrder, FW_DEFAULT);
+ dictAppendWord(dp, "search-wordlist",
+ searchWordlist, FW_DEFAULT);
+ dictAppendWord(dp, "set-current",
+ setCurrent, FW_DEFAULT);
+ dictAppendWord(dp, "set-order", setOrder, FW_DEFAULT);
+ dictAppendWord(dp, "ficl-wordlist",
+ ficlWordlist, FW_DEFAULT);
+
+ /*
+ ** Set SEARCH environment query values
+ */
+ ficlSetEnv(pSys, "search-order", FICL_TRUE);
+ ficlSetEnv(pSys, "search-order-ext", FICL_TRUE);
+ ficlSetEnv(pSys, "wordlists", FICL_DEFAULT_VOCS);
+
+ dictAppendWord(dp, "wid-get-name", widGetName, FW_DEFAULT);
+ dictAppendWord(dp, "wid-set-name", widSetName, FW_DEFAULT);
+ dictAppendWord(dp, "wid-set-super",
+ setParentWid, FW_DEFAULT);
+ return;
+}
+
diff --git a/softcore.c b/softcore.c
new file mode 100644
index 000000000000..2e38728a49a9
--- /dev/null
+++ b/softcore.c
@@ -0,0 +1,1028 @@
+/*******************************************************************
+** s o f t c o r e . c
+** Forth Inspired Command Language -
+** Words from CORE set written in FICL
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 27 December 1997
+** Last update: Thu Jun 13 02:57:00 2002
+*******************************************************************/
+/*
+** DO NOT EDIT THIS FILE -- it is generated by softwords/softcore.py
+** Make changes to the .fr files in ficl/softwords instead.
+** This file contains definitions that are compiled into the
+** system dictionary by the first virtual machine to be created.
+** Created automagically by ficl/softwords/softcore.py
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+
+#include "ficl.h"
+
+static char softWords[] =
+#if FICL_WANT_SOFTWORDS
+/*
+** ficl/softwords/softcore.fr
+** FICL soft extensions
+** John Sadler (john_sadler@alum.mit.edu)
+** September, 1998
+*/
+/*
+** Ficl USER variables
+** See words.c for primitive def'n of USER
+*/
+ ".( loading ficl soft extensions ) cr "
+#if FICL_WANT_USER
+ "variable nUser 0 nUser ! "
+ ": user "
+ "nUser dup @ user 1 swap +! ; "
+#endif
+/*
+** ficl extras
+*/
+ ": empty depth 0 ?do drop loop ; "
+ ": cell- [ 1 cells ] literal - ; "
+ ": -rot 2 -roll ; "
+/*
+** CORE
+*/
+ ": abs "
+ "dup 0< if negate endif ; "
+ "decimal 32 constant bl "
+ ": space bl emit ; "
+ ": spaces 0 ?do space loop ; "
+ ": abort\" "
+ "state @ if "
+ "postpone if "
+ "postpone .\" "
+ "postpone cr "
+ "-2 "
+ "postpone literal "
+ "postpone throw "
+ "postpone endif "
+ "else "
+ "[char] \" parse "
+ "rot if "
+ "type "
+ "cr "
+ "-2 throw "
+ "else "
+ "2drop "
+ "endif "
+ "endif "
+ "; immediate "
+/*
+** CORE EXT
+*/
+ ".( loading CORE EXT words ) cr "
+ "0 constant false "
+ "false invert constant true "
+ ": <> = 0= ; "
+ ": 0<> 0= 0= ; "
+ ": compile, , ; "
+ ": convert char+ 65535 >number drop ; "
+ ": erase 0 fill ; "
+ "variable span "
+ ": expect accept span ! ; "
+ ": nip swap drop ; "
+ ": tuck swap over ; "
+ ": within over - >r - r> u< ; "
+/*
+** LOCAL EXT word set
+*/
+#if FICL_WANT_LOCALS
+ ": locals| "
+ "begin "
+ "bl word count "
+ "dup 0= abort\" where's the delimiter??\" "
+ "over c@ "
+ "[char] | - over 1- or "
+ "while "
+ "(local) "
+ "repeat 2drop 0 0 (local) "
+ "; immediate "
+ ": local bl word count (local) ; immediate "
+ ": 2local bl word count (2local) ; immediate "
+ ": end-locals 0 0 (local) ; immediate "
+#endif
+/*
+** TOOLS word set...
+*/
+ ": ? @ . ; "
+ ": dump "
+ "0 ?do "
+ "dup c@ . 1+ "
+ "i 7 and 7 = if cr endif "
+ "loop drop "
+ "; "
+/*
+** SEARCH+EXT words and ficl helpers
+*/
+ ".( loading SEARCH & SEARCH-EXT words ) cr "
+ ": brand-wordlist last-word >name drop wid-set-name ; "
+ ": ficl-named-wordlist "
+ "ficl-wordlist dup create , brand-wordlist does> @ ; "
+ ": wordlist "
+ "1 ficl-wordlist ; "
+ ": ficl-set-current "
+ "get-current swap set-current ; "
+ ": do-vocabulary "
+ "does> @ search> drop >search ; "
+ ": ficl-vocabulary "
+ "ficl-named-wordlist do-vocabulary ; "
+ ": vocabulary "
+ "1 ficl-vocabulary ; "
+ ": previous search> drop ; "
+ "1 ficl-named-wordlist hidden "
+ ": hide hidden dup >search ficl-set-current ; "
+ ": also "
+ "search> dup >search >search ; "
+ ": forth "
+ "search> drop "
+ "forth-wordlist >search ; "
+ ": only "
+ "-1 set-order ; "
+ "hide "
+ ": list-wid "
+ "dup wid-get-name "
+ "?dup if "
+ "type drop "
+ "else "
+ "drop .\" (unnamed wid) \" x. "
+ "endif cr "
+ "; "
+ "set-current "
+ ": order "
+ ".\" Search:\" cr "
+ "get-order 0 ?do 3 spaces list-wid loop cr "
+ ".\" Compile: \" get-current list-wid cr "
+ "; "
+ ": debug ' debug-xt ; immediate "
+ ": on-step .\" S: \" .s cr ; "
+ ": strdup "
+ "0 locals| addr2 length c-addr | end-locals "
+ "length 1 + allocate "
+ "0= if "
+ "to addr2 "
+ "c-addr addr2 length move "
+ "addr2 length 0 "
+ "else "
+ "0 -1 "
+ "endif "
+ "; "
+ ": strcat "
+ "0 locals| b-length b-u b-addr a-u a-addr | end-locals "
+ "b-u to b-length "
+ "b-addr a-addr a-u + b-length move "
+ "a-addr a-u b-length + "
+ "; "
+ ": strcpy "
+ "locals| b-u b-addr a-u a-addr | end-locals "
+ "a-addr 0 b-addr b-u strcat "
+ "; "
+ "previous "
+/*
+** E N D S O F T C O R E . F R
+*/
+#if FICL_WANT_LOCALS
+/*
+** ficl/softwords/jhlocal.fr
+** stack comment style local syntax...
+*/
+ ".( loading Johns-Hopkins locals ) cr "
+ "hide "
+ "0 constant zero "
+ ": ?-- "
+ "2dup s\" --\" compare 0= ; "
+ ": ?} "
+ "2dup s\" }\" compare 0= ; "
+ ": ?| "
+ "2dup s\" |\" compare 0= ; "
+ ": ?2loc "
+ "over dup c@ [char] 2 = "
+ "swap 1+ c@ [char] : = and "
+ "if "
+ "2 - swap char+ char+ swap "
+ "true "
+ "else "
+ "false "
+ "endif "
+ "; "
+ ": ?delim "
+ "?| if 2drop 1 exit endif "
+ "?-- if 2drop 2 exit endif "
+ "?} if 2drop 3 exit endif "
+ "dup 0= "
+ "if 2drop 4 exit endif "
+ "0 "
+ "; "
+ "set-current "
+ ": { "
+ "0 dup locals| locstate | "
+ "begin "
+ "parse-word "
+ "?delim dup to locstate "
+ "0= while "
+ "rot 1+ "
+ "repeat "
+ "0 ?do "
+ "?2loc if (2local) else (local) endif "
+ "loop "
+ "locstate 1 = if "
+ "begin "
+ "parse-word "
+ "?delim dup to locstate "
+ "0= while "
+ "?2loc if "
+ "postpone zero postpone zero (2local) "
+ "else "
+ "postpone zero (local) "
+ "endif "
+ "repeat "
+ "endif "
+ "0 0 (local) "
+ "locstate 2 = if "
+ "begin "
+ "parse-word "
+ "?delim dup to locstate "
+ "3 < while "
+ "locstate 0= if 2drop endif "
+ "repeat "
+ "endif "
+ "locstate 3 <> abort\" syntax error in { } local line\" "
+ "; immediate compile-only "
+ "previous "
+#endif
+/*
+** ficl/softwords/marker.fr
+** Ficl implementation of CORE EXT MARKER
+*/
+ ".( loading MARKER ) cr "
+ ": marker "
+ "create "
+ "get-current , "
+ "get-order dup , "
+ "0 ?do , loop "
+ "does> "
+ "0 set-order "
+ "dup body> >name drop "
+ "here - allot "
+ "dup @ "
+ "dup set-current forget-wid "
+ "cell+ dup @ swap "
+ "over cells + swap "
+ "0 ?do "
+ "dup @ dup "
+ ">search forget-wid "
+ "cell- "
+ "loop "
+ "drop "
+ "; "
+/*
+**
+** Prefix words for ficl
+** submitted by Larry Hastings, larry@hastings.org
+**
+*/
+ "variable save-current "
+ ": start-prefixes get-current save-current ! <prefixes> set-current ; "
+ ": end-prefixes save-current @ set-current ; "
+ ": show-prefixes <prefixes> >search words search> drop ; "
+#if (FICL_EXTENDED_PREFIX)
+ "start-prefixes "
+ ": \" postpone s\" ; immediate "
+ ": .( postpone .( ; immediate "
+/*
+** add 0b, 0o, 0d, and 0x as prefixes
+** these temporarily shift the base to 2, 8, 10, and 16 respectively
+** and consume the next number in the input stream, pushing/compiling
+** as normal
+*/
+ ": 0b 2 __tempbase ; immediate "
+ ": 0o 8 __tempbase ; immediate "
+ "end-prefixes "
+#endif
+/*
+** ficl/softwords/ifbrack.fr
+** ANS conditional compile directives [if] [else] [then]
+** Requires ficl 2.0 or greater...
+*/
+ "hide "
+ ": ?[if] "
+ "2dup s\" [if]\" compare-insensitive 0= "
+ "; "
+ ": ?[else] "
+ "2dup s\" [else]\" compare-insensitive 0= "
+ "; "
+ ": ?[then] "
+ "2dup s\" [then]\" compare-insensitive 0= >r "
+ "2dup s\" [endif]\" compare-insensitive 0= r> "
+ "or "
+ "; "
+ "set-current "
+ ": [else] "
+ "1 "
+ "begin "
+ "begin "
+ "parse-word dup while "
+ "?[if] if "
+ "2drop 1+ "
+ "else "
+ "?[else] if "
+ "2drop 1- dup if 1+ endif "
+ "else "
+ "?[then] if 2drop 1- else 2drop endif "
+ "endif "
+ "endif ?dup 0= if exit endif "
+ "repeat 2drop "
+ "refill 0= until "
+ "drop "
+ "; immediate "
+ ": [if] "
+ "0= if postpone [else] then ; immediate "
+ ": [then] ; immediate "
+ ": [endif] ; immediate "
+ "previous "
+#if FICL_WANT_OOP
+/*
+** ficl/softwords/oo.fr
+** F I C L O - O E X T E N S I O N S
+** john sadler aug 1998
+*/
+ ".( loading ficl O-O extensions ) cr "
+ "17 ficl-vocabulary oop "
+ "also oop definitions "
+ "user current-class "
+ "0 current-class ! "
+/*
+** L A T E B I N D I N G
+*/
+ ": parse-method "
+ "parse-word "
+ "postpone sliteral "
+ "; compile-only "
+ ": (lookup-method) { class 2:name -- class 0 | class xt 1 | class xt -1 } "
+ "class name class cell+ @ "
+ "search-wordlist "
+ "; "
+ ": lookup-method { class 2:name -- class xt } "
+ "class name (lookup-method) "
+ "0= if "
+ "name type .\" not found in \" "
+ "class body> >name type "
+ "cr abort "
+ "endif "
+ "; "
+ ": find-method-xt "
+ "parse-word lookup-method "
+ "; "
+ ": catch-method "
+ "lookup-method catch "
+ "; "
+ ": exec-method "
+ "lookup-method execute "
+ "; "
+ ": --> "
+ "state @ 0= if "
+ "find-method-xt execute "
+ "else "
+ "parse-method postpone exec-method "
+ "endif "
+ "; immediate "
+ ": c-> "
+ "state @ 0= if "
+ "find-method-xt catch "
+ "else "
+ "parse-method postpone catch-method "
+ "endif "
+ "; immediate "
+ ": method create does> body> >name lookup-method execute ; "
+/*
+** E A R L Y B I N D I N G
+*/
+ "1 ficl-named-wordlist instance-vars "
+ "instance-vars dup >search ficl-set-current "
+ ": => "
+ "drop find-method-xt compile, drop "
+ "; immediate compile-only "
+ ": my=> "
+ "current-class @ dup postpone => "
+ "; immediate compile-only "
+ ": my=[ "
+ "current-class @ "
+ "begin "
+ "parse-word 2dup "
+ "s\" ]\" compare while "
+ "lookup-method "
+ "dup compile, "
+ "dup ?object if "
+ "nip >body cell+ @ "
+ "else "
+ "drop "
+ "endif "
+ "repeat 2drop drop "
+ "; immediate compile-only "
+/*
+** I N S T A N C E V A R I A B L E S
+*/
+ ": do-instance-var "
+ "does> "
+ "nip @ + "
+ "; "
+ ": addr-units: "
+ "create over , + "
+ "do-instance-var "
+ "; "
+ ": chars: "
+ "chars addr-units: ; "
+ ": char: "
+ "1 chars: ; "
+ ": cells: "
+ "cells >r aligned r> addr-units: "
+ "; "
+ ": cell: "
+ "1 cells: ; "
+ ": do-aggregate "
+ "objectify "
+ "does> "
+ "2@ "
+ "2swap drop "
+ "+ swap "
+ "; "
+ ": obj: { offset class meta -- offset' } "
+ "create offset , class , "
+ "class meta --> get-size offset + "
+ "do-aggregate "
+ "; "
+ ": array: "
+ "locals| meta class nobjs offset | "
+ "create offset , class , "
+ "class meta --> get-size nobjs * offset + "
+ "do-aggregate "
+ "; "
+ ": ref: "
+ "locals| meta class offset | "
+ "create offset , class , "
+ "offset cell+ "
+ "does> "
+ "2@ "
+ "2swap drop + @ swap "
+ "; "
+#if FICL_WANT_VCALL
+ ": vcall: "
+ "current-class @ 8 + dup @ dup 1+ rot ! "
+ "create , , "
+ "does> "
+ "nip 2@ vcall "
+ "; "
+ ": vcallr: 0x80000000 or vcall: ; "
+#if FICL_WANT_FLOAT
+ ": vcallf: "
+ "0x80000000 or "
+ "current-class @ 8 + dup @ dup 1+ rot ! "
+ "create , , "
+ "does> "
+ "nip 2@ vcall f> "
+ "; "
+#endif /* FLOAT */
+#endif /* VCALL */
+ ": end-class "
+ "swap ! set-current "
+ "search> drop "
+ "; "
+ ": suspend-class end-class ; "
+ "set-current previous "
+ ": do-do-instance "
+ "s\" : .do-instance does> [ current-class @ ] literal ;\" "
+ "evaluate "
+ "; "
+/*
+** M E T A C L A S S
+*/
+ ":noname "
+ "wordlist "
+ "create "
+ "immediate "
+ "0 , "
+ "dup , "
+#if FICL_WANT_VCALL
+ "4 cells , "
+#else
+ "3 cells , "
+#endif
+ "ficl-set-current "
+ "does> dup "
+ "; execute metaclass "
+ "metaclass drop cell+ @ brand-wordlist "
+ "metaclass drop current-class ! "
+ "do-do-instance "
+ "instance-vars >search "
+ "create .super "
+ "0 cells , do-instance-var "
+ "create .wid "
+ "1 cells , do-instance-var "
+#if FICL_WANT_VCALL
+ "create .vtCount "
+ "2 cells , do-instance-var "
+ "create .size "
+ "3 cells , do-instance-var "
+#else
+ "create .size "
+ "2 cells , do-instance-var "
+#endif
+ ": get-size metaclass => .size @ ; "
+ ": get-wid metaclass => .wid @ ; "
+ ": get-super metaclass => .super @ ; "
+#if FICL_WANT_VCALL
+ ": get-vtCount metaclass => .vtCount @ ; "
+ ": get-vtAdd metaclass => .vtCount ; "
+#endif
+ ": instance "
+ "locals| meta parent | "
+ "create "
+ "here parent --> .do-instance "
+ "parent meta metaclass => get-size "
+ "allot "
+ "; "
+ ": array "
+ "locals| meta parent nobj | "
+ "create nobj "
+ "here parent --> .do-instance "
+ "parent meta metaclass => get-size "
+ "nobj * allot "
+ "; "
+ ": new "
+ "metaclass => instance --> init "
+ "; "
+ ": new-array "
+ "metaclass => array "
+ "--> array-init "
+ "; "
+ ": alloc "
+ "locals| meta class | "
+ "class meta metaclass => get-size allocate "
+ "abort\" allocate failed \" "
+ "class 2dup --> init "
+ "; "
+ ": alloc-array "
+ "locals| meta class nobj | "
+ "class meta metaclass => get-size "
+ "nobj * allocate "
+ "abort\" allocate failed \" "
+ "nobj over class --> array-init "
+ "class "
+ "; "
+ ": allot { 2:this -- 2:instance } "
+ "here "
+ "this my=> get-size allot "
+ "this drop 2dup --> init "
+ "; "
+ ": allot-array { nobj 2:this -- 2:instance } "
+ "here "
+ "this my=> get-size nobj * allot "
+ "this drop 2dup "
+ "nobj -rot --> array-init "
+ "; "
+ ": ref "
+ "drop create , , "
+ "does> 2@ "
+ "; "
+ ": resume-class { 2:this -- old-wid addr[size] size } "
+ "this --> .wid @ ficl-set-current "
+ "this --> .size dup @ "
+ "instance-vars >search "
+ "; "
+ ": sub "
+ "wordlist "
+ "locals| wid meta parent | "
+ "parent meta metaclass => get-wid "
+ "wid wid-set-super "
+ "create immediate "
+ "wid brand-wordlist "
+ "here current-class ! "
+ "parent , "
+ "wid , "
+#if FICL_WANT_VCALL
+ "parent meta --> get-vtCount , "
+#endif
+ "here parent meta --> get-size dup , "
+ "metaclass => .do-instance "
+ "wid ficl-set-current -rot "
+ "do-do-instance "
+ "instance-vars >search "
+ "; "
+ ": offset-of "
+ "drop find-method-xt nip >body @ ; "
+ ": id "
+ "drop body> >name ; "
+ ": methods "
+ "locals| meta class | "
+ "begin "
+ "class body> >name type .\" methods:\" cr "
+ "class meta --> get-wid >search words cr previous "
+ "class meta metaclass => get-super "
+ "dup to class "
+ "0= until cr "
+ "; "
+ ": pedigree "
+ "locals| meta class | "
+ "begin "
+ "class body> >name type space "
+ "class meta metaclass => get-super "
+ "dup to class "
+ "0= until cr "
+ "; "
+ ": see "
+ "metaclass => get-wid >search see previous ; "
+ ": debug "
+ "find-method-xt debug-xt ; "
+ "previous set-current "
+/*
+** META is a nickname for the address of METACLASS...
+*/
+ "metaclass drop "
+ "constant meta "
+/*
+** SUBCLASS is a nickname for a class's SUB method...
+*/
+ ": subclass --> sub ; "
+#if FICL_WANT_VCALL
+ ": hasvtable 4 + ; immediate "
+#endif
+/*
+** O B J E C T
+*/
+ ":noname "
+ "wordlist "
+ "create immediate "
+ "0 , "
+ "dup , "
+ "0 , "
+ "ficl-set-current "
+ "does> meta "
+ "; execute object "
+ "object drop cell+ @ brand-wordlist "
+ "object drop current-class ! "
+ "do-do-instance "
+ "instance-vars >search "
+ ": class "
+ "nip meta ; "
+ ": init "
+ "meta "
+ "metaclass => get-size "
+ "erase ; "
+ ": array-init "
+ "0 dup locals| &init &next class inst | "
+ "class s\" init\" lookup-method to &init "
+ "s\" next\" lookup-method to &next "
+ "drop "
+ "0 ?do "
+ "inst class 2dup "
+ "&init execute "
+ "&next execute drop to inst "
+ "loop "
+ "; "
+ ": free "
+ "drop free "
+ "abort\" free failed \" "
+ "; "
+ ": super "
+ "meta metaclass => get-super ; "
+ ": pedigree "
+ "object => class "
+ "metaclass => pedigree ; "
+ ": size "
+ "object => class "
+ "metaclass => get-size ; "
+ ": methods "
+ "object => class "
+ "metaclass => methods ; "
+ ": index "
+ "locals| class inst | "
+ "inst class "
+ "object => class "
+ "metaclass => get-size * "
+ "inst + class ; "
+ ": next "
+ "locals| class inst | "
+ "inst class "
+ "object => class "
+ "metaclass => get-size "
+ "inst + "
+ "class ; "
+ ": prev "
+ "locals| class inst | "
+ "inst class "
+ "object => class "
+ "metaclass => get-size "
+ "inst swap - "
+ "class ; "
+ ": debug "
+ "find-method-xt debug-xt ; "
+ "previous set-current "
+ "only definitions "
+ ": oo only also oop definitions ; "
+#endif
+#if (FICL_WANT_OOP)
+/*
+** ficl/softwords/classes.fr
+** F I C L 2 . 0 C L A S S E S
+*/
+ ".( loading ficl utility classes ) cr "
+ "also oop definitions "
+ "object subclass c-ref "
+ "cell: .class "
+ "cell: .instance "
+ ": get "
+ "drop 2@ ; "
+ ": set "
+ "drop 2! ; "
+ "end-class "
+ "object subclass c-byte "
+ "char: .payload "
+ ": get drop c@ ; "
+ ": set drop c! ; "
+ "end-class "
+ "object subclass c-2byte "
+ "2 chars: .payload "
+ ": get drop w@ ; "
+ ": set drop w! ; "
+ "end-class "
+ "object subclass c-4byte "
+ "4 chars: .payload "
+ ": get drop q@ ; "
+ ": set drop q! ; "
+ "end-class "
+ "object subclass c-cell "
+ "cell: .payload "
+ ": get drop @ ; "
+ ": set drop ! ; "
+ "end-class "
+/*
+** C - P T R
+*/
+ "object subclass c-ptr "
+ "c-cell obj: .addr "
+ ": get-ptr "
+ "c-ptr => .addr "
+ "c-cell => get "
+ "; "
+ ": set-ptr "
+ "c-ptr => .addr "
+ "c-cell => set "
+ "; "
+ ": clr-ptr "
+ "0 -rot c-ptr => .addr c-cell => set "
+ "; "
+ ": ?null "
+ "c-ptr => get-ptr 0= "
+ "; "
+ ": inc-ptr "
+ "2dup 2dup "
+ "c-ptr => get-ptr -rot "
+ "--> @size + -rot "
+ "c-ptr => set-ptr "
+ "; "
+ ": dec-ptr "
+ "2dup 2dup "
+ "c-ptr => get-ptr -rot "
+ "--> @size - -rot "
+ "c-ptr => set-ptr "
+ "; "
+ ": index-ptr { index 2:this -- } "
+ "this --> get-ptr "
+ "this --> @size index * + "
+ "this --> set-ptr "
+ "; "
+ "end-class "
+/*
+** C - C E L L P T R
+*/
+ "c-ptr subclass c-cellPtr "
+ ": @size 2drop 1 cells ; "
+ ": get "
+ "c-ptr => get-ptr @ "
+ "; "
+ ": set "
+ "c-ptr => get-ptr ! "
+ "; "
+ "end-class "
+/*
+** C - 4 B Y T E P T R
+*/
+ "c-ptr subclass c-4bytePtr "
+ ": @size 2drop 4 ; "
+ ": get "
+ "c-ptr => get-ptr q@ "
+ "; "
+ ": set "
+ "c-ptr => get-ptr q! "
+ "; "
+ "end-class "
+/*
+** C - 2 B Y T E P T R
+*/
+ "c-ptr subclass c-2bytePtr "
+ ": @size 2drop 2 ; "
+ ": get "
+ "c-ptr => get-ptr w@ "
+ "; "
+ ": set "
+ "c-ptr => get-ptr w! "
+ "; "
+ "end-class "
+/*
+** C - B Y T E P T R
+*/
+ "c-ptr subclass c-bytePtr "
+ ": @size 2drop 1 ; "
+ ": get "
+ "c-ptr => get-ptr c@ "
+ "; "
+ ": set "
+ "c-ptr => get-ptr c! "
+ "; "
+ "end-class "
+ "previous definitions "
+#endif
+#if (FICL_WANT_OOP)
+/*
+** ficl/softwords/string.fr
+*/
+/*
+** C - S T R I N G
+*/
+ ".( loading ficl string class ) cr "
+ "also oop definitions "
+ "object subclass c-string "
+ "c-cell obj: .count "
+ "c-cell obj: .buflen "
+ "c-ptr obj: .buf "
+ "32 constant min-buf "
+ ": get-count my=[ .count get ] ; "
+ ": set-count my=[ .count set ] ; "
+ ": ?empty --> get-count 0= ; "
+ ": get-buflen my=[ .buflen get ] ; "
+ ": set-buflen my=[ .buflen set ] ; "
+ ": get-buf my=[ .buf get-ptr ] ; "
+ ": set-buf { ptr len 2:this -- } "
+ "ptr this my=[ .buf set-ptr ] "
+ "len this my=> set-buflen "
+ "; "
+ ": clr-buf "
+ "0 0 2over my=> set-buf "
+ "0 -rot my=> set-count "
+ "; "
+ ": free-buf { 2:this -- } "
+ "this my=> get-buf "
+ "?dup if "
+ "free "
+ "abort\" c-string free failed\" "
+ "this my=> clr-buf "
+ "endif "
+ "; "
+ ": size-buf { size 2:this -- } "
+ "size 0< abort\" need positive size for size-buf\" "
+ "size 0= if "
+ "this --> free-buf exit "
+ "endif "
+ "my=> min-buf size over / 1+ * chars to size "
+ "this --> get-buflen 0= "
+ "if "
+ "size allocate "
+ "abort\" out of memory\" "
+ "size this --> set-buf "
+ "size this --> set-buflen "
+ "exit "
+ "endif "
+ "size this --> get-buflen > if "
+ "this --> get-buf size resize "
+ "abort\" out of memory\" "
+ "size this --> set-buf "
+ "endif "
+ "; "
+ ": set { c-addr u 2:this -- } "
+ "u this --> size-buf "
+ "u this --> set-count "
+ "c-addr this --> get-buf u move "
+ "; "
+ ": get { 2:this -- c-addr u } "
+ "this --> get-buf "
+ "this --> get-count "
+ "; "
+ ": cat { c-addr u 2:this -- } "
+ "this --> get-count u + dup >r "
+ "this --> size-buf "
+ "c-addr this --> get-buf this --> get-count + u move "
+ "r> this --> set-count "
+ "; "
+ ": type { 2:this -- } "
+ "this --> ?empty if .\" (empty) \" exit endif "
+ "this --> .buf --> get-ptr "
+ "this --> .count --> get "
+ "type "
+ "; "
+ ": compare "
+ "--> get "
+ "2swap "
+ "--> get "
+ "2swap compare "
+ "; "
+ ": hashcode "
+ "--> get hash "
+ "; "
+ ": free 2dup --> free-buf object => free ; "
+ "end-class "
+ "c-string subclass c-hashstring "
+ "c-2byte obj: .hashcode "
+ ": set-hashcode { 2:this -- } "
+ "this --> super --> hashcode "
+ "this --> .hashcode --> set "
+ "; "
+ ": get-hashcode "
+ "--> .hashcode --> get "
+ "; "
+ ": set "
+ "2swap 2over --> super --> set "
+ "--> set-hashcode "
+ "; "
+ ": cat "
+ "2swap 2over --> super --> cat "
+ "--> set-hashcode "
+ "; "
+ "end-class "
+ "previous definitions "
+#endif
+#if FICL_WANT_FILE
+/*
+**
+** File Access words for ficl
+** submitted by Larry Hastings, larry@hastings.org
+**
+*/
+ ": r/o 1 ; "
+ ": r/w 3 ; "
+ ": w/o 2 ; "
+ ": bin 8 or ; "
+ ": included "
+ "r/o bin open-file 0= if "
+ "locals| f | end-locals "
+ "f include-file "
+ "else "
+ "drop "
+ "endif "
+ "; "
+ ": include parse-word included ; "
+#endif
+#endif /* WANT_SOFTWORDS */
+ "quit ";
+
+
+void ficlCompileSoftCore(FICL_SYSTEM *pSys)
+{
+ FICL_VM *pVM = pSys->vmList;
+ CELL id = pVM->sourceID;
+ int ret = sizeof (softWords);
+ assert(pVM);
+ pVM->sourceID.i = -1;
+ ret = ficlExec(pVM, softWords);
+ pVM->sourceID = id;
+ if (ret == VM_ERREXIT)
+ assert(FALSE);
+ return;
+}
+
+
+
diff --git a/softwords/classes.fr b/softwords/classes.fr
new file mode 100644
index 000000000000..1a00cc95b913
--- /dev/null
+++ b/softwords/classes.fr
@@ -0,0 +1,172 @@
+\ #if (FICL_WANT_OOP)
+\ ** ficl/softwords/classes.fr
+\ ** F I C L 2 . 0 C L A S S E S
+\ john sadler 1 sep 98
+\ Needs oop.fr
+
+.( loading ficl utility classes ) cr
+also oop definitions
+
+\ REF subclass holds a pointer to an object. It's
+\ mainly for aggregation to help in making data structures.
+\
+object subclass c-ref
+ cell: .class
+ cell: .instance
+
+ : get ( inst class -- refinst refclass )
+ drop 2@ ;
+ : set ( refinst refclass inst class -- )
+ drop 2! ;
+end-class
+
+object subclass c-byte
+ char: .payload
+
+ : get drop c@ ;
+ : set drop c! ;
+end-class
+
+object subclass c-2byte
+ 2 chars: .payload
+
+ : get drop w@ ;
+ : set drop w! ;
+end-class
+
+object subclass c-4byte
+ 4 chars: .payload
+
+ : get drop q@ ;
+ : set drop q! ;
+end-class
+
+
+object subclass c-cell
+ cell: .payload
+
+ : get drop @ ;
+ : set drop ! ;
+end-class
+
+
+\ ** C - P T R
+\ Base class for pointers to scalars (not objects).
+\ Note: use c-ref to make references to objects. C-ptr
+\ subclasses refer to untyped quantities of various sizes.
+
+\ Derived classes must specify the size of the thing
+\ they point to, and supply get and set methods.
+
+\ All derived classes must define the @size method:
+\ @size ( inst class -- addr-units )
+\ Returns the size in address units of the thing the pointer
+\ refers to.
+object subclass c-ptr
+ c-cell obj: .addr
+
+ \ get the value of the pointer
+ : get-ptr ( inst class -- addr )
+ c-ptr => .addr
+ c-cell => get
+ ;
+
+ \ set the pointer to address supplied
+ : set-ptr ( addr inst class -- )
+ c-ptr => .addr
+ c-cell => set
+ ;
+
+ \ force the pointer to be null
+ : clr-ptr
+ 0 -rot c-ptr => .addr c-cell => set
+ ;
+
+ \ return flag indicating null-ness
+ : ?null ( inst class -- flag )
+ c-ptr => get-ptr 0=
+ ;
+
+ \ increment the pointer in place
+ : inc-ptr ( inst class -- )
+ 2dup 2dup ( i c i c i c )
+ c-ptr => get-ptr -rot ( i c addr i c )
+ --> @size + -rot ( addr' i c )
+ c-ptr => set-ptr
+ ;
+
+ \ decrement the pointer in place
+ : dec-ptr ( inst class -- )
+ 2dup 2dup ( i c i c i c )
+ c-ptr => get-ptr -rot ( i c addr i c )
+ --> @size - -rot ( addr' i c )
+ c-ptr => set-ptr
+ ;
+
+ \ index the pointer in place
+ : index-ptr { index 2:this -- }
+ this --> get-ptr ( addr )
+ this --> @size index * + ( addr' )
+ this --> set-ptr
+ ;
+
+end-class
+
+
+\ ** C - C E L L P T R
+\ Models a pointer to cell (a 32 or 64 bit scalar).
+c-ptr subclass c-cellPtr
+ : @size 2drop 1 cells ;
+ \ fetch and store through the pointer
+ : get ( inst class -- cell )
+ c-ptr => get-ptr @
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr !
+ ;
+end-class
+
+
+\ ** C - 4 B Y T E P T R
+\ Models a pointer to a quadbyte scalar
+c-ptr subclass c-4bytePtr
+ : @size 2drop 4 ;
+ \ fetch and store through the pointer
+ : get ( inst class -- value )
+ c-ptr => get-ptr q@
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr q!
+ ;
+ end-class
+
+\ ** C - 2 B Y T E P T R
+\ Models a pointer to a 16 bit scalar
+c-ptr subclass c-2bytePtr
+ : @size 2drop 2 ;
+ \ fetch and store through the pointer
+ : get ( inst class -- value )
+ c-ptr => get-ptr w@
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr w!
+ ;
+end-class
+
+
+\ ** C - B Y T E P T R
+\ Models a pointer to an 8 bit scalar
+c-ptr subclass c-bytePtr
+ : @size 2drop 1 ;
+ \ fetch and store through the pointer
+ : get ( inst class -- value )
+ c-ptr => get-ptr c@
+ ;
+ : set ( value inst class -- )
+ c-ptr => get-ptr c!
+ ;
+end-class
+
+
+previous definitions
+\ #endif
diff --git a/softwords/ficlclass.fr b/softwords/ficlclass.fr
new file mode 100644
index 000000000000..5922c6e19aae
--- /dev/null
+++ b/softwords/ficlclass.fr
@@ -0,0 +1,84 @@
+\ #if (FICL_WANT_OOP)
+\ ** ficl/softwords/ficlclass.fr
+\ Classes to model ficl data structures in objects
+\ This is a demo!
+\ John Sadler 14 Sep 1998
+\
+\ ** C - W O R D
+\ Models a FICL_WORD
+
+object subclass c-word
+ c-word ref: .link
+ c-2byte obj: .hashcode
+ c-byte obj: .flags
+ c-byte obj: .nName
+ c-bytePtr obj: .pName
+ c-cellPtr obj: .pCode
+ c-4byte obj: .param0
+
+ \ Push word's name...
+ : get-name ( inst class -- c-addr u )
+ 2dup
+ my=[ .pName get-ptr ] -rot
+ my=[ .nName get ]
+ ;
+
+ : next ( inst class -- link-inst class )
+ my=> .link ;
+
+ : ?
+ ." c-word: "
+ 2dup --> get-name type cr
+ ;
+
+end-class
+
+\ ** C - W O R D L I S T
+\ Models a FICL_HASH
+\ Example of use:
+\ get-current c-wordlist --> ref current
+\ current --> ?
+\ current --> .hash --> ?
+\ current --> .hash --> next --> ?
+
+object subclass c-wordlist
+ c-wordlist ref: .parent
+ c-ptr obj: .name
+ c-cell obj: .size
+ c-word ref: .hash ( first entry in hash table )
+
+ : ?
+ --> get-name ." ficl wordlist " type cr ;
+ : push drop >search ;
+ : pop 2drop previous ;
+ : set-current drop set-current ;
+ : get-name drop wid-get-name ;
+ : words { 2:this -- }
+ this my=[ .size get ] 0 do
+ i this my=[ .hash index ] ( 2list-head )
+ begin
+ 2dup --> get-name type space
+ --> next over
+ 0= until 2drop cr
+ loop
+ ;
+end-class
+
+\ : named-wid wordlist postpone c-wordlist metaclass => ref ;
+
+
+\ ** C - F I C L S T A C K
+object subclass c-ficlstack
+ c-4byte obj: .nCells
+ c-cellPtr obj: .link
+ c-cellPtr obj: .sp
+ c-4byte obj: .stackBase
+
+ : init 2drop ;
+ : ? 2drop
+ ." ficl stack " cr ;
+ : top
+ --> .sp --> .addr --> prev --> get ;
+end-class
+
+\ #endif
diff --git a/softwords/ficllocal.fr b/softwords/ficllocal.fr
new file mode 100644
index 000000000000..9381247edfc4
--- /dev/null
+++ b/softwords/ficllocal.fr
@@ -0,0 +1,46 @@
+\ ** ficl/softwords/ficllocal.fr
+\ ** stack comment style local syntax...
+\ {{ a b c -- d e }}
+\ variables before the "--" are initialized in reverse order
+\ from the stack. Those after the "--" are zero initialized
+\ Uses locals...
+\ locstate: 0 = looking for -- or }}
+\ 1 = found --
+hide
+0 constant zero
+
+: ?-- s" --" compare 0= ;
+: ?}} s" }}" compare 0= ;
+
+set-current
+
+: {{
+ 0 dup locals| nLocs locstate |
+ begin
+ parse-word
+ ?dup 0= abort" Error: out of text without seeing }}"
+ 2dup 2dup ?-- -rot ?}} or 0=
+ while
+ nLocs 1+ to nLocs
+ repeat
+
+ ?-- if 1 to locstate endif
+
+ nLocs 0 do
+ (local)
+ loop
+
+ locstate 1 = if
+ begin
+ parse-word
+ 2dup ?}} 0=
+ while
+ postpone zero (local)
+ repeat
+ 2drop
+ endif
+
+ 0 0 (local)
+; immediate compile-only
+
+previous
diff --git a/softwords/fileaccess.fr b/softwords/fileaccess.fr
new file mode 100644
index 000000000000..7a4452ac7e75
--- /dev/null
+++ b/softwords/fileaccess.fr
@@ -0,0 +1,23 @@
+\ #if FICL_WANT_FILE
+\ **
+\ ** File Access words for ficl
+\ ** submitted by Larry Hastings, larry@hastings.org
+\ **
+
+: r/o 1 ;
+: r/w 3 ;
+: w/o 2 ;
+: bin 8 or ;
+
+: included
+ r/o bin open-file 0= if
+ locals| f | end-locals
+ f include-file
+ else
+ drop
+ endif
+ ;
+
+: include parse-word included ;
+
+\ #endif
diff --git a/softwords/forml.fr b/softwords/forml.fr
new file mode 100644
index 000000000000..cc684e086131
--- /dev/null
+++ b/softwords/forml.fr
@@ -0,0 +1,72 @@
+\ examples from FORML conference paper Nov 98
+\ sadler
+.( loading FORML examples ) cr
+object --> sub c-example
+ cell: .cell0
+ c-4byte obj: .nCells
+ 4 c-4byte array: .quad
+ c-byte obj: .length
+ 79 chars: .name
+
+ : init ( inst class -- )
+ 2dup object => init
+ s" aardvark" 2swap --> set-name
+ ;
+
+ : get-name ( inst class -- c-addr u )
+ 2dup
+ --> .name -rot ( c-addr inst class )
+ --> .length --> get
+ ;
+
+ : set-name { c-addr u 2:this -- }
+ u this --> .length --> set
+ c-addr this --> .name u move
+ ;
+
+ : ? ( inst class ) c-example => get-name type cr ;
+end-class
+
+
+: test ." this is a test" cr ;
+' test
+c-word --> ref testref
+
+\ add a method to c-word...
+c-word --> get-wid ficl-set-current
+\ list dictionary thread
+: list ( inst class )
+ begin
+ 2dup --> get-name type cr
+ --> next over
+ 0= until
+ 2drop
+;
+set-current
+
+object subclass c-led
+ c-byte obj: .state
+
+ : on { led# 2:this -- }
+ this --> .state --> get
+ 1 led# lshift or dup !oreg
+ this --> .state --> set
+ ;
+
+ : off { led# 2:this -- }
+ this --> .state --> get
+ 1 led# lshift invert and dup !oreg
+ this --> .state --> set
+ ;
+
+end-class
+
+
+object subclass c-switch
+
+ : ?on { bit# 2:this -- flag }
+
+ 1 bit# lshift
+ ;
+end-class
+
diff --git a/softwords/ifbrack.fr b/softwords/ifbrack.fr
new file mode 100644
index 000000000000..af276b8e0947
--- /dev/null
+++ b/softwords/ifbrack.fr
@@ -0,0 +1,48 @@
+\ ** ficl/softwords/ifbrack.fr
+\ ** ANS conditional compile directives [if] [else] [then]
+\ ** Requires ficl 2.0 or greater...
+
+hide
+
+: ?[if] ( c-addr u -- c-addr u flag )
+ 2dup s" [if]" compare-insensitive 0=
+;
+
+: ?[else] ( c-addr u -- c-addr u flag )
+ 2dup s" [else]" compare-insensitive 0=
+;
+
+: ?[then] ( c-addr u -- c-addr u flag )
+ 2dup s" [then]" compare-insensitive 0= >r
+ 2dup s" [endif]" compare-insensitive 0= r>
+ or
+;
+
+set-current
+
+: [else] ( -- )
+ 1 \ ( level )
+ begin
+ begin
+ parse-word dup while \ ( level addr len )
+ ?[if] if \ ( level addr len )
+ 2drop 1+ \ ( level )
+ else \ ( level addr len )
+ ?[else] if \ ( level addr len )
+ 2drop 1- dup if 1+ endif
+ else
+ ?[then] if 2drop 1- else 2drop endif
+ endif
+ endif ?dup 0= if exit endif \ level
+ repeat 2drop \ level
+ refill 0= until \ level
+ drop
+; immediate
+
+: [if] ( flag -- )
+0= if postpone [else] then ; immediate
+
+: [then] ( -- ) ; immediate
+: [endif] ( -- ) ; immediate
+
+previous
diff --git a/softwords/jhlocal.fr b/softwords/jhlocal.fr
new file mode 100644
index 000000000000..a6e946a36462
--- /dev/null
+++ b/softwords/jhlocal.fr
@@ -0,0 +1,103 @@
+\ #if FICL_WANT_LOCALS
+\ ** ficl/softwords/jhlocal.fr
+\ ** stack comment style local syntax...
+\ { a b c | cleared -- d e }
+\ variables before the "|" are initialized in reverse order
+\ from the stack. Those after the "|" are zero initialized.
+\ Anything between "--" and "}" is treated as comment
+\ Uses locals...
+\ locstate: 0 = looking for | or -- or }}
+\ 1 = found |
+\ 2 = found --
+\ 3 = found }
+\ 4 = end of line
+\
+\ revised 2 June 2000 - { | a -- } now works correctly
+.( loading Johns-Hopkins locals ) cr
+hide
+
+0 constant zero
+
+
+: ?-- ( c-addr u -- c-addr u flag )
+ 2dup s" --" compare 0= ;
+: ?} ( c-addr u -- c-addr u flag )
+ 2dup s" }" compare 0= ;
+: ?| ( c-addr u -- c-addr u flag )
+ 2dup s" |" compare 0= ;
+
+\ examine name - if it's a 2local (starts with "2:"),
+\ nibble the prefix (the "2:") off the name and push true.
+\ Otherwise push false
+\ Problem if the local is named "2:" - we fall off the end...
+: ?2loc ( c-addr u -- c-addr u flag )
+ over dup c@ [char] 2 =
+ swap 1+ c@ [char] : = and
+ if
+ 2 - swap char+ char+ swap \ dcs/jws: nibble the '2:'
+ true
+ else
+ false
+ endif
+;
+
+: ?delim ( c-addr u -- state | c-addr u 0 )
+ ?| if 2drop 1 exit endif
+ ?-- if 2drop 2 exit endif
+ ?} if 2drop 3 exit endif
+ dup 0=
+ if 2drop 4 exit endif
+ 0
+;
+
+set-current
+
+: {
+ 0 dup locals| locstate |
+
+ \ stack locals until we hit a delimiter
+ begin
+ parse-word \ ( nLocals c-addr u )
+ ?delim dup to locstate
+ 0= while
+ rot 1+ \ ( c-addr u ... c-addr u nLocals )
+ repeat
+
+ \ now unstack the locals
+ 0 ?do
+ ?2loc if (2local) else (local) endif
+ loop \ ( )
+
+ \ zero locals until -- or }
+ locstate 1 = if
+ begin
+ parse-word
+ ?delim dup to locstate
+ 0= while
+ ?2loc if
+ postpone zero postpone zero (2local)
+ else
+ postpone zero (local)
+ endif
+ repeat
+ endif
+
+ 0 0 (local)
+
+ \ toss words until }
+ \ (explicitly allow | and -- in the comment)
+ locstate 2 = if
+ begin
+ parse-word
+ ?delim dup to locstate
+ 3 < while
+ locstate 0= if 2drop endif
+ repeat
+ endif
+
+ locstate 3 <> abort" syntax error in { } local line"
+; immediate compile-only
+
+previous
+\ #endif
+
diff --git a/softwords/makefile b/softwords/makefile
new file mode 100644
index 000000000000..55edd857cb7b
--- /dev/null
+++ b/softwords/makefile
@@ -0,0 +1,9 @@
+SOURCES = softcore.fr jhlocal.fr marker.fr prefix.fr ifbrack.fr oo.fr classes.fr string.fr fileaccess.fr
+
+softcore.c: softcore.pl $(SOURCES)
+ ./softcore.pl $(SOURCES) >softcore.c
+ cp softcore.c ..
+
+clean:
+ rm *.c
+
diff --git a/softwords/marker.fr b/softwords/marker.fr
new file mode 100644
index 000000000000..0f2ee5eaf493
--- /dev/null
+++ b/softwords/marker.fr
@@ -0,0 +1,25 @@
+\ ** ficl/softwords/marker.fr
+\ ** Ficl implementation of CORE EXT MARKER
+\ John Sadler, 4 Oct 98
+\ Requires ficl 2.02 FORGET-WID !!
+.( loading MARKER ) cr
+: marker ( "name" -- )
+ create
+ get-current ,
+ get-order dup ,
+ 0 ?do , loop
+ does>
+ 0 set-order \ clear search order
+ dup body> >name drop
+ here - allot \ reset HERE to my xt-addr
+ dup @ ( pfa current-wid )
+ dup set-current forget-wid ( pfa )
+ cell+ dup @ swap ( count count-addr )
+ over cells + swap ( last-wid-addr count )
+ 0 ?do
+ dup @ dup ( wid-addr wid wid )
+ >search forget-wid ( wid-addr )
+ cell-
+ loop
+ drop
+;
diff --git a/softwords/oo.fr b/softwords/oo.fr
new file mode 100644
index 000000000000..31ab7e3d816d
--- /dev/null
+++ b/softwords/oo.fr
@@ -0,0 +1,693 @@
+\ #if FICL_WANT_OOP
+\ ** ficl/softwords/oo.fr
+\ ** F I C L O - O E X T E N S I O N S
+\ ** john sadler aug 1998
+
+.( loading ficl O-O extensions ) cr
+17 ficl-vocabulary oop
+also oop definitions
+
+\ Design goals:
+\ 0. Traditional OOP: late binding by default for safety.
+\ Early binding if you ask for it.
+\ 1. Single inheritance
+\ 2. Object aggregation (has-a relationship)
+\ 3. Support objects in the dictionary and as proxies for
+\ existing structures (by reference):
+\ *** A ficl object can wrap a C struct ***
+\ 4. Separate name-spaces for methods - methods are
+\ only visible in the context of a class / object
+\ 5. Methods can be overridden, and subclasses can add methods.
+\ No limit on number of methods.
+
+\ General info:
+\ Classes are objects, too: all classes are instances of METACLASS
+\ All classes are derived (by convention) from OBJECT. This
+\ base class provides a default initializer and superclass
+\ access method
+
+\ A ficl object binds instance storage (payload) to a class.
+\ object ( -- instance class )
+\ All objects push their payload address and class address when
+\ executed.
+
+\ A ficl class consists of a parent class pointer, a wordlist
+\ ID for the methods of the class, and a size for the payload
+\ of objects created by the class. A class is an object.
+\ The NEW method creates and initializes an instance of a class.
+\ Classes have this footprint:
+\ cell 0: parent class address
+\ cell 1: wordlist ID
+\ cell 2: size of instance's payload
+
+\ Methods expect an object couple ( instance class )
+\ on the stack. This is by convention - ficl has no way to
+\ police your code to make sure this is always done, but it
+\ happens naturally if you use the facilities presented here.
+\
+\ Overridden methods must maintain the same stack signature as
+\ their predecessors. Ficl has no way of enforcing this, either.
+\
+\ Revised Apr 2001 - Added Guy Carver's vtable extensions. Class now
+\ has an extra field for the vtable method count. Hasvtable declares
+\ refs to vtable classes
+\
+\ Revised Nov 2001 - metaclass debug method now finds only metaclass methods
+\
+\ Planned: Ficl vtable support
+\ Each class has a vtable size parameter
+\ END-CLASS allocates and clears the vtable - then it walks class's method
+\ list and inserts all new methods into table. For each method, if the table
+\ slot is already nonzero, do nothing (overridden method). Otherwise fill
+\ vtable slot. Now do same check for parent class vtable, filling only
+\ empty slots in the new vtable.
+\ Methods are now structured as follows:
+\ - header
+\ - vtable index
+\ - xt
+\ :noname definition for code
+\
+\ : is redefined to check for override, fill in vtable index, increment method
+\ count if not an override, create header and fill in index. Allot code pointer
+\ and run :noname
+\ ; is overridden to fill in xt returned by :noname
+\ --> compiles code to fetch vtable address, offset by index, and execute
+\ => looks up xt in the vtable and compiles it directly
+
+
+
+user current-class
+0 current-class !
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** L A T E B I N D I N G
+\ Compile the method name, and code to find and
+\ execute it at run-time...
+\
+
+\ p a r s e - m e t h o d
+\ compiles a method name so that it pushes
+\ the string base address and count at run-time.
+
+: parse-method \ name run: ( -- c-addr u )
+ parse-word
+ postpone sliteral
+; compile-only
+
+
+
+: (lookup-method) { class 2:name -- class 0 | class xt 1 | class xt -1 }
+ class name class cell+ @ ( class c-addr u wid )
+ search-wordlist
+;
+
+\ l o o k u p - m e t h o d
+\ takes a counted string method name from the stack (as compiled
+\ by parse-method) and attempts to look this method up in the method list of
+\ the class that's on the stack. If successful, it leaves the class on the stack
+\ and pushes the xt of the method. If not, it aborts with an error message.
+
+: lookup-method { class 2:name -- class xt }
+ class name (lookup-method) ( 0 | xt 1 | xt -1 )
+ 0= if
+ name type ." not found in "
+ class body> >name type
+ cr abort
+ endif
+;
+
+: find-method-xt \ name ( class -- class xt )
+ parse-word lookup-method
+;
+
+: catch-method ( instance class c-addr u -- <method-signature> exc-flag )
+ lookup-method catch
+;
+
+: exec-method ( instance class c-addr u -- <method-signature> )
+ lookup-method execute
+;
+
+\ Method lookup operator takes a class-addr and instance-addr
+\ and executes the method from the class's wordlist if
+\ interpreting. If compiling, bind late.
+\
+: --> ( instance class -- ??? )
+ state @ 0= if
+ find-method-xt execute
+ else
+ parse-method postpone exec-method
+ endif
+; immediate
+
+\ Method lookup with CATCH in case of exceptions
+: c-> ( instance class -- ?? exc-flag )
+ state @ 0= if
+ find-method-xt catch
+ else
+ parse-method postpone catch-method
+ endif
+; immediate
+
+\ METHOD makes global words that do method invocations by late binding
+\ in case you prefer this style (no --> in your code)
+\ Example: everything has next and prev for array access, so...
+\ method next
+\ method prev
+\ my-instance next ( does whatever next does to my-instance by late binding )
+
+: method create does> body> >name lookup-method execute ;
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** E A R L Y B I N D I N G
+\ Early binding operator compiles code to execute a method
+\ given its class at compile time. Classes are immediate,
+\ so they leave their cell-pair on the stack when compiling.
+\ Example:
+\ : get-wid metaclass => .wid @ ;
+\ Usage
+\ my-class get-wid ( -- wid-of-my-class )
+\
+1 ficl-named-wordlist instance-vars
+instance-vars dup >search ficl-set-current
+
+: => \ c:( class meta -- ) run: ( -- ??? ) invokes compiled method
+ drop find-method-xt compile, drop
+; immediate compile-only
+
+: my=> \ c:( -- ) run: ( -- ??? ) late bind compiled method of current-class
+ current-class @ dup postpone =>
+; immediate compile-only
+
+\ Problem: my=[ assumes that each method except the last is am obj: member
+\ which contains its class as the first field of its parameter area. The code
+\ detects non-obect members and assumes the class does not change in this case.
+\ This handles methods like index, prev, and next correctly, but does not deal
+\ correctly with CLASS.
+: my=[ \ same as my=> , but binds a chain of methods
+ current-class @
+ begin
+ parse-word 2dup ( class c-addr u c-addr u )
+ s" ]" compare while ( class c-addr u )
+ lookup-method ( class xt )
+ dup compile, ( class xt )
+ dup ?object if \ If object member, get new class. Otherwise assume same class
+ nip >body cell+ @ ( new-class )
+ else
+ drop ( class )
+ endif
+ repeat 2drop drop
+; immediate compile-only
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** I N S T A N C E V A R I A B L E S
+\ Instance variables (IV) are represented by words in the class's
+\ private wordlist. Each IV word contains the offset
+\ of the IV it represents, and runs code to add that offset
+\ to the base address of an instance when executed.
+\ The metaclass SUB method, defined below, leaves the address
+\ of the new class's offset field and its initial size on the
+\ stack for these words to update. When a class definition is
+\ complete, END-CLASS saves the final size in the class's size
+\ field, and restores the search order and compile wordlist to
+\ prior state. Note that these words are hidden in their own
+\ wordlist to prevent accidental use outside a SUB END-CLASS pair.
+\
+: do-instance-var
+ does> ( instance class addr[offset] -- addr[field] )
+ nip @ +
+;
+
+: addr-units: ( offset size "name" -- offset' )
+ create over , +
+ do-instance-var
+;
+
+: chars: \ ( offset nCells "name" -- offset' ) Create n char member.
+ chars addr-units: ;
+
+: char: \ ( offset nCells "name" -- offset' ) Create 1 char member.
+ 1 chars: ;
+
+: cells: ( offset nCells "name" -- offset' )
+ cells >r aligned r> addr-units:
+;
+
+: cell: ( offset nCells "name" -- offset' )
+ 1 cells: ;
+
+\ Aggregate an object into the class...
+\ Needs the class of the instance to create
+\ Example: object obj: m_obj
+\
+: do-aggregate
+ objectify
+ does> ( instance class pfa -- a-instance a-class )
+ 2@ ( inst class a-class a-offset )
+ 2swap drop ( a-class a-offset inst )
+ + swap ( a-inst a-class )
+;
+
+: obj: { offset class meta -- offset' } \ "name"
+ create offset , class ,
+ class meta --> get-size offset +
+ do-aggregate
+;
+
+\ Aggregate an array of objects into a class
+\ Usage example:
+\ 3 my-class array: my-array
+\ Makes an instance variable array of 3 instances of my-class
+\ named my-array.
+\
+: array: ( offset n class meta "name" -- offset' )
+ locals| meta class nobjs offset |
+ create offset , class ,
+ class meta --> get-size nobjs * offset +
+ do-aggregate
+;
+
+\ Aggregate a pointer to an object: REF is a member variable
+\ whose class is set at compile time. This is useful for wrapping
+\ data structures in C, where there is only a pointer and the type
+\ it refers to is known. If you want polymorphism, see c_ref
+\ in classes.fr. REF is only useful for pre-initialized structures,
+\ since there's no supported way to set one.
+: ref: ( offset class meta "name" -- offset' )
+ locals| meta class offset |
+ create offset , class ,
+ offset cell+
+ does> ( inst class pfa -- ptr-inst ptr-class )
+ 2@ ( inst class ptr-class ptr-offset )
+ 2swap drop + @ swap
+;
+
+\ #if FICL_WANT_VCALL
+\ vcall extensions contributed by Guy Carver
+: vcall: ( paramcnt "name" -- )
+ current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
+ create , , \ ( paramcnt index -- )
+ does> \ ( inst class pfa -- ptr-inst ptr-class )
+ nip 2@ vcall \ ( params offset inst class offset -- )
+;
+
+: vcallr: 0x80000000 or vcall: ; \ Call with return address desired.
+
+\ #if FICL_WANT_FLOAT
+: vcallf: \ ( paramcnt -<name>- f: r )
+ 0x80000000 or
+ current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
+ create , , \ ( paramcnt index -- )
+ does> \ ( inst class pfa -- ptr-inst ptr-class )
+ nip 2@ vcall f> \ ( params offset inst class offset -- f: r )
+;
+\ #endif /* FLOAT */
+\ #endif /* VCALL */
+
+\ END-CLASS terminates construction of a class by storing
+\ the size of its instance variables in the class's size field
+\ ( -- old-wid addr[size] 0 )
+\
+: end-class ( old-wid addr[size] size -- )
+ swap ! set-current
+ search> drop \ pop struct builder wordlist
+;
+
+\ See resume-class (a metaclass method) below for usage
+\ This is equivalent to end-class for now, but that will change
+\ when we support vtable bindings.
+: suspend-class ( old-wid addr[size] size -- ) end-class ;
+
+set-current previous
+\ E N D I N S T A N C E V A R I A B L E S
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ D O - D O - I N S T A N C E
+\ Makes a class method that contains the code for an
+\ instance of the class. This word gets compiled into
+\ the wordlist of every class by the SUB method.
+\ PRECONDITION: current-class contains the class address
+\ why use a state variable instead of the stack?
+\ >> Stack state is not well-defined during compilation (there are
+\ >> control structure match codes on the stack, of undefined size
+\ >> easiest way around this is use of this thread-local variable
+\
+: do-do-instance ( -- )
+ s" : .do-instance does> [ current-class @ ] literal ;"
+ evaluate
+;
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** M E T A C L A S S
+\ Every class is an instance of metaclass. This lets
+\ classes have methods that are different from those
+\ of their instances.
+\ Classes are IMMEDIATE to make early binding simpler
+\ See above...
+\
+:noname
+ wordlist
+ create
+ immediate
+ 0 , \ NULL parent class
+ dup , \ wid
+\ #if FICL_WANT_VCALL
+ 4 cells , \ instance size
+\ #else
+ 3 cells , \ instance size
+\ #endif
+ ficl-set-current
+ does> dup
+; execute metaclass
+\ now brand OBJECT's wordlist (so that ORDER can display it by name)
+metaclass drop cell+ @ brand-wordlist
+
+metaclass drop current-class !
+do-do-instance
+
+\
+\ C L A S S M E T H O D S
+\
+instance-vars >search
+
+create .super ( class metaclass -- parent-class )
+ 0 cells , do-instance-var
+
+create .wid ( class metaclass -- wid ) \ return wid of class
+ 1 cells , do-instance-var
+
+\ #if FICL_WANT_VCALL
+create .vtCount \ Number of VTABLE methods, if any
+ 2 cells , do-instance-var
+
+create .size ( class metaclass -- size ) \ return class's payload size
+ 3 cells , do-instance-var
+\ #else
+create .size ( class metaclass -- size ) \ return class's payload size
+ 2 cells , do-instance-var
+\ #endif
+
+: get-size metaclass => .size @ ;
+: get-wid metaclass => .wid @ ;
+: get-super metaclass => .super @ ;
+\ #if FICL_WANT_VCALL
+: get-vtCount metaclass => .vtCount @ ;
+: get-vtAdd metaclass => .vtCount ;
+\ #endif
+
+\ create an uninitialized instance of a class, leaving
+\ the address of the new instance and its class
+\
+: instance ( class metaclass "name" -- instance class )
+ locals| meta parent |
+ create
+ here parent --> .do-instance \ ( inst class )
+ parent meta metaclass => get-size
+ allot \ allocate payload space
+;
+
+\ create an uninitialized array
+: array ( n class metaclass "name" -- n instance class )
+ locals| meta parent nobj |
+ create nobj
+ here parent --> .do-instance \ ( nobj inst class )
+ parent meta metaclass => get-size
+ nobj * allot \ allocate payload space
+;
+
+\ create an initialized instance
+\
+: new \ ( class metaclass "name" -- )
+ metaclass => instance --> init
+;
+
+\ create an initialized array of instances
+: new-array ( n class metaclass "name" -- )
+ metaclass => array
+ --> array-init
+;
+
+\ Create an anonymous initialized instance from the heap
+: alloc \ ( class metaclass -- instance class )
+ locals| meta class |
+ class meta metaclass => get-size allocate ( -- addr fail-flag )
+ abort" allocate failed " ( -- addr )
+ class 2dup --> init
+;
+
+\ Create an anonymous array of initialized instances from the heap
+: alloc-array \ ( n class metaclass -- instance class )
+ locals| meta class nobj |
+ class meta metaclass => get-size
+ nobj * allocate ( -- addr fail-flag )
+ abort" allocate failed " ( -- addr )
+ nobj over class --> array-init
+ class
+;
+
+\ Create an anonymous initialized instance from the dictionary
+: allot { 2:this -- 2:instance }
+ here ( instance-address )
+ this my=> get-size allot
+ this drop 2dup --> init
+;
+
+\ Create an anonymous array of initialized instances from the dictionary
+: allot-array { nobj 2:this -- 2:instance }
+ here ( instance-address )
+ this my=> get-size nobj * allot
+ this drop 2dup ( 2instance 2instance )
+ nobj -rot --> array-init
+;
+
+\ create a proxy object with initialized payload address given
+: ref ( instance-addr class metaclass "name" -- )
+ drop create , ,
+ does> 2@
+;
+
+\ suspend-class and resume-class help to build mutually referent classes.
+\ Example:
+\ object subclass c-akbar
+\ suspend-class ( put akbar on hold while we define jeff )
+\ object subclass c-jeff
+\ c-akbar ref: .akbar
+\ ( and whatever else comprises this class )
+\ end-class ( done with c-jeff )
+\ c-akbar --> resume-class
+\ c-jeff ref: .jeff
+\ ( and whatever else goes in c-akbar )
+\ end-class ( done with c-akbar )
+\
+: resume-class { 2:this -- old-wid addr[size] size }
+ this --> .wid @ ficl-set-current ( old-wid )
+ this --> .size dup @ ( old-wid addr[size] size )
+ instance-vars >search
+;
+
+\ create a subclass
+\ This method leaves the stack and search order ready for instance variable
+\ building. Pushes the instance-vars wordlist onto the search order,
+\ and sets the compilation wordlist to be the private wordlist of the
+\ new class. The class's wordlist is deliberately NOT in the search order -
+\ to prevent methods from getting used with wrong data.
+\ Postcondition: leaves the address of the new class in current-class
+: sub ( class metaclass "name" -- old-wid addr[size] size )
+ wordlist
+ locals| wid meta parent |
+ parent meta metaclass => get-wid
+ wid wid-set-super \ set superclass
+ create immediate \ get the subclass name
+ wid brand-wordlist \ label the subclass wordlist
+ here current-class ! \ prep for do-do-instance
+ parent , \ save parent class
+ wid , \ save wid
+\ #if FICL_WANT_VCALL
+ parent meta --> get-vtCount ,
+\ #endif
+ here parent meta --> get-size dup , ( addr[size] size )
+ metaclass => .do-instance
+ wid ficl-set-current -rot
+ do-do-instance
+ instance-vars >search \ push struct builder wordlist
+;
+
+\ OFFSET-OF returns the offset of an instance variable
+\ from the instance base address. If the next token is not
+\ the name of in instance variable method, you get garbage
+\ results -- there is no way at present to check for this error.
+: offset-of ( class metaclass "name" -- offset )
+ drop find-method-xt nip >body @ ;
+
+\ ID returns the string name cell-pair of its class
+: id ( class metaclass -- c-addr u )
+ drop body> >name ;
+
+\ list methods of the class
+: methods \ ( class meta -- )
+ locals| meta class |
+ begin
+ class body> >name type ." methods:" cr
+ class meta --> get-wid >search words cr previous
+ class meta metaclass => get-super
+ dup to class
+ 0= until cr
+;
+
+\ list class's ancestors
+: pedigree ( class meta -- )
+ locals| meta class |
+ begin
+ class body> >name type space
+ class meta metaclass => get-super
+ dup to class
+ 0= until cr
+;
+
+\ decompile an instance method
+: see ( class meta -- )
+ metaclass => get-wid >search see previous ;
+
+\ debug a method of metaclass
+\ Eg: my-class --> debug my-method
+: debug ( class meta -- )
+ find-method-xt debug-xt ;
+
+previous set-current
+\ E N D M E T A C L A S S
+
+\ ** META is a nickname for the address of METACLASS...
+metaclass drop
+constant meta
+
+\ ** SUBCLASS is a nickname for a class's SUB method...
+\ Subclass compilation ends when you invoke end-class
+\ This method is late bound for safety...
+: subclass --> sub ;
+
+\ #if FICL_WANT_VCALL
+\ VTABLE Support extensions (Guy Carver)
+\ object --> sub mine hasvtable
+: hasvtable 4 + ; immediate
+\ #endif
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** O B J E C T
+\ Root of all classes
+:noname
+ wordlist
+ create immediate
+ 0 , \ NULL parent class
+ dup , \ wid
+ 0 , \ instance size
+ ficl-set-current
+ does> meta
+; execute object
+\ now brand OBJECT's wordlist (so that ORDER can display it by name)
+object drop cell+ @ brand-wordlist
+
+object drop current-class !
+do-do-instance
+instance-vars >search
+
+\ O B J E C T M E T H O D S
+\ Convert instance cell-pair to class cell-pair
+\ Useful for binding class methods from an instance
+: class ( instance class -- class metaclass )
+ nip meta ;
+
+\ default INIT method zero fills an instance
+: init ( instance class -- )
+ meta
+ metaclass => get-size ( inst size )
+ erase ;
+
+\ Apply INIT to an array of NOBJ objects...
+\
+: array-init ( nobj inst class -- )
+ 0 dup locals| &init &next class inst |
+ \
+ \ bind methods outside the loop to save time
+ \
+ class s" init" lookup-method to &init
+ s" next" lookup-method to &next
+ drop
+ 0 ?do
+ inst class 2dup
+ &init execute
+ &next execute drop to inst
+ loop
+;
+
+\ free storage allocated to a heap instance by alloc or alloc-array
+\ NOTE: not protected against errors like FREEing something that's
+\ really in the dictionary.
+: free \ ( instance class -- )
+ drop free
+ abort" free failed "
+;
+
+\ Instance aliases for common class methods
+\ Upcast to parent class
+: super ( instance class -- instance parent-class )
+ meta metaclass => get-super ;
+
+: pedigree ( instance class -- )
+ object => class
+ metaclass => pedigree ;
+
+: size ( instance class -- sizeof-instance )
+ object => class
+ metaclass => get-size ;
+
+: methods ( instance class -- )
+ object => class
+ metaclass => methods ;
+
+\ Array indexing methods...
+\ Usage examples:
+\ 10 object-array --> index
+\ obj --> next
+\
+: index ( n instance class -- instance[n] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size * ( n*size )
+ inst + class ;
+
+: next ( instance[n] class -- instance[n+1] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size
+ inst +
+ class ;
+
+: prev ( instance[n] class -- instance[n-1] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size
+ inst swap -
+ class ;
+
+: debug ( 2this -- ?? )
+ find-method-xt debug-xt ;
+
+previous set-current
+\ E N D O B J E C T
+
+\ reset to default search order
+only definitions
+
+\ redefine oop in default search order to put OOP words in the search order and make them
+\ the compiling wordlist...
+
+: oo only also oop definitions ;
+
+\ #endif
diff --git a/softwords/oo.fr.bak b/softwords/oo.fr.bak
new file mode 100644
index 000000000000..afe8edb38b80
--- /dev/null
+++ b/softwords/oo.fr.bak
@@ -0,0 +1,678 @@
+\ #if FICL_WANT_OOP
+\ ** ficl/softwords/oo.fr
+\ ** F I C L O - O E X T E N S I O N S
+\ ** john sadler aug 1998
+
+.( loading ficl O-O extensions ) cr
+17 ficl-vocabulary oop
+also oop definitions
+
+\ Design goals:
+\ 0. Traditional OOP: late binding by default for safety.
+\ Early binding if you ask for it.
+\ 1. Single inheritance
+\ 2. Object aggregation (has-a relationship)
+\ 3. Support objects in the dictionary and as proxies for
+\ existing structures (by reference):
+\ *** A ficl object can wrap a C struct ***
+\ 4. Separate name-spaces for methods - methods are
+\ only visible in the context of a class / object
+\ 5. Methods can be overridden, and subclasses can add methods.
+\ No limit on number of methods.
+
+\ General info:
+\ Classes are objects, too: all classes are instances of METACLASS
+\ All classes are derived (by convention) from OBJECT. This
+\ base class provides a default initializer and superclass
+\ access method
+
+\ A ficl object binds instance storage (payload) to a class.
+\ object ( -- instance class )
+\ All objects push their payload address and class address when
+\ executed.
+
+\ A ficl class consists of a parent class pointer, a wordlist
+\ ID for the methods of the class, and a size for the payload
+\ of objects created by the class. A class is an object.
+\ The NEW method creates and initializes an instance of a class.
+\ Classes have this footprint:
+\ cell 0: parent class address
+\ cell 1: wordlist ID
+\ cell 2: size of instance's payload
+
+\ Methods expect an object couple ( instance class )
+\ on the stack. This is by convention - ficl has no way to
+\ police your code to make sure this is always done, but it
+\ happens naturally if you use the facilities presented here.
+\
+\ Overridden methods must maintain the same stack signature as
+\ their predecessors. Ficl has no way of enforcing this, either.
+\
+\ Revised Apr 2001 - Added Guy Carver's vtable extensions. Class now
+\ has an extra field for the vtable method count. Hasvtable declares
+\ refs to vtable classes
+\
+\ Revised August 2001 - Ficl vtable support
+\ Each class has a vtable size parameter
+\ END-CLASS allocates and clears the vtable - then it walks class's method
+\ list and inserts all new methods into table. For each method, if the table
+\ slot is already nonzero, do nothing (overridden method). Otherwise fill
+\ vtable slot. Now do same check for parent class vtable, filling only
+\ empty slots in the new vtable.
+\ Methods are now structured as follows:
+\ - header
+\ - vtable index
+\ - xt
+\ :noname definition for code
+\
+\ : is redefined to check for override, fill in vtable index, increment method
+\ count if not an override, create header and fill in index. Allot code pointer
+\ and run :noname
+\ ; is overridden to fill in xt returned by :noname
+\ --> compiles code to fetch vtable address, offset by index, and execute
+\ => looks up xt in the vtable and compiles it directly
+
+
+
+user current-class
+0 current-class !
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** L A T E B I N D I N G
+\ Compile the method name, and code to find and
+\ execute it at run-time...
+\
+
+hide
+
+\ p a r s e - m e t h o d
+\ compiles a method name so that it pushes
+\ the string base address and count at run-time.
+
+: parse-method \ name run: ( -- c-addr u )
+ parse-word
+ postpone sliteral
+; compile-only
+
+\ l o o k u p - m e t h o d
+\ takes a counted string method name from the stack (as compiled
+\ by parse-method) and attempts to look this method up in the method list of
+\ the class that's on the stack. If successful, it leaves the class on the stack
+\ and pushes the xt of the method. If not, it aborts with an error message.
+
+: lookup-method { class 2:name -- class xt }
+ name class cell+ @ ( c-addr u wid )
+ search-wordlist ( 0 | xt 1 | xt -1 )
+ 0= if
+ name type ." not found in "
+ class body> >name type
+ cr abort
+ endif
+ class swap
+;
+
+: find-method-xt \ name ( class -- class xt )
+ parse-word lookup-method
+;
+
+set-current ( stop hiding definitions )
+
+: catch-method ( instance class c-addr u -- <method-signature> exc-flag )
+ lookup-method catch
+;
+
+: exec-method ( instance class c-addr u -- <method-signature> )
+ lookup-method execute
+;
+
+\ Method lookup operator takes a class-addr and instance-addr
+\ and executes the method from the class's wordlist if
+\ interpreting. If compiling, bind late.
+\
+: --> ( instance class -- ??? )
+ state @ 0= if
+ find-method-xt execute
+ else
+ parse-method postpone exec-method
+ endif
+; immediate
+
+\ Method lookup with CATCH in case of exceptions
+: c-> ( instance class -- ?? exc-flag )
+ state @ 0= if
+ find-method-xt catch
+ else
+ parse-method postpone catch-method
+ endif
+; immediate
+
+\ METHOD makes global words that do method invocations by late binding
+\ in case you prefer this style (no --> in your code)
+\ Example: everything has next and prev for array access, so...
+\ method next
+\ method prev
+\ my-instance next ( does whatever next does to my-instance by late binding )
+
+: method create does> body> >name lookup-method execute ;
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** E A R L Y B I N D I N G
+\ Early binding operator compiles code to execute a method
+\ given its class at compile time. Classes are immediate,
+\ so they leave their cell-pair on the stack when compiling.
+\ Example:
+\ : get-wid metaclass => .wid @ ;
+\ Usage
+\ my-class get-wid ( -- wid-of-my-class )
+\
+1 ficl-named-wordlist instance-vars
+instance-vars dup >search ficl-set-current
+
+: => \ c:( class meta -- ) run: ( -- ??? ) invokes compiled method
+ drop find-method-xt compile, drop
+; immediate compile-only
+
+: my=> \ c:( -- ) run: ( -- ??? ) late bind compiled method of current-class
+ current-class @ dup postpone =>
+; immediate compile-only
+
+: my=[ \ same as my=> , but binds a chain of methods
+ current-class @
+ begin
+ parse-word 2dup
+ s" ]" compare while ( class c-addr u )
+ lookup-method nip dup ( xt xt )
+ compile, >body cell+ @ ( class' )
+ repeat 2drop drop
+; immediate compile-only
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** I N S T A N C E V A R I A B L E S
+\ Instance variables (IV) are represented by words in the class's
+\ private wordlist. Each IV word contains the offset
+\ of the IV it represents, and runs code to add that offset
+\ to the base address of an instance when executed.
+\ The metaclass SUB method, defined below, leaves the address
+\ of the new class's offset field and its initial size on the
+\ stack for these words to update. When a class definition is
+\ complete, END-CLASS saves the final size in the class's size
+\ field, and restores the search order and compile wordlist to
+\ prior state. Note that these words are hidden in their own
+\ wordlist to prevent accidental use outside a SUB END-CLASS pair.
+\
+: do-instance-var
+ does> ( instance class addr[offset] -- addr[field] )
+ nip @ +
+;
+
+: addr-units: ( offset size "name" -- offset' )
+ create over , +
+ do-instance-var
+;
+
+: chars: \ ( offset nCells "name" -- offset' ) Create n char member.
+ chars addr-units: ;
+
+: char: \ ( offset nCells "name" -- offset' ) Create 1 char member.
+ 1 chars: ;
+
+: cells: ( offset nCells "name" -- offset' )
+ cells >r aligned r> addr-units:
+;
+
+: cell: ( offset nCells "name" -- offset' )
+ 1 cells: ;
+
+\ Aggregate an object into the class...
+\ Needs the class of the instance to create
+\ Example: object obj: m_obj
+\
+: do-aggregate
+ does> ( instance class pfa -- a-instance a-class )
+ 2@ ( inst class a-class a-offset )
+ 2swap drop ( a-class a-offset inst )
+ + swap ( a-inst a-class )
+;
+
+: obj: ( offset class meta "name" -- offset' )
+ locals| meta class offset |
+ create offset , class ,
+ class meta --> get-size offset +
+ do-aggregate
+;
+
+\ Aggregate an array of objects into a class
+\ Usage example:
+\ 3 my-class array: my-array
+\ Makes an instance variable array of 3 instances of my-class
+\ named my-array.
+\
+: array: ( offset n class meta "name" -- offset' )
+ locals| meta class nobjs offset |
+ create offset , class ,
+ class meta --> get-size nobjs * offset +
+ do-aggregate
+;
+
+\ Aggregate a pointer to an object: REF is a member variable
+\ whose class is set at compile time. This is useful for wrapping
+\ data structures in C, where there is only a pointer and the type
+\ it refers to is known. If you want polymorphism, see c_ref
+\ in classes.fr. REF is only useful for pre-initialized structures,
+\ since there's no supported way to set one.
+: ref: ( offset class meta "name" -- offset' )
+ locals| meta class offset |
+ create offset , class ,
+ offset cell+
+ does> ( inst class pfa -- ptr-inst ptr-class )
+ 2@ ( inst class ptr-class ptr-offset )
+ 2swap drop + @ swap
+;
+
+\ #if FICL_WANT_VCALL
+\ vcall extensions contributed by Guy Carver
+: vcall: ( paramcnt "name" -- )
+ current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
+ create , , \ ( paramcnt index -- )
+ does> \ ( inst class pfa -- ptr-inst ptr-class )
+ nip 2@ vcall \ ( params offset inst class offset -- )
+;
+
+: vcallr: 0x80000000 or vcall: ; \ Call with return address desired.
+
+\ #if FICL_WANT_FLOAT
+: vcallf: \ ( paramcnt -<name>- f: r )
+ 0x80000000 or
+ current-class @ 8 + dup @ dup 1+ rot ! \ Kludge fix to get to .vtCount before it's defined.
+ create , , \ ( paramcnt index -- )
+ does> \ ( inst class pfa -- ptr-inst ptr-class )
+ nip 2@ vcall f> \ ( params offset inst class offset -- f: r )
+;
+\ #endif /* FLOAT */
+\ #endif /* VCALL */
+
+\ END-CLASS terminates construction of a class by storing
+\ the size of its instance variables in the class's size field
+\ ( -- old-wid addr[size] 0 )
+\
+: end-class ( old-wid addr[size] size -- )
+ swap ! set-current
+ search> drop \ pop struct builder wordlist
+;
+
+\ See resume-class (a metaclass method) below for usage
+\ This is equivalent to end-class for now, but that will change
+\ when we support vtable bindings.
+: suspend-class ( old-wid addr[size] size -- ) end-class ;
+
+set-current previous
+\ E N D I N S T A N C E V A R I A B L E S
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ D O - D O - I N S T A N C E
+\ Makes a class method that contains the code for an
+\ instance of the class. This word gets compiled into
+\ the wordlist of every class by the SUB method.
+\ PRECONDITION: current-class contains the class address
+\ why use a state variable instead of the stack?
+\ >> Stack state is not well-defined during compilation (there are
+\ >> control structure match codes on the stack, of undefined size
+\ >> easiest way around this is use of this thread-local variable
+\
+: do-do-instance ( -- )
+ s" : .do-instance does> [ current-class @ ] literal ;"
+ evaluate
+;
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** M E T A C L A S S
+\ Every class is an instance of metaclass. This lets
+\ classes have methods that are different from those
+\ of their instances.
+\ Classes are IMMEDIATE to make early binding simpler
+\ See above...
+\
+:noname
+ wordlist
+ create
+ immediate
+ 0 , \ NULL parent class
+ dup , \ wid
+\ #if FICL_WANT_VCALL
+ 4 cells , \ instance size
+\ #else
+ 3 cells , \ instance size
+\ #endif
+ ficl-set-current
+ does> dup
+; execute metaclass
+\ now brand OBJECT's wordlist (so that ORDER can display it by name)
+metaclass drop cell+ @ brand-wordlist
+
+metaclass drop current-class !
+do-do-instance
+
+\
+\ C L A S S M E T H O D S
+\
+instance-vars >search
+
+create .super ( class metaclass -- parent-class )
+ 0 cells , do-instance-var
+
+create .wid ( class metaclass -- wid ) \ return wid of class
+ 1 cells , do-instance-var
+
+\ #if FICL_WANT_VCALL
+create .vtCount \ Number of VTABLE methods, if any
+ 2 cells , do-instance-var
+
+create .size ( class metaclass -- size ) \ return class's payload size
+ 3 cells , do-instance-var
+\ #else
+create .size ( class metaclass -- size ) \ return class's payload size
+ 2 cells , do-instance-var
+\ #endif
+
+: get-size metaclass => .size @ ;
+: get-wid metaclass => .wid @ ;
+: get-super metaclass => .super @ ;
+\ #if FICL_WANT_VCALL
+: get-vtCount metaclass => .vtCount @ ;
+: get-vtAdd metaclass => .vtCount ;
+\ #endif
+
+\ create an uninitialized instance of a class, leaving
+\ the address of the new instance and its class
+\
+: instance ( class metaclass "name" -- instance class )
+ locals| meta parent |
+ create
+ here parent --> .do-instance \ ( inst class )
+ parent meta metaclass => get-size
+ allot \ allocate payload space
+;
+
+\ create an uninitialized array
+: array ( n class metaclass "name" -- n instance class )
+ locals| meta parent nobj |
+ create nobj
+ here parent --> .do-instance \ ( nobj inst class )
+ parent meta metaclass => get-size
+ nobj * allot \ allocate payload space
+;
+
+\ create an initialized instance
+\
+: new \ ( class metaclass "name" -- )
+ metaclass => instance --> init
+;
+
+\ create an initialized array of instances
+: new-array ( n class metaclass "name" -- )
+ metaclass => array
+ --> array-init
+;
+
+\ Create an anonymous initialized instance from the heap
+: alloc \ ( class metaclass -- instance class )
+ locals| meta class |
+ class meta metaclass => get-size allocate ( -- addr fail-flag )
+ abort" allocate failed " ( -- addr )
+ class 2dup --> init
+;
+
+\ Create an anonymous array of initialized instances from the heap
+: alloc-array \ ( n class metaclass -- instance class )
+ locals| meta class nobj |
+ class meta metaclass => get-size
+ nobj * allocate ( -- addr fail-flag )
+ abort" allocate failed " ( -- addr )
+ nobj over class --> array-init
+ class
+;
+
+\ Create an anonymous initialized instance from the dictionary
+: allot { 2:this -- 2:instance }
+ here ( instance-address )
+ this my=> get-size allot
+ this drop 2dup --> init
+;
+
+\ Create an anonymous array of initialized instances from the dictionary
+: allot-array { nobj 2:this -- 2:instance }
+ here ( instance-address )
+ this my=> get-size nobj * allot
+ this drop 2dup ( 2instance 2instance )
+ nobj -rot --> array-init
+;
+
+\ create a proxy object with initialized payload address given
+: ref ( instance-addr class metaclass "name" -- )
+ drop create , ,
+ does> 2@
+;
+
+\ suspend-class and resume-class help to build mutually referent classes.
+\ Example:
+\ object subclass c-akbar
+\ suspend-class ( put akbar on hold while we define jeff )
+\ object subclass c-jeff
+\ c-akbar ref: .akbar
+\ ( and whatever else comprises this class )
+\ end-class ( done with c-jeff )
+\ c-akbar --> resume-class
+\ c-jeff ref: .jeff
+\ ( and whatever else goes in c-akbar )
+\ end-class ( done with c-akbar )
+\
+: resume-class { 2:this -- old-wid addr[size] size }
+ this --> .wid @ ficl-set-current ( old-wid )
+ this --> .size dup @ ( old-wid addr[size] size )
+ instance-vars >search
+;
+
+\ create a subclass
+\ This method leaves the stack and search order ready for instance variable
+\ building. Pushes the instance-vars wordlist onto the search order,
+\ and sets the compilation wordlist to be the private wordlist of the
+\ new class. The class's wordlist is deliberately NOT in the search order -
+\ to prevent methods from getting used with wrong data.
+\ Postcondition: leaves the address of the new class in current-class
+: sub ( class metaclass "name" -- old-wid addr[size] size )
+ wordlist
+ locals| wid meta parent |
+ parent meta metaclass => get-wid
+ wid wid-set-super \ set superclass
+ create immediate \ get the subclass name
+ wid brand-wordlist \ label the subclass wordlist
+ here current-class ! \ prep for do-do-instance
+ parent , \ save parent class
+ wid , \ save wid
+\ #if FICL_WANT_VCALL
+ parent meta --> get-vtCount ,
+\ #endif
+ here parent meta --> get-size dup , ( addr[size] size )
+ metaclass => .do-instance
+ wid ficl-set-current -rot
+ do-do-instance
+ instance-vars >search \ push struct builder wordlist
+;
+
+\ OFFSET-OF returns the offset of an instance variable
+\ from the instance base address. If the next token is not
+\ the name of in instance variable method, you get garbage
+\ results -- there is no way at present to check for this error.
+: offset-of ( class metaclass "name" -- offset )
+ drop find-method-xt nip >body @ ;
+
+\ ID returns the string name cell-pair of its class
+: id ( class metaclass -- c-addr u )
+ drop body> >name ;
+
+\ list methods of the class
+: methods \ ( class meta -- )
+ locals| meta class |
+ begin
+ class body> >name type ." methods:" cr
+ class meta --> get-wid >search words cr previous
+ class meta metaclass => get-super
+ dup to class
+ 0= until cr
+;
+
+\ list class's ancestors
+: pedigree ( class meta -- )
+ locals| meta class |
+ begin
+ class body> >name type space
+ class meta metaclass => get-super
+ dup to class
+ 0= until cr
+;
+
+\ decompile an instance method
+: see ( class meta -- )
+ metaclass => get-wid >search see previous ;
+
+\ debug a method of metaclass
+\ Eg: my-object --> debug my-method
+: debug ( class meta -- )
+ find-method-xt debug-xt ;
+
+previous set-current
+\ E N D M E T A C L A S S
+
+\ ** META is a nickname for the address of METACLASS...
+metaclass drop
+constant meta
+
+\ ** SUBCLASS is a nickname for a class's SUB method...
+\ Subclass compilation ends when you invoke end-class
+\ This method is late bound for safety...
+: subclass --> sub ;
+
+\ #if FICL_WANT_VCALL
+\ VTABLE Support extensions (Guy Carver)
+\ object --> sub mine hasvtable
+: hasvtable 4 + ; immediate
+\ #endif
+
+
+\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
+\ ** O B J E C T
+\ Root of all classes
+:noname
+ wordlist
+ create immediate
+ 0 , \ NULL parent class
+ dup , \ wid
+ 0 , \ instance size
+ ficl-set-current
+ does> meta
+; execute object
+\ now brand OBJECT's wordlist (so that ORDER can display it by name)
+object drop cell+ @ brand-wordlist
+
+object drop current-class !
+do-do-instance
+instance-vars >search
+
+\ O B J E C T M E T H O D S
+\ Convert instance cell-pair to class cell-pair
+\ Useful for binding class methods from an instance
+: class ( instance class -- class metaclass )
+ nip meta ;
+
+\ default INIT method zero fills an instance
+: init ( instance class -- )
+ meta
+ metaclass => get-size ( inst size )
+ erase ;
+
+\ Apply INIT to an array of NOBJ objects...
+\
+: array-init ( nobj inst class -- )
+ 0 dup locals| &init &next class inst |
+ \
+ \ bind methods outside the loop to save time
+ \
+ class s" init" lookup-method to &init
+ s" next" lookup-method to &next
+ drop
+ 0 ?do
+ inst class 2dup
+ &init execute
+ &next execute drop to inst
+ loop
+;
+
+\ free storage allocated to a heap instance by alloc or alloc-array
+\ NOTE: not protected against errors like FREEing something that's
+\ really in the dictionary.
+: free \ ( instance class -- )
+ drop free
+ abort" free failed "
+;
+
+\ Instance aliases for common class methods
+\ Upcast to parent class
+: super ( instance class -- instance parent-class )
+ meta metaclass => get-super ;
+
+: pedigree ( instance class -- )
+ object => class
+ metaclass => pedigree ;
+
+: size ( instance class -- sizeof-instance )
+ object => class
+ metaclass => get-size ;
+
+: methods ( instance class -- )
+ object => class
+ metaclass => methods ;
+
+\ Array indexing methods...
+\ Usage examples:
+\ 10 object-array --> index
+\ obj --> next
+\
+: index ( n instance class -- instance[n] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size * ( n*size )
+ inst + class ;
+
+: next ( instance[n] class -- instance[n+1] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size
+ inst +
+ class ;
+
+: prev ( instance[n] class -- instance[n-1] class )
+ locals| class inst |
+ inst class
+ object => class
+ metaclass => get-size
+ inst swap -
+ class ;
+
+: debug ( 2this -- ?? )
+ find-method-xt debug-xt ;
+
+previous set-current
+\ E N D O B J E C T
+
+\ reset to default search order
+only definitions
+
+\ redefine oop in default search order to put OOP words in the search order and make them
+\ the compiling wordlist...
+: oop only also oop definitions ;
+\ #endif \ No newline at end of file
diff --git a/softwords/prefix.fr b/softwords/prefix.fr
new file mode 100644
index 000000000000..7ccd14f0cb14
--- /dev/null
+++ b/softwords/prefix.fr
@@ -0,0 +1,57 @@
+\ **
+\ ** Prefix words for ficl
+\ ** submitted by Larry Hastings, larry@hastings.org
+\ **
+\ (jws) To make a prefix, simply create a new definition in the <prefixes>
+\ wordlist. start-prefixes and end-prefixes handle the bookkeeping
+
+variable save-current
+
+: start-prefixes get-current save-current ! <prefixes> set-current ;
+: end-prefixes save-current @ set-current ;
+: show-prefixes <prefixes> >search words search> drop ;
+
+\ #if (FICL_EXTENDED_PREFIX)
+
+start-prefixes
+
+\ define " (double-quote) as an alias for s", and make it a prefix
+: " postpone s" ; immediate
+
+
+\ make .( a prefix (we just create an alias for it in the prefixes list)
+: .( postpone .( ; immediate
+
+
+\ make \ a prefix, and add // (same thing) as a prefix too
+\ (jws) "//" is precompiled to save aggravation with Perl
+\ : // postpone \ ; immediate
+
+
+\ ** add 0b, 0o, 0d, and 0x as prefixes
+\ ** these temporarily shift the base to 2, 8, 10, and 16 respectively
+\ ** and consume the next number in the input stream, pushing/compiling
+\ ** as normal
+
+\ (jws) __tempbase is precompiled, as are 0x and 0d - see prefix.c
+\
+\ : __tempbase { newbase | oldbase -- }
+\ base @ to oldbase
+\ newbase base !
+\ 0 0 parse-word >number 2drop drop
+\ oldbase base !
+\ ;
+
+: 0b 2 __tempbase ; immediate
+
+: 0o 8 __tempbase ; immediate
+
+\ : 0d 10 __tempbase ; immediate
+\ "0d" add-prefix
+
+\ : 0x 16 __tempbase ; immediate
+\ "0x" add-prefix
+
+end-prefixes
+
+\ #endif
diff --git a/softwords/softcore.bat b/softwords/softcore.bat
new file mode 100644
index 000000000000..85633280495f
--- /dev/null
+++ b/softwords/softcore.bat
@@ -0,0 +1 @@
+perl softcore.pl softcore.fr jhlocal.fr marker.fr prefix.fr ifbrack.fr oo.fr classes.fr string.fr fileaccess.fr >..\softcore.c
diff --git a/softwords/softcore.fr b/softwords/softcore.fr
new file mode 100644
index 000000000000..14bc065af073
--- /dev/null
+++ b/softwords/softcore.fr
@@ -0,0 +1,207 @@
+\ ** ficl/softwords/softcore.fr
+\ ** FICL soft extensions
+\ ** John Sadler (john_sadler@alum.mit.edu)
+\ ** September, 1998
+
+\ ** Ficl USER variables
+\ ** See words.c for primitive def'n of USER
+.( loading ficl soft extensions ) cr
+\ #if FICL_WANT_USER
+variable nUser 0 nUser !
+: user \ name ( -- )
+ nUser dup @ user 1 swap +! ;
+
+\ #endif
+
+\ ** ficl extras
+\ EMPTY cleans the parameter stack
+: empty ( xn..x1 -- ) depth 0 ?do drop loop ;
+\ CELL- undoes CELL+
+: cell- ( addr -- addr ) [ 1 cells ] literal - ;
+: -rot ( a b c -- c a b ) 2 -roll ;
+
+\ ** CORE
+: abs ( x -- x )
+ dup 0< if negate endif ;
+decimal 32 constant bl
+
+: space ( -- ) bl emit ;
+
+: spaces ( n -- ) 0 ?do space loop ;
+
+: abort"
+ state @ if
+ postpone if
+ postpone ."
+ postpone cr
+ -2
+ postpone literal
+ postpone throw
+ postpone endif
+ else
+ [char] " parse
+ rot if
+ type
+ cr
+ -2 throw
+ else
+ 2drop
+ endif
+ endif
+; immediate
+
+
+\ ** CORE EXT
+.( loading CORE EXT words ) cr
+0 constant false
+false invert constant true
+: <> = 0= ;
+: 0<> 0= 0= ;
+: compile, , ;
+: convert char+ 65535 >number drop ; \ cribbed from DPANS A.6.2.0970
+: erase ( addr u -- ) 0 fill ;
+variable span
+: expect ( c-addr u1 -- ) accept span ! ;
+\ see marker.fr for MARKER implementation
+: nip ( y x -- x ) swap drop ;
+: tuck ( y x -- x y x) swap over ;
+: within ( test low high -- flag ) over - >r - r> u< ;
+
+
+\ ** LOCAL EXT word set
+\ #if FICL_WANT_LOCALS
+: locals| ( name...name | -- )
+ begin
+ bl word count
+ dup 0= abort" where's the delimiter??"
+ over c@
+ [char] | - over 1- or
+ while
+ (local)
+ repeat 2drop 0 0 (local)
+; immediate
+
+: local ( name -- ) bl word count (local) ; immediate
+
+: 2local ( name -- ) bl word count (2local) ; immediate
+
+: end-locals ( -- ) 0 0 (local) ; immediate
+
+\ #endif
+
+\ ** TOOLS word set...
+: ? ( addr -- ) @ . ;
+: dump ( addr u -- )
+ 0 ?do
+ dup c@ . 1+
+ i 7 and 7 = if cr endif
+ loop drop
+;
+
+\ ** SEARCH+EXT words and ficl helpers
+.( loading SEARCH & SEARCH-EXT words ) cr
+\ BRAND-WORDLIST is a helper for ficl-named-wordlist. Usage idiom:
+\ wordlist dup create , brand-wordlist
+\ gets the name of the word made by create and applies it to the wordlist...
+: brand-wordlist ( wid -- ) last-word >name drop wid-set-name ;
+
+: ficl-named-wordlist \ ( hash-size name -- ) run: ( -- wid )
+ ficl-wordlist dup create , brand-wordlist does> @ ;
+
+: wordlist ( -- )
+ 1 ficl-wordlist ;
+
+\ FICL-SET-CURRENT sets the compile wordlist and pushes the previous value
+: ficl-set-current ( wid -- old-wid )
+ get-current swap set-current ;
+
+\ DO_VOCABULARY handles the DOES> part of a VOCABULARY
+\ When executed, new voc replaces top of search stack
+: do-vocabulary ( -- )
+ does> @ search> drop >search ;
+
+: ficl-vocabulary ( nBuckets name -- )
+ ficl-named-wordlist do-vocabulary ;
+
+: vocabulary ( name -- )
+ 1 ficl-vocabulary ;
+
+\ PREVIOUS drops the search order stack
+: previous ( -- ) search> drop ;
+
+\ HIDDEN vocabulary is a place to keep helper words from cluttering the namespace
+\ USAGE:
+\ hide
+\ <definitions to hide>
+\ set-current
+\ <words that use hidden defs>
+\ previous ( pop HIDDEN off the search order )
+
+1 ficl-named-wordlist hidden
+: hide hidden dup >search ficl-set-current ;
+
+\ ALSO dups the search stack...
+: also ( -- )
+ search> dup >search >search ;
+
+\ FORTH drops the top of the search stack and pushes FORTH-WORDLIST
+: forth ( -- )
+ search> drop
+ forth-wordlist >search ;
+
+\ ONLY sets the search order to a default state
+: only ( -- )
+ -1 set-order ;
+
+\ ORDER displays the compile wid and the search order list
+hide
+: list-wid ( wid -- )
+ dup wid-get-name ( wid c-addr u )
+ ?dup if
+ type drop
+ else
+ drop ." (unnamed wid) " x.
+ endif cr
+;
+set-current \ stop hiding words
+
+: order ( -- )
+ ." Search:" cr
+ get-order 0 ?do 3 spaces list-wid loop cr
+ ." Compile: " get-current list-wid cr
+;
+
+: debug ' debug-xt ; immediate
+: on-step ." S: " .s cr ;
+
+
+\ Submitted by lch.
+: strdup ( c-addr length -- c-addr2 length2 ior )
+ 0 locals| addr2 length c-addr | end-locals
+ length 1 + allocate
+ 0= if
+ to addr2
+ c-addr addr2 length move
+ addr2 length 0
+ else
+ 0 -1
+ endif
+ ;
+
+: strcat ( 2:a 2:b -- 2:new-a )
+ 0 locals| b-length b-u b-addr a-u a-addr | end-locals
+ b-u to b-length
+ b-addr a-addr a-u + b-length move
+ a-addr a-u b-length +
+ ;
+
+: strcpy ( 2:a 2:b -- 2:new-a )
+ locals| b-u b-addr a-u a-addr | end-locals
+ a-addr 0 b-addr b-u strcat
+ ;
+
+
+previous \ lose hidden words from search order
+
+\ ** E N D S O F T C O R E . F R
+
diff --git a/softwords/softcore.pl b/softwords/softcore.pl
new file mode 100755
index 000000000000..cb521ad9ec3b
--- /dev/null
+++ b/softwords/softcore.pl
@@ -0,0 +1,144 @@
+#! /usr/bin/perl
+# Convert forth source files to a giant C string
+
+$now = localtime;
+
+print <<EOF
+/*******************************************************************
+** s o f t c o r e . c
+** Forth Inspired Command Language -
+** Words from CORE set written in FICL
+** Author: John Sadler (john_sadler\@alum.mit.edu)
+** Created: 27 December 1997
+** Last update: $now
+*******************************************************************/
+/*
+** DO NOT EDIT THIS FILE -- it is generated by softwords/softcore.pl
+** Make changes to the .fr files in ficl/softwords instead.
+** This file contains definitions that are compiled into the
+** system dictionary by the first virtual machine to be created.
+** Created automagically by ficl/softwords/softcore.pl
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler\@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+
+#include "ficl.h"
+
+static char softWords[] =
+#if FICL_WANT_SOFTWORDS
+EOF
+;
+
+$commenting = 0;
+
+while (<>) {
+ s"\n$""; # remove EOL
+ s/\"/\\\"/g; # escape quotes
+
+ #
+ # emit lines beginnning with "\ **" as C comments
+ #
+ if (/^\\\s\*\*/) {
+ s"^\\ "";
+ if ($commenting == 0) {
+ print "/*\n";
+ }
+ $commenting = 1;
+ print "$_\n";
+ next;
+ }
+
+ if ($commenting == 1) {
+ print "*/\n";
+ }
+
+ $commenting = 0;
+
+ #
+ # ignore empty lines and lines containing
+ # only empty comments
+ #
+ next if /^\s*\\\s*$/;
+ next if /^\s*$/;
+
+ #
+ # pass commented preprocessor directives
+ # == lines starting with "\ #"
+ # (supports single line directives only)
+ #
+ if (/^\\\s#/) {
+ s"^\\ "";
+ print "$_\n";
+ next;
+ }
+
+ next if /^\s*\\ /; # toss all other \ comment lines
+ s"\\\s+.*$"" ; # lop off trailing \ comments
+ s"\s+\(\s.*?\)""g; # delete ( ) comments
+ s"^\s+""; # remove leading spaces
+ s"\s+$""; # remove trailing spaces
+
+ #
+ # emit whatever's left as quoted string fragments
+ #
+# $out = " \"" . $_ . " \\n\"";
+ $out = " \"" . $_ . " \"";
+ print "$out\n";
+}
+
+print <<EOF
+#endif /* WANT_SOFTWORDS */
+ "quit ";
+
+
+void ficlCompileSoftCore(FICL_SYSTEM *pSys)
+{
+ FICL_VM *pVM = pSys->vmList;
+ CELL id = pVM->sourceID;
+ int ret = sizeof (softWords);
+ assert(pVM);
+ pVM->sourceID.i = -1;
+ ret = ficlExec(pVM, softWords);
+ pVM->sourceID = id;
+ if (ret == VM_ERREXIT)
+ assert(FALSE);
+ return;
+}
+
+
+EOF
+;
+
diff --git a/softwords/softcore.py b/softwords/softcore.py
new file mode 100644
index 000000000000..f5f3d8dc9cc3
--- /dev/null
+++ b/softwords/softcore.py
@@ -0,0 +1,152 @@
+#! python
+# Convert forth source files to a giant C string
+
+import re;
+import sys;
+import time;
+
+
+print """/*******************************************************************
+** s o f t c o r e . c
+** Forth Inspired Command Language -
+** Words from CORE set written in FICL
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 27 December 1997
+** Last update: """ + time.ctime(time.time()) + """
+*******************************************************************/
+/*
+** DO NOT EDIT THIS FILE -- it is generated by softwords/softcore.py
+** Make changes to the .fr files in ficl/softwords instead.
+** This file contains definitions that are compiled into the
+** system dictionary by the first virtual machine to be created.
+** Created automagically by ficl/softwords/softcore.py
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please send
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+
+#include "ficl.h"
+
+static char softWords[] =
+#if FICL_WANT_SOFTWORDS"""
+
+escapedQuotes = re.compile( r'^"(.*)"$' )
+backslash = re.compile( r'^(.*[^\s])\s+\\(\s+[^\s].*)$' )
+parentheses = re.compile( r'^(.*[^\s])\s+\(\s[^)]+\)(\s+[^\s].*)?$' )
+
+
+commenting = 0;
+
+for a in (sys.argv[1:]):
+ f = open(a)
+ for line in f.readlines():
+
+ # trim all whitespace
+ line = line.strip();
+
+ # remove quotes around quoted lines
+ quoted = escapedQuotes.match(line)
+ if (quoted != None):
+ line = quoted.group(1).strip()
+
+ #
+ # emit lines beginnning with "\ **" as C comments
+ #
+ if (line[0:4] == "\\ **"):
+ if (commenting == 0):
+ print("/*")
+ commenting = 1
+ print(line[2:])
+ continue
+
+ if (commenting == 1):
+ print "*/"
+
+ commenting = 0
+
+ # ignore empty lines
+ if (len(line) == 0):
+ continue
+
+ # pass commented preprocessor directives
+ # == lines starting with "\ #"
+ # (supports single line directives only)
+ if (line[0:3] == "\\ #"):
+ print(line[2:]) # include the leading #!
+ continue
+
+ # ignore remaining lines starting with comments
+ if (line[0] == "\\"):
+ continue
+
+ # remove trailing comments
+ trailingComment = backslash.match(line)
+ if (trailingComment != None):
+ line = trailingComment.group(1)
+
+ # remove ( comments ) in the middle
+ embeddedComment = parentheses.match(line)
+ if (embeddedComment != None):
+ line = embeddedComment.group(1)
+ if (embeddedComment.lastindex >= 2):
+ line = line + " " + embeddedComment.group(2).strip()
+
+ # quote double-quote characters
+ line = line.replace("\"", "\\\"")
+
+ # emit whatever's left as quoted string fragments
+ print(" \"" + line + " \"");
+
+
+print """#endif /* WANT_SOFTWORDS */
+ "quit ";
+
+
+void ficlCompileSoftCore(FICL_SYSTEM *pSys)
+{
+ FICL_VM *pVM = pSys->vmList;
+ CELL id = pVM->sourceID;
+ int ret = sizeof (softWords);
+ assert(pVM);
+ pVM->sourceID.i = -1;
+ ret = ficlExec(pVM, softWords);
+ pVM->sourceID = id;
+ if (ret == VM_ERREXIT)
+ assert(FALSE);
+ return;
+}
+
+
+"""
diff --git a/softwords/softcore.py.bat b/softwords/softcore.py.bat
new file mode 100644
index 000000000000..20ada6f0bc9b
--- /dev/null
+++ b/softwords/softcore.py.bat
@@ -0,0 +1 @@
+python softcore.py softcore.fr jhlocal.fr marker.fr prefix.fr ifbrack.fr oo.fr classes.fr string.fr fileaccess.fr >..\softcore.c
diff --git a/softwords/string.fr b/softwords/string.fr
new file mode 100644
index 000000000000..e7f2c698f2f4
--- /dev/null
+++ b/softwords/string.fr
@@ -0,0 +1,148 @@
+\ #if (FICL_WANT_OOP)
+\ ** ficl/softwords/string.fr
+\ A useful dynamic string class
+\ John Sadler 14 Sep 1998
+\
+\ ** C - S T R I N G
+\ counted string, buffer sized dynamically
+\ Creation example:
+\ c-string --> new str
+\ s" arf arf!!" str --> set
+\ s" woof woof woof " str --> cat
+\ str --> type cr
+\
+
+.( loading ficl string class ) cr
+also oop definitions
+
+object subclass c-string
+ c-cell obj: .count
+ c-cell obj: .buflen
+ c-ptr obj: .buf
+ 32 constant min-buf
+
+ : get-count ( 2:this -- count ) my=[ .count get ] ;
+ : set-count ( count 2:this -- ) my=[ .count set ] ;
+
+ : ?empty ( 2:this -- flag ) --> get-count 0= ;
+
+ : get-buflen ( 2:this -- len ) my=[ .buflen get ] ;
+ : set-buflen ( len 2:this -- ) my=[ .buflen set ] ;
+
+ : get-buf ( 2:this -- ptr ) my=[ .buf get-ptr ] ;
+ : set-buf { ptr len 2:this -- }
+ ptr this my=[ .buf set-ptr ]
+ len this my=> set-buflen
+ ;
+
+ \ set buffer to null and buflen to zero
+ : clr-buf ( 2:this -- )
+ 0 0 2over my=> set-buf
+ 0 -rot my=> set-count
+ ;
+
+ \ free the buffer if there is one, set buf pointer to null
+ : free-buf { 2:this -- }
+ this my=> get-buf
+ ?dup if
+ free
+ abort" c-string free failed"
+ this my=> clr-buf
+ endif
+ ;
+
+ \ guarantee buffer is large enough to hold size chars
+ : size-buf { size 2:this -- }
+ size 0< abort" need positive size for size-buf"
+ size 0= if
+ this --> free-buf exit
+ endif
+
+ \ force buflen to be a positive multiple of min-buf chars
+ my=> min-buf size over / 1+ * chars to size
+
+ \ if buffer is null, allocate one, else resize it
+ this --> get-buflen 0=
+ if
+ size allocate
+ abort" out of memory"
+ size this --> set-buf
+ size this --> set-buflen
+ exit
+ endif
+
+ size this --> get-buflen > if
+ this --> get-buf size resize
+ abort" out of memory"
+ size this --> set-buf
+ endif
+ ;
+
+ : set { c-addr u 2:this -- }
+ u this --> size-buf
+ u this --> set-count
+ c-addr this --> get-buf u move
+ ;
+
+ : get { 2:this -- c-addr u }
+ this --> get-buf
+ this --> get-count
+ ;
+
+ \ append string to existing one
+ : cat { c-addr u 2:this -- }
+ this --> get-count u + dup >r
+ this --> size-buf
+ c-addr this --> get-buf this --> get-count + u move
+ r> this --> set-count
+ ;
+
+ : type { 2:this -- }
+ this --> ?empty if ." (empty) " exit endif
+ this --> .buf --> get-ptr
+ this --> .count --> get
+ type
+ ;
+
+ : compare ( 2string 2:this -- n )
+ --> get
+ 2swap
+ --> get
+ 2swap compare
+ ;
+
+ : hashcode ( 2:this -- hashcode )
+ --> get hash
+ ;
+
+ \ destructor method (overrides object --> free)
+ : free ( 2:this -- ) 2dup --> free-buf object => free ;
+
+end-class
+
+c-string subclass c-hashstring
+ c-2byte obj: .hashcode
+
+ : set-hashcode { 2:this -- }
+ this --> super --> hashcode
+ this --> .hashcode --> set
+ ;
+
+ : get-hashcode ( 2:this -- hashcode )
+ --> .hashcode --> get
+ ;
+
+ : set ( c-addr u 2:this -- )
+ 2swap 2over --> super --> set
+ --> set-hashcode
+ ;
+
+ : cat ( c-addr u 2:this -- )
+ 2swap 2over --> super --> cat
+ --> set-hashcode
+ ;
+
+end-class
+
+previous definitions
+\ #endif
diff --git a/softwords/win32.fr b/softwords/win32.fr
new file mode 100644
index 000000000000..b34f4e329481
--- /dev/null
+++ b/softwords/win32.fr
@@ -0,0 +1,10 @@
+\ **
+\ ** win32.fr
+\ ** submitted by Larry Hastings, larry@hastings.org
+\ **
+
+
+\ ** simple wrappers for callnativeFunction that specify the calling convention
+: callcfunction 1 callnativeFunction ;
+: callpascalfunction 0 callnativeFunction ;
+
diff --git a/stack.c b/stack.c
new file mode 100644
index 000000000000..84e7da0fb8a2
--- /dev/null
+++ b/stack.c
@@ -0,0 +1,367 @@
+/*******************************************************************
+** s t a c k . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** $Id: stack.c,v 1.7 2001-06-12 01:24:35-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+
+#include "ficl.h"
+
+#define STKDEPTH(s) ((s)->sp - (s)->base)
+
+/*
+** N O T E: Stack convention:
+**
+** sp points to the first available cell
+** push: store value at sp, increment sp
+** pop: decrement sp, fetch value at sp
+** Stack grows from low to high memory
+*/
+
+/*******************************************************************
+ v m C h e c k S t a c k
+** Check the parameter stack for underflow or overflow.
+** nCells controls the type of check: if nCells is zero,
+** the function checks the stack state for underflow and overflow.
+** If nCells > 0, checks to see that the stack has room to push
+** that many cells. If less than zero, checks to see that the
+** stack has room to pop that many cells. If any test fails,
+** the function throws (via vmThrow) a VM_ERREXIT exception.
+*******************************************************************/
+void vmCheckStack(FICL_VM *pVM, int popCells, int pushCells)
+{
+ FICL_STACK *pStack = pVM->pStack;
+ int nFree = pStack->base + pStack->nCells - pStack->sp;
+
+ if (popCells > STKDEPTH(pStack))
+ {
+ vmThrowErr(pVM, "Error: stack underflow");
+ }
+
+ if (nFree < pushCells - popCells)
+ {
+ vmThrowErr(pVM, "Error: stack overflow");
+ }
+
+ return;
+}
+
+#if FICL_WANT_FLOAT
+void vmCheckFStack(FICL_VM *pVM, int popCells, int pushCells)
+{
+ FICL_STACK *fStack = pVM->fStack;
+ int nFree = fStack->base + fStack->nCells - fStack->sp;
+
+ if (popCells > STKDEPTH(fStack))
+ {
+ vmThrowErr(pVM, "Error: float stack underflow");
+ }
+
+ if (nFree < pushCells - popCells)
+ {
+ vmThrowErr(pVM, "Error: float stack overflow");
+ }
+}
+#endif
+
+/*******************************************************************
+ s t a c k C r e a t e
+**
+*******************************************************************/
+
+FICL_STACK *stackCreate(unsigned nCells)
+{
+ size_t size = sizeof (FICL_STACK) + nCells * sizeof (CELL);
+ FICL_STACK *pStack = ficlMalloc(size);
+
+#if FICL_ROBUST
+ assert (nCells != 0);
+ assert (pStack != NULL);
+#endif
+
+ pStack->nCells = nCells;
+ pStack->sp = pStack->base;
+ pStack->pFrame = NULL;
+ return pStack;
+}
+
+
+/*******************************************************************
+ s t a c k D e l e t e
+**
+*******************************************************************/
+
+void stackDelete(FICL_STACK *pStack)
+{
+ if (pStack)
+ ficlFree(pStack);
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k D e p t h
+**
+*******************************************************************/
+
+int stackDepth(FICL_STACK *pStack)
+{
+ return STKDEPTH(pStack);
+}
+
+/*******************************************************************
+ s t a c k D r o p
+**
+*******************************************************************/
+
+void stackDrop(FICL_STACK *pStack, int n)
+{
+#if FICL_ROBUST
+ assert(n > 0);
+#endif
+ pStack->sp -= n;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k F e t c h
+**
+*******************************************************************/
+
+CELL stackFetch(FICL_STACK *pStack, int n)
+{
+ return pStack->sp[-n-1];
+}
+
+void stackStore(FICL_STACK *pStack, int n, CELL c)
+{
+ pStack->sp[-n-1] = c;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k G e t T o p
+**
+*******************************************************************/
+
+CELL stackGetTop(FICL_STACK *pStack)
+{
+ return pStack->sp[-1];
+}
+
+
+/*******************************************************************
+ s t a c k L i n k
+** Link a frame using the stack's frame pointer. Allot space for
+** nCells cells in the frame
+** 1) Push pFrame
+** 2) pFrame = sp
+** 3) sp += nCells
+*******************************************************************/
+
+void stackLink(FICL_STACK *pStack, int nCells)
+{
+ stackPushPtr(pStack, pStack->pFrame);
+ pStack->pFrame = pStack->sp;
+ pStack->sp += nCells;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k U n l i n k
+** Unink a stack frame previously created by stackLink
+** 1) sp = pFrame
+** 2) pFrame = pop()
+*******************************************************************/
+
+void stackUnlink(FICL_STACK *pStack)
+{
+ pStack->sp = pStack->pFrame;
+ pStack->pFrame = stackPopPtr(pStack);
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k P i c k
+**
+*******************************************************************/
+
+void stackPick(FICL_STACK *pStack, int n)
+{
+ stackPush(pStack, stackFetch(pStack, n));
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k P o p
+**
+*******************************************************************/
+
+CELL stackPop(FICL_STACK *pStack)
+{
+ return *--pStack->sp;
+}
+
+void *stackPopPtr(FICL_STACK *pStack)
+{
+ return (*--pStack->sp).p;
+}
+
+FICL_UNS stackPopUNS(FICL_STACK *pStack)
+{
+ return (*--pStack->sp).u;
+}
+
+FICL_INT stackPopINT(FICL_STACK *pStack)
+{
+ return (*--pStack->sp).i;
+}
+
+#if (FICL_WANT_FLOAT)
+float stackPopFloat(FICL_STACK *pStack)
+{
+ return (*(--pStack->sp)).f;
+}
+#endif
+
+/*******************************************************************
+ s t a c k P u s h
+**
+*******************************************************************/
+
+void stackPush(FICL_STACK *pStack, CELL c)
+{
+ *pStack->sp++ = c;
+}
+
+void stackPushPtr(FICL_STACK *pStack, void *ptr)
+{
+ *pStack->sp++ = LVALUEtoCELL(ptr);
+}
+
+void stackPushUNS(FICL_STACK *pStack, FICL_UNS u)
+{
+ *pStack->sp++ = LVALUEtoCELL(u);
+}
+
+void stackPushINT(FICL_STACK *pStack, FICL_INT i)
+{
+ *pStack->sp++ = LVALUEtoCELL(i);
+}
+
+#if (FICL_WANT_FLOAT)
+void stackPushFloat(FICL_STACK *pStack, FICL_FLOAT f)
+{
+ *pStack->sp++ = LVALUEtoCELL(f);
+}
+#endif
+
+/*******************************************************************
+ s t a c k R e s e t
+**
+*******************************************************************/
+
+void stackReset(FICL_STACK *pStack)
+{
+ pStack->sp = pStack->base;
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k R o l l
+** Roll nth stack entry to the top (counting from zero), if n is
+** >= 0. Drop other entries as needed to fill the hole.
+** If n < 0, roll top-of-stack to nth entry, pushing others
+** upward as needed to fill the hole.
+*******************************************************************/
+
+void stackRoll(FICL_STACK *pStack, int n)
+{
+ CELL c;
+ CELL *pCell;
+
+ if (n == 0)
+ return;
+ else if (n > 0)
+ {
+ pCell = pStack->sp - n - 1;
+ c = *pCell;
+
+ for (;n > 0; --n, pCell++)
+ {
+ *pCell = pCell[1];
+ }
+
+ *pCell = c;
+ }
+ else
+ {
+ pCell = pStack->sp - 1;
+ c = *pCell;
+
+ for (; n < 0; ++n, pCell--)
+ {
+ *pCell = pCell[-1];
+ }
+
+ *pCell = c;
+ }
+ return;
+}
+
+
+/*******************************************************************
+ s t a c k S e t T o p
+**
+*******************************************************************/
+
+void stackSetTop(FICL_STACK *pStack, CELL c)
+{
+ pStack->sp[-1] = c;
+ return;
+}
+
+
diff --git a/sysdep.c b/sysdep.c
new file mode 100644
index 000000000000..3c87db2d5c89
--- /dev/null
+++ b/sysdep.c
@@ -0,0 +1,409 @@
+/*******************************************************************
+** s y s d e p . c
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Implementations of FICL external interface functions...
+**
+** (simple) port to Linux, Skip Carter 26 March 1998
+** $Id: sysdep.c,v 1.9 2001-07-23 22:01:24-07 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "ficl.h"
+
+/*
+******************* FreeBSD P O R T B E G I N S H E R E ******************** Michael Smith
+*/
+#if defined (FREEBSD_ALPHA)
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ u_int64_t qx;
+
+ qx = (u_int64_t)x * (u_int64_t) y;
+
+ q.hi = (u_int32_t)( qx >> 32 );
+ q.lo = (u_int32_t)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ u_int64_t qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ while(*msg != 0)
+ putchar(*(msg++));
+ if (fNewline)
+ putchar('\n');
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void *ficlRealloc (void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
+
+/*
+******************* P C / W I N 3 2 P O R T B E G I N S H E R E ***********************
+*/
+#elif defined (_M_IX86)
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+
+ __asm
+ {
+ mov eax,x
+ mov edx,y
+ mul edx
+ mov q.hi,edx
+ mov q.lo,eax
+ }
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+
+ __asm
+ {
+ mov eax,q.lo
+ mov edx,q.hi
+ div y
+ mov result.quot,eax
+ mov result.rem,edx
+ }
+
+ return result;
+}
+
+#endif
+
+#if !defined (_WINDOWS)
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ if (fNewline)
+ puts(msg);
+ else
+ fputs(msg, stdout);
+
+ return;
+}
+
+#endif
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+
+void *ficlRealloc(void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
+
+/*
+******************* 6 8 K C P U 3 2 P O R T B E G I N S H E R E ********************
+*/
+#elif defined (MOTO_CPU32)
+
+#if PORTABLE_LONGMULDIV == 0
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ IGNORE(q); /* suppress goofy compiler warnings */
+ IGNORE(x);
+ IGNORE(y);
+
+#pragma ASM
+ move.l (S_x,a6),d1
+ mulu.l (S_y,a6),d0:d1
+ move.l d1,(S_q+4,a6)
+ move.l d0,(S_q+0,a6)
+#pragma END_ASM
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ IGNORE(result); /* suppress goofy compiler warnings */
+ IGNORE(q);
+ IGNORE(y);
+
+#pragma ASM
+ move.l (S_q+0,a6),d0 ; hi 32 --> d0
+ move.l (S_q+4,a6),d1 ; lo 32 --> d1
+ divu.l (S_y,a6),d0:d1 ; d0 <-- rem, d1 <-- quot
+ move.l d1,(S_result+0,a6)
+ move.l d0,(S_result+4,a6)
+#pragma END_ASM
+
+ return result;
+}
+
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+}
+
+void ficlFree (void *p)
+{
+}
+
+
+void *ficlRealloc(void *p, size_t size)
+{
+ void *pv = malloc(size);
+ if (p)
+ {
+ memcpy(pv, p, size)
+ free(p);
+ }
+
+ return pv;
+}
+
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
+
+#endif /* MOTO_CPU32 */
+
+/*
+******************* Linux P O R T B E G I N S H E R E ******************** Skip Carter, March 1998
+*/
+
+#if defined(linux) || defined(riscos)
+
+#if PORTABLE_LONGMULDIV == 0
+
+typedef unsigned long long __u64;
+typedef unsigned long __u32;
+
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)
+{
+ DPUNS q;
+ __u64 qx;
+
+ qx = (__u64)x * (__u64) y;
+
+ q.hi = (__u32)( qx >> 32 );
+ q.lo = (__u32)( qx & 0xFFFFFFFFL);
+
+ return q;
+}
+
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)
+{
+ UNSQR result;
+ __u64 qx, qh;
+
+ qh = q.hi;
+ qx = (qh << 32) | q.lo;
+
+ result.quot = qx / y;
+ result.rem = qx % y;
+
+ return result;
+}
+
+#endif
+
+void ficlTextOut(FICL_VM *pVM, char *msg, int fNewline)
+{
+ IGNORE(pVM);
+
+ if (fNewline)
+ puts(msg);
+ else
+ fputs(msg, stdout);
+
+ return;
+}
+
+void *ficlMalloc (size_t size)
+{
+ return malloc(size);
+}
+
+void ficlFree (void *p)
+{
+ free(p);
+}
+
+void *ficlRealloc(void *p, size_t size)
+{
+ return realloc(p, size);
+}
+
+
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** is guaranteed to be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** befor timeout (optional - could also block forever)
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock)
+{
+ IGNORE(fLock);
+ return 0;
+}
+#endif /* FICL_MULTITHREAD */
+
+#endif /* linux */
+
+
diff --git a/sysdep.h b/sysdep.h
new file mode 100644
index 000000000000..27e55a9a10a4
--- /dev/null
+++ b/sysdep.h
@@ -0,0 +1,465 @@
+/*******************************************************************
+ s y s d e p . h
+** Forth Inspired Command Language
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 16 Oct 1997
+** Ficl system dependent types and prototypes...
+**
+** Note: Ficl also depends on the use of "assert" when
+** FICL_ROBUST is enabled. This may require some consideration
+** in firmware systems since assert often
+** assumes stderr/stdout.
+** $Id: sysdep.h,v 1.11 2001-11-11 12:25:46-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#if !defined (__SYSDEP_H__)
+#define __SYSDEP_H__
+
+#include <stddef.h> /* size_t, NULL */
+#include <setjmp.h>
+#include <assert.h>
+
+#if defined(_WIN32)
+ #include <stdio.h>
+ #ifndef alloca
+ #define alloca(x) _alloca(x)
+ #endif /* alloca */
+ #define fstat _fstat
+ #define stat _stat
+ #define getcwd _getcwd
+ #define chdir _chdir
+ #define unlink _unlink
+ #define fileno _fileno
+
+ #define FICL_HAVE_FTRUNCATE 1
+ extern int ftruncate(int fileno, size_t size);
+#elif defined(linux)
+ #define FICL_HAVE_FTRUNCATE 1
+#endif /* platform */
+
+#if !defined IGNORE /* Macro to silence unused param warnings */
+#define IGNORE(x) &x
+#endif
+
+/*
+** TRUE and FALSE for C boolean operations, and
+** portable 32 bit types for CELLs
+**
+*/
+#if !defined TRUE
+#define TRUE 1
+#endif
+#if !defined FALSE
+#define FALSE 0
+#endif
+
+/*
+** FreeBSD Alpha (64 bit) data types
+*/
+#if defined (FREEBSD_ALPHA)
+
+#define INT32 int
+#define UNS32 unsigned int
+#define FICL_INT long
+#define FICL_UNS unsigned long
+#define BITS_PER_CELL 64
+#define FICL_ALIGN 3
+#endif
+
+/*
+** System dependent data type declarations...
+*/
+#if !defined INT32
+#define INT32 long
+#endif
+
+#if !defined UNS32
+#define UNS32 unsigned long
+#endif
+
+#if !defined UNS16
+#define UNS16 unsigned short
+#endif
+
+#if !defined UNS8
+#define UNS8 unsigned char
+#endif
+
+#if !defined NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+** FICL_UNS and FICL_INT must have the same size as a void* on
+** the target system. A CELL is a union of void*, FICL_UNS, and
+** FICL_INT.
+** (11/2000: same for FICL_FLOAT)
+*/
+#if !defined FICL_INT
+#define FICL_INT INT32
+#endif
+
+#if !defined FICL_UNS
+#define FICL_UNS UNS32
+#endif
+
+#if !defined FICL_FLOAT
+#define FICL_FLOAT float
+#endif
+
+/*
+** Ficl presently supports values of 32 and 64 for BITS_PER_CELL
+*/
+#if !defined BITS_PER_CELL
+#define BITS_PER_CELL 32
+#endif
+
+#if ((BITS_PER_CELL != 32) && (BITS_PER_CELL != 64))
+ Error!
+#endif
+
+typedef struct
+{
+ FICL_UNS hi;
+ FICL_UNS lo;
+} DPUNS;
+
+typedef struct
+{
+ FICL_UNS quot;
+ FICL_UNS rem;
+} UNSQR;
+
+typedef struct
+{
+ FICL_INT hi;
+ FICL_INT lo;
+} DPINT;
+
+typedef struct
+{
+ FICL_INT quot;
+ FICL_INT rem;
+} INTQR;
+
+
+/*
+** B U I L D C O N T R O L S
+*/
+
+#if !defined (FICL_MINIMAL)
+#define FICL_MINIMAL 0
+#endif
+#if (FICL_MINIMAL)
+#define FICL_WANT_SOFTWORDS 0
+#define FICL_WANT_FILE 0
+#define FICL_WANT_FLOAT 0
+#define FICL_WANT_USER 0
+#define FICL_WANT_LOCALS 0
+#define FICL_WANT_DEBUGGER 0
+#define FICL_WANT_OOP 0
+#define FICL_PLATFORM_EXTEND 0
+#define FICL_MULTITHREAD 0
+#define FICL_ROBUST 0
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** FICL_PLATFORM_EXTEND
+** Includes words defined in ficlCompilePlatform (see win32.c and unix.c for example)
+*/
+#if defined (_WIN32)
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 1
+#endif
+#endif
+
+#if !defined (FICL_PLATFORM_EXTEND)
+#define FICL_PLATFORM_EXTEND 0
+#endif
+
+
+/*
+** FICL_WANT_FILE
+** Includes the FILE and FILE-EXT wordset and associated code. Turn this off if you do not
+** have a file system!
+** Contributed by Larry Hastings
+*/
+#if !defined (FICL_WANT_FILE)
+#define FICL_WANT_FILE 1
+#endif
+
+/*
+** FICL_WANT_FLOAT
+** Includes a floating point stack for the VM, and words to do float operations.
+** Contributed by Guy Carver
+*/
+#if !defined (FICL_WANT_FLOAT)
+#define FICL_WANT_FLOAT 1
+#endif
+
+/*
+** FICL_WANT_DEBUGGER
+** Inludes a simple source level debugger
+*/
+#if !defined (FICL_WANT_DEBUGGER)
+#define FICL_WANT_DEBUGGER 1
+#endif
+
+/*
+** FICL_EXTENDED_PREFIX enables a bunch of extra prefixes in prefix.c and prefix.fr (if
+** included as part of softcore.c)
+*/
+#if !defined FICL_EXTENDED_PREFIX
+#define FICL_EXTENDED_PREFIX 0
+#endif
+
+/*
+** User variables: per-instance variables bound to the VM.
+** Kinda like thread-local storage. Could be implemented in a
+** VM private dictionary, but I've chosen the lower overhead
+** approach of an array of CELLs instead.
+*/
+#if !defined FICL_WANT_USER
+#define FICL_WANT_USER 1
+#endif
+
+#if !defined FICL_USER_CELLS
+#define FICL_USER_CELLS 16
+#endif
+
+/*
+** FICL_WANT_LOCALS controls the creation of the LOCALS wordset and
+** a private dictionary for local variable compilation.
+*/
+#if !defined FICL_WANT_LOCALS
+#define FICL_WANT_LOCALS 1
+#endif
+
+/* Max number of local variables per definition */
+#if !defined FICL_MAX_LOCALS
+#define FICL_MAX_LOCALS 16
+#endif
+
+/*
+** FICL_WANT_OOP
+** Inludes object oriented programming support (in softwords)
+** OOP support requires locals and user variables!
+*/
+#if !(FICL_WANT_LOCALS) || !(FICL_WANT_USER)
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 0
+#endif
+#endif
+
+#if !defined (FICL_WANT_OOP)
+#define FICL_WANT_OOP 1
+#endif
+
+/*
+** FICL_WANT_SOFTWORDS
+** Controls inclusion of all softwords in softcore.c
+*/
+#if !defined (FICL_WANT_SOFTWORDS)
+#define FICL_WANT_SOFTWORDS 1
+#endif
+
+/*
+** FICL_MULTITHREAD enables dictionary mutual exclusion
+** wia the ficlLockDictionary system dependent function.
+** Note: this implementation is experimental and poorly
+** tested. Further, it's unnecessary unless you really
+** intend to have multiple SESSIONS (poor choice of name
+** on my part) - that is, threads that modify the dictionary
+** at the same time.
+*/
+#if !defined FICL_MULTITHREAD
+#define FICL_MULTITHREAD 0
+#endif
+
+/*
+** PORTABLE_LONGMULDIV causes ficlLongMul and ficlLongDiv to be
+** defined in C in sysdep.c. Use this if you cannot easily
+** generate an inline asm definition
+*/
+#if !defined (PORTABLE_LONGMULDIV)
+#define PORTABLE_LONGMULDIV 0
+#endif
+
+/*
+** INLINE_INNER_LOOP causes the inner interpreter to be inline code
+** instead of a function call. This is mainly because MS VC++ 5
+** chokes with an internal compiler error on the function version.
+** in release mode. Sheesh.
+*/
+#if !defined INLINE_INNER_LOOP
+#if defined _DEBUG
+#define INLINE_INNER_LOOP 0
+#else
+#define INLINE_INNER_LOOP 1
+#endif
+#endif
+
+/*
+** FICL_ROBUST enables bounds checking of stacks and the dictionary.
+** This will detect stack over and underflows and dictionary overflows.
+** Any exceptional condition will result in an assertion failure.
+** (As generated by the ANSI assert macro)
+** FICL_ROBUST == 1 --> stack checking in the outer interpreter
+** FICL_ROBUST == 2 also enables checking in many primitives
+*/
+
+#if !defined FICL_ROBUST
+#define FICL_ROBUST 2
+#endif
+
+/*
+** FICL_DEFAULT_STACK Specifies the default size (in CELLs) of
+** a new virtual machine's stacks, unless overridden at
+** create time.
+*/
+#if !defined FICL_DEFAULT_STACK
+#define FICL_DEFAULT_STACK 128
+#endif
+
+/*
+** FICL_DEFAULT_DICT specifies the number of CELLs to allocate
+** for the system dictionary by default. The value
+** can be overridden at startup time as well.
+** FICL_DEFAULT_ENV specifies the number of cells to allot
+** for the environment-query dictionary.
+*/
+#if !defined FICL_DEFAULT_DICT
+#define FICL_DEFAULT_DICT 12288
+#endif
+
+#if !defined FICL_DEFAULT_ENV
+#define FICL_DEFAULT_ENV 512
+#endif
+
+/*
+** FICL_DEFAULT_VOCS specifies the maximum number of wordlists in
+** the dictionary search order. See Forth DPANS sec 16.3.3
+** (file://dpans16.htm#16.3.3)
+*/
+#if !defined FICL_DEFAULT_VOCS
+#define FICL_DEFAULT_VOCS 16
+#endif
+
+/*
+** FICL_MAX_PARSE_STEPS controls the size of an array in the FICL_SYSTEM structure
+** that stores pointers to parser extension functions. I would never expect to have
+** more than 8 of these, so that's the default limit. Too many of these functions
+** will probably exact a nasty performance penalty.
+*/
+#if !defined FICL_MAX_PARSE_STEPS
+#define FICL_MAX_PARSE_STEPS 8
+#endif
+
+/*
+** FICL_ALIGN is the power of two to which the dictionary
+** pointer address must be aligned. This value is usually
+** either 1 or 2, depending on the memory architecture
+** of the target system; 2 is safe on any 16 or 32 bit
+** machine. 3 would be appropriate for a 64 bit machine.
+*/
+#if !defined FICL_ALIGN
+#define FICL_ALIGN 2
+#define FICL_ALIGN_ADD ((1 << FICL_ALIGN) - 1)
+#endif
+
+/*
+** System dependent routines --
+** edit the implementations in sysdep.c to be compatible
+** with your runtime environment...
+** ficlTextOut sends a NULL terminated string to the
+** default output device - used for system error messages
+** ficlMalloc and ficlFree have the same semantics as malloc and free
+** in standard C
+** ficlLongMul multiplies two UNS32s and returns a 64 bit unsigned
+** product
+** ficlLongDiv divides an UNS64 by an UNS32 and returns UNS32 quotient
+** and remainder
+*/
+struct vm;
+void ficlTextOut(struct vm *pVM, char *msg, int fNewline);
+void *ficlMalloc (size_t size);
+void ficlFree (void *p);
+void *ficlRealloc(void *p, size_t size);
+/*
+** Stub function for dictionary access control - does nothing
+** by default, user can redefine to guarantee exclusive dict
+** access to a single thread for updates. All dict update code
+** must be bracketed as follows:
+** ficlLockDictionary(TRUE);
+** <code that updates dictionary>
+** ficlLockDictionary(FALSE);
+**
+** Returns zero if successful, nonzero if unable to acquire lock
+** before timeout (optional - could also block forever)
+**
+** NOTE: this function must be implemented with lock counting
+** semantics: nested calls must behave properly.
+*/
+#if FICL_MULTITHREAD
+int ficlLockDictionary(short fLock);
+#else
+#define ficlLockDictionary(x) 0 /* ignore */
+#endif
+
+/*
+** 64 bit integer math support routines: multiply two UNS32s
+** to get a 64 bit product, & divide the product by an UNS32
+** to get an UNS32 quotient and remainder. Much easier in asm
+** on a 32 bit CPU than in C, which usually doesn't support
+** the double length result (but it should).
+*/
+DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y);
+UNSQR ficlLongDiv(DPUNS q, FICL_UNS y);
+
+
+/*
+** FICL_HAVE_FTRUNCATE indicates whether the current OS supports
+** the ftruncate() function (available on most UNIXes). This
+** function is necessary to provide the complete File-Access wordset.
+*/
+#if !defined (FICL_HAVE_FTRUNCATE)
+#define FICL_HAVE_FTRUNCATE 0
+#endif
+
+
+#endif /*__SYSDEP_H__*/
diff --git a/test/asm68k.4th b/test/asm68k.4th
new file mode 100644
index 000000000000..a549d69c17c6
--- /dev/null
+++ b/test/asm68k.4th
@@ -0,0 +1,308 @@
+HEX
+4e71 constant nop
+
+\ w, ( WORD compile )
+: w, ( d16 -- ) dup 100 / c, c, ;
+
+: OCTAL 8 BASE ! ;
+
+
+\ FORTH ASSEMBLER ....
+
+ALSO FORTH
+VOCABULARY ASSEMBLER IMMEDIATE
+ASSEMBLER DEFINITIONS
+
+: END-CODE ALIGN CURRENT @ CONTEXT ! ;
+: *SWAP SWAP ;
+: ?, IF w, THEN w, ;
+
+\ SIZES
+
+OCTAL
+VARIABLE SIZE
+: BYTE 10000 SIZE ! ;
+: WORD 30100 SIZE ! ;
+: LONG 24600 SIZE ! ;
+: SZ CREATE , DOES> @ SIZE @ AND OR ;
+
+00300 SZ SZ3
+00400 SZ SZ4
+04000 SZ SZ40
+30000 SZ SZ300
+
+: LONG? SIZE @ 24600 = ;
+: -SZ1 LONG? IF 100 OR THEN ;
+
+\ ADDRESSING MODES
+
+: REGS 10 0 DO DUP 1001 I * OR CONSTANT LOOP DROP ;
+: MODE CREATE , DOES> @ SWAP 7007 AND OR ;
+
+0000 REGS D0 D1 D2 D3 D4 D5 D6 D7
+0110 REGS A0 A1 A2 A3 A4 A5 A6 A7
+
+0220 MODE )
+0330 MODE )+
+0440 MODE -)
+0550 MODE D)
+0660 MODE DI)
+0770 CONSTANT #)
+1771 CONSTANT L#)
+2772 CONSTANT PCD)
+3773 CONSTANT PCDI)
+4774 CONSTANT #
+
+\ FIELDS AND REGISTER ASSIGNMENTS
+
+: FIELD CREATE , DOES> @ AND ;
+
+7000 FIELD RD
+0007 FIELD RS
+0070 FIELD MS
+0077 FIELD EAS
+0377 FIELD LOW
+
+: DN? DUP MS 0 = ;
+: SRC OVER EAS OR ;
+: DST SWAP RD OR ;
+
+A7 CONSTANT SP
+A6 CONSTANT RP
+A5 CONSTANT IP
+
+: ?MODE 0 = ABORT" BAD MODE" ;
+: ??Dn DN? ?MODE ;
+: ??An DUP MS 1 = ?MODE ;
+: ??JMP DUP MS DUP 2 = SWAP 4 > OR OVER 74 = NOT AND ?MODE ;
+
+\ EXTENDED ADDRESSING
+
+: DOUBLE? DUP L#) = SWAP # = LONG? AND OR ;
+: INDEX?
+ DUP >R DUP 0770 AND A0 DI) = SWAP PCDI) = OR
+ IF DUP RD 10 * SWAP MS IF 100000 OR THEN
+ SZ40 SWAP LOW OR
+ THEN R> ;
+: MORE? DUP MS 0040 > ;
+: ,MORE MORE? IF INDEX? DOUBLE? ?, ELSE DROP THEN ;
+
+\ EXTENDED ADDRESSING EXTRAS
+
+CREATE EXTRA HERE 10 ALLOT 10 ERASE
+
+: EXTRA? MORE?
+ IF >R R@ INDEX? DOUBLE? EXTRA 1 + SWAP
+ IF 2! 2 ELSE ! 1 THEN EXTRA C! R>
+ ELSE 0 EXTRA ! THEN ;
+: ,EXTRA EXTRA C@ ?DUP
+ IF EXTRA 1 + SWAP 1 =
+ IF @ w, ELSE 2@ , THEN EXTRA 10 ERASE
+ THEN ;
+
+\ IMMEDIATE & ADDRESS REGISTER SPECIFIC INSTRUCTIONS
+
+: IMM CREATE , DOES> @ >R EXTRA? EAS R> OR SZ3 w, LONG? ?, ,EXTRA ;
+0000 IMM ORI
+1000 IMM ANDI
+2000 IMM SUBI
+3000 IMM ADDI
+5000 IMM EORI
+6000 IMM CMPI
+
+: IMMSR CREATE , DOES> @ SZ3 , ;
+001074 IMMSR ANDI>SR
+005074 IMMSR EORI>SR
+000074 IMMSR ORI>SR
+
+: IQ CREATE , DOES> @ >R EXTRA? EAS SWAP RS 1000 * OR R> OR SZ3 w, ,EXTRA ;
+050000 IQ ADDQ
+050400 IQ SUBQ
+
+: IEAA CREATE , DOES> @ DST SRC SZ4 w, ,MORE ;
+150300 IEAA ADDA
+130300 IEAA CMPA
+040700 IEAA LEA
+110300 IEAA SUBA
+
+\ SHIFTS, ROTATES, & BIT MANIPULATION
+: ISR CREATE , DOES> @ >R DN?
+ IF SWAP DN? IF R> 40 OR >R ELSE DROP SWAP 1000 * THEN
+ RD SWAP RS OR R> OR 160000 OR SZ3 w,
+ ELSE DUP EAS 300 OR R@ 400 AND OR R> 70 AND 100 * OR
+ 160000 OR w, ,MORE
+ THEN ;
+400 ISR ASL
+000 ISR ASR
+410 ISR LSL
+010 ISR LSR
+420 ISR ROXL
+020 ISR ROXR
+430 ISR ROL
+030 ISR ROR
+
+: IBIT CREATE , DOES> @ >R EXTRA? DN?
+ IF RD SRC 400 ELSE DROP DUP EAS 4000 THEN
+ OR R> OR w, ,EXTRA ,MORE ;
+000 IBIT BTST
+100 IBIT BCHG
+200 IBIT BCLR
+300 IBIT BSET
+
+\ BRANCH, LOOP, & SET CONDITIONALS
+
+: SETCLASS ' SWAP 0 DO I OVER EXECUTE LOOP DROP ;
+: SETCLAS2 ' ROT ROT DO I OVER EXECUTE LOOP DROP ;
+: IBRA 400 * 060000 OR CREATE ,
+ DOES> @ SWAP HERE 2 + - DUP ABS 200 <
+ IF LOW OR w, ELSE SWAP , THEN ;
+: IDBR 400 * 050310 OR CREATE ,
+ DOES> @ SWAP RS OR w, HERE - , ;
+: ISET 400 * 050300 OR CREATE ,
+ DOES> @ SRC w, ,MORE ;
+
+20 SETCLASS IBRA BRA BSR BHI BLS BCC BCS BNE BEQ BVC BVS BPL BMI BGE BLT BGT BLE
+
+10 SETCLASS IDBR DXIT DBRA DBHI DBLS DBCC DBCS DBNE DBEQ
+
+20 10 SETCLAS2 IDBR DBVC DBVS DBPL DBMI DBGE DBLT DBGT DBLE
+
+20 SETCLASS ISET SET SNO SHI SLS SCC SCS SNE SEQ SVC SVS SPL SMI SGE SLT SGT SLE
+
+\ MOVES
+
+: MOVE EXTRA? 7700 AND SRC SZ300 w, ,MORE ,EXTRA ;
+
+: MOVEQ RD SWAP LOW OR 070000 OR w, ;
+
+: MOVE>USP RS 047140 OR w, ;
+: MOVE<USP RS 047150 OR w, ;
+: MOVEM> EXTRA? EAS 044200 OR -SZ1 w, w, ,EXTRA ;
+: MOVEM< EXTRA? EAS 046200 OR -SZ1 w, w, ,EXTRA ;
+: MOVEP DN? IF RD SWAP RS OR 410 OR
+ ELSE RS ROT RD OR 610 OR
+ THEN -SZ1 , ;
+: LMOVE 7700 AND SWAP EAS OR 20000 OR w, ;
+
+\ ODDS AND ENDS
+
+: CMPM RD SWAP RS OR 130410 OR SZ3 w, ;
+: EXG
+ DN? IF SWAP DN? IF 140500 ELSE 140610 THEN >R
+ ELSE SWAP DN? IF 140610 ELSE 140510 THEN >R SWAP
+ THEN RS DST R> OR w, ;
+: EXT RS 044200 OR -SZ1 w, ;
+: SWAP RS 044100 OR w, ;
+: STOP 47162 , ;
+: TRAP 17 AND 47100 OR w, ;
+: LINK RS 047120 OR , ;
+: UNLK RS 047130 OR w, ;
+
+\ ARITHMETIC & LOGIC
+
+: EOR EXTRA? EAS DST SZ3 130400 OR w, ,EXTRA ;
+
+: IDD CREATE ,
+ DOES> @ DST OVER RS OR *SWAP MS IF 10 OR THEN w, ;
+
+140400 IDD ABCD
+100400 IDD SBCD
+150300 IDD ADDX
+110400 IDD SUBX
+
+: IDEA CREATE ,
+ DOES> @ >R DN?
+ IF RD SRC R> OR SZ3 w, ,MORE
+ ELSE EXTRA? EAS DST 400 OR R> OR SZ3 w, ,EXTRA THEN ;
+
+150000 IDEA ADD
+110000 IDEA SUB
+140000 IDEA AND
+100000 IDEA OR
+
+: IEAD CREATE , DOES> @ DST SRC w, ,MORE ;
+
+040600 IEAD CHK
+100300 IEAD DIVU
+100700 IEAD DIVS
+140300 IEAD MULU
+140700 IEAD MULS
+
+: CMP 130000 DST SRC SZ3 w, ,MORE ;
+
+\ ARITHMETIC & CONTROL
+
+
+: IEA CREATE , DOES> @ SRC w, ,MORE ;
+
+047200 IEA JSR
+047300 IEA JMP
+042300 IEA MOVE>CCR
+040300 IEA MOVE<SR
+043300 IEA MOVE>SR
+044000 IEA NBCD
+044100 IEA PEA
+045300 IEA TAS
+
+: IEAS CREATE , DOES> @ SRC SZ3 w, ,MORE ;
+
+041000 IEAS CLR
+043000 IEAS NOT
+042000 IEAS NEG
+040000 IEAS NEGX
+045000 IEAS TST
+
+: ICON CREATE , DOES> @ w, ;
+
+47160 ICON RESET
+47161 ICON NOP
+47163 ICON RTE
+47165 ICON RTS
+47166 ICON TRAPV
+47167 ICON RTR
+
+\ STRUCTURED CONDITIONALS ( +/- 256 BYTES )
+
+: THEN HERE OVER 2 + - *SWAP 1 + C! ;
+: ENDIF THEN ;
+: IF w, HERE 2 - ;
+
+HEX
+
+: ELSE 6000 IF *SWAP THEN ;
+: BEGIN HERE ;
+: UNTIL , HERE - HERE 1 - C! ;
+: AGAIN 6000 UNTIL ;
+: WHILE IF ;
+: REPEAT *SWAP AGAIN THEN ;
+: DO HERE *SWAP ;
+: LOOP DBRA ;
+
+6600 CONSTANT 0=
+6700 CONSTANT 0<>
+6A00 CONSTANT 0<
+6B00 CONSTANT 0>=
+6C00 CONSTANT <
+6D00 CONSTANT >=
+6E00 CONSTANT <=
+6F00 CONSTANT >
+
+DECIMAL
+
+: NEXT
+ A5 )+ A0 LMOVE
+ A0 ) JMP ;
+
+FORTH DEFINITIONS
+
+: LABEL CREATE [COMPILE] ASSEMBLER ASSEMBLER WORD ;
+: CODE LABEL HERE CELL- CELL- CELL- CP ! ;
+
+
+
+
+--openmail-part-01d4752f-00000002--
+
+--openmail-part-01d4752f-00000001--
+
+
diff --git a/test/core.fr b/test/core.fr
new file mode 100644
index 000000000000..39bc181c1203
--- /dev/null
+++ b/test/core.fr
@@ -0,0 +1,997 @@
+\ From: John Hayes S1I
+\ Subject: core.fr
+\ Date: Mon, 27 Nov 95 13:10
+
+\ (C) 1995 JOHNS HOPKINS UNIVERSITY / APPLIED PHYSICS LABORATORY
+\ MAY BE DISTRIBUTED FREELY AS LONG AS THIS COPYRIGHT NOTICE REMAINS.
+\ VERSION 1.2
+\ THIS PROGRAM TESTS THE CORE WORDS OF AN ANS FORTH SYSTEM.
+\ THE PROGRAM ASSUMES A TWO'S COMPLEMENT IMPLEMENTATION WHERE
+\ THE RANGE OF SIGNED NUMBERS IS -2^(N-1) ... 2^(N-1)-1 AND
+\ THE RANGE OF UNSIGNED NUMBERS IS 0 ... 2^(N)-1.
+\ I HAVEN'T FIGURED OUT HOW TO TEST KEY, QUIT, ABORT, OR ABORT"...
+\ I ALSO HAVEN'T THOUGHT OF A WAY TO TEST ENVIRONMENT?...
+
+TESTING CORE WORDS
+HEX
+
+\ ------------------------------------------------------------------------
+TESTING BASIC ASSUMPTIONS
+
+{ -> } \ START WITH CLEAN SLATE
+( TEST IF ANY BITS ARE SET; ANSWER IN BASE 1 )
+{ : BITSSET? IF 0 0 ELSE 0 THEN ; -> }
+{ 0 BITSSET? -> 0 } ( ZERO IS ALL BITS CLEAR )
+{ 1 BITSSET? -> 0 0 } ( OTHER NUMBER HAVE AT LEAST ONE BIT )
+{ -1 BITSSET? -> 0 0 }
+
+\ ------------------------------------------------------------------------
+TESTING BOOLEANS: INVERT AND OR XOR
+
+{ 0 0 AND -> 0 }
+{ 0 1 AND -> 0 }
+{ 1 0 AND -> 0 }
+{ 1 1 AND -> 1 }
+
+{ 0 INVERT 1 AND -> 1 }
+{ 1 INVERT 1 AND -> 0 }
+
+0 CONSTANT 0S
+0 INVERT CONSTANT 1S
+
+{ 0S INVERT -> 1S }
+{ 1S INVERT -> 0S }
+
+{ 0S 0S AND -> 0S }
+{ 0S 1S AND -> 0S }
+{ 1S 0S AND -> 0S }
+{ 1S 1S AND -> 1S }
+
+{ 0S 0S OR -> 0S }
+{ 0S 1S OR -> 1S }
+{ 1S 0S OR -> 1S }
+{ 1S 1S OR -> 1S }
+
+{ 0S 0S XOR -> 0S }
+{ 0S 1S XOR -> 1S }
+{ 1S 0S XOR -> 1S }
+{ 1S 1S XOR -> 0S }
+
+\ ------------------------------------------------------------------------
+TESTING 2* 2/ LSHIFT RSHIFT
+
+( WE TRUST 1S, INVERT, AND BITSSET?; WE WILL CONFIRM RSHIFT LATER )
+1S 1 RSHIFT INVERT CONSTANT MSB
+{ MSB BITSSET? -> 0 0 }
+
+{ 0S 2* -> 0S }
+{ 1 2* -> 2 }
+{ 4000 2* -> 8000 }
+{ 1S 2* 1 XOR -> 1S }
+{ MSB 2* -> 0S }
+
+{ 0S 2/ -> 0S }
+{ 1 2/ -> 0 }
+{ 4000 2/ -> 2000 }
+{ 1S 2/ -> 1S } \ MSB PROPOGATED
+{ 1S 1 XOR 2/ -> 1S }
+{ MSB 2/ MSB AND -> MSB }
+
+{ 1 0 LSHIFT -> 1 }
+{ 1 1 LSHIFT -> 2 }
+{ 1 2 LSHIFT -> 4 }
+{ 1 F LSHIFT -> 8000 } \ BIGGEST GUARANTEED SHIFT
+{ 1S 1 LSHIFT 1 XOR -> 1S }
+{ MSB 1 LSHIFT -> 0 }
+
+{ 1 0 RSHIFT -> 1 }
+{ 1 1 RSHIFT -> 0 }
+{ 2 1 RSHIFT -> 1 }
+{ 4 2 RSHIFT -> 1 }
+{ 8000 F RSHIFT -> 1 } \ BIGGEST
+{ MSB 1 RSHIFT MSB AND -> 0 } \ RSHIFT ZERO FILLS MSBS
+{ MSB 1 RSHIFT 2* -> MSB }
+
+\ ------------------------------------------------------------------------
+TESTING COMPARISONS: 0= = 0< < > U< MIN MAX
+0 INVERT CONSTANT MAX-UINT
+0 INVERT 1 RSHIFT CONSTANT MAX-INT
+0 INVERT 1 RSHIFT INVERT CONSTANT MIN-INT
+0 INVERT 1 RSHIFT CONSTANT MID-UINT
+0 INVERT 1 RSHIFT INVERT CONSTANT MID-UINT+1
+
+0S CONSTANT <FALSE>
+1S CONSTANT <TRUE>
+
+{ 0 0= -> <TRUE> }
+{ 1 0= -> <FALSE> }
+{ 2 0= -> <FALSE> }
+{ -1 0= -> <FALSE> }
+{ MAX-UINT 0= -> <FALSE> }
+{ MIN-INT 0= -> <FALSE> }
+{ MAX-INT 0= -> <FALSE> }
+
+{ 0 0 = -> <TRUE> }
+{ 1 1 = -> <TRUE> }
+{ -1 -1 = -> <TRUE> }
+{ 1 0 = -> <FALSE> }
+{ -1 0 = -> <FALSE> }
+{ 0 1 = -> <FALSE> }
+{ 0 -1 = -> <FALSE> }
+
+{ 0 0< -> <FALSE> }
+{ -1 0< -> <TRUE> }
+{ MIN-INT 0< -> <TRUE> }
+{ 1 0< -> <FALSE> }
+{ MAX-INT 0< -> <FALSE> }
+
+{ 0 1 < -> <TRUE> }
+{ 1 2 < -> <TRUE> }
+{ -1 0 < -> <TRUE> }
+{ -1 1 < -> <TRUE> }
+{ MIN-INT 0 < -> <TRUE> }
+{ MIN-INT MAX-INT < -> <TRUE> }
+{ 0 MAX-INT < -> <TRUE> }
+{ 0 0 < -> <FALSE> }
+{ 1 1 < -> <FALSE> }
+{ 1 0 < -> <FALSE> }
+{ 2 1 < -> <FALSE> }
+{ 0 -1 < -> <FALSE> }
+{ 1 -1 < -> <FALSE> }
+{ 0 MIN-INT < -> <FALSE> }
+{ MAX-INT MIN-INT < -> <FALSE> }
+{ MAX-INT 0 < -> <FALSE> }
+
+{ 0 1 > -> <FALSE> }
+{ 1 2 > -> <FALSE> }
+{ -1 0 > -> <FALSE> }
+{ -1 1 > -> <FALSE> }
+{ MIN-INT 0 > -> <FALSE> }
+{ MIN-INT MAX-INT > -> <FALSE> }
+{ 0 MAX-INT > -> <FALSE> }
+{ 0 0 > -> <FALSE> }
+{ 1 1 > -> <FALSE> }
+{ 1 0 > -> <TRUE> }
+{ 2 1 > -> <TRUE> }
+{ 0 -1 > -> <TRUE> }
+{ 1 -1 > -> <TRUE> }
+{ 0 MIN-INT > -> <TRUE> }
+{ MAX-INT MIN-INT > -> <TRUE> }
+{ MAX-INT 0 > -> <TRUE> }
+
+{ 0 1 U< -> <TRUE> }
+{ 1 2 U< -> <TRUE> }
+{ 0 MID-UINT U< -> <TRUE> }
+{ 0 MAX-UINT U< -> <TRUE> }
+{ MID-UINT MAX-UINT U< -> <TRUE> }
+{ 0 0 U< -> <FALSE> }
+{ 1 1 U< -> <FALSE> }
+{ 1 0 U< -> <FALSE> }
+{ 2 1 U< -> <FALSE> }
+{ MID-UINT 0 U< -> <FALSE> }
+{ MAX-UINT 0 U< -> <FALSE> }
+{ MAX-UINT MID-UINT U< -> <FALSE> }
+
+{ 0 1 MIN -> 0 }
+{ 1 2 MIN -> 1 }
+{ -1 0 MIN -> -1 }
+{ -1 1 MIN -> -1 }
+{ MIN-INT 0 MIN -> MIN-INT }
+{ MIN-INT MAX-INT MIN -> MIN-INT }
+{ 0 MAX-INT MIN -> 0 }
+{ 0 0 MIN -> 0 }
+{ 1 1 MIN -> 1 }
+{ 1 0 MIN -> 0 }
+{ 2 1 MIN -> 1 }
+{ 0 -1 MIN -> -1 }
+{ 1 -1 MIN -> -1 }
+{ 0 MIN-INT MIN -> MIN-INT }
+{ MAX-INT MIN-INT MIN -> MIN-INT }
+{ MAX-INT 0 MIN -> 0 }
+
+{ 0 1 MAX -> 1 }
+{ 1 2 MAX -> 2 }
+{ -1 0 MAX -> 0 }
+{ -1 1 MAX -> 1 }
+{ MIN-INT 0 MAX -> 0 }
+{ MIN-INT MAX-INT MAX -> MAX-INT }
+{ 0 MAX-INT MAX -> MAX-INT }
+{ 0 0 MAX -> 0 }
+{ 1 1 MAX -> 1 }
+{ 1 0 MAX -> 1 }
+{ 2 1 MAX -> 2 }
+{ 0 -1 MAX -> 0 }
+{ 1 -1 MAX -> 1 }
+{ 0 MIN-INT MAX -> 0 }
+{ MAX-INT MIN-INT MAX -> MAX-INT }
+{ MAX-INT 0 MAX -> MAX-INT }
+
+\ ------------------------------------------------------------------------
+TESTING STACK OPS: 2DROP 2DUP 2OVER 2SWAP ?DUP DEPTH DROP DUP OVER ROT SWAP
+
+{ 1 2 2DROP -> }
+{ 1 2 2DUP -> 1 2 1 2 }
+{ 1 2 3 4 2OVER -> 1 2 3 4 1 2 }
+{ 1 2 3 4 2SWAP -> 3 4 1 2 }
+{ 0 ?DUP -> 0 }
+{ 1 ?DUP -> 1 1 }
+{ -1 ?DUP -> -1 -1 }
+{ DEPTH -> 0 }
+{ 0 DEPTH -> 0 1 }
+{ 0 1 DEPTH -> 0 1 2 }
+{ 0 DROP -> }
+{ 1 2 DROP -> 1 }
+{ 1 DUP -> 1 1 }
+{ 1 2 OVER -> 1 2 1 }
+{ 1 2 3 ROT -> 2 3 1 }
+{ 1 2 SWAP -> 2 1 }
+
+\ ------------------------------------------------------------------------
+TESTING >R R> R@
+
+{ : GR1 >R R> ; -> }
+{ : GR2 >R R@ R> DROP ; -> }
+{ 123 GR1 -> 123 }
+{ 123 GR2 -> 123 }
+{ 1S GR1 -> 1S } ( RETURN STACK HOLDS CELLS )
+
+\ ------------------------------------------------------------------------
+TESTING ADD/SUBTRACT: + - 1+ 1- ABS NEGATE
+
+{ 0 5 + -> 5 }
+{ 5 0 + -> 5 }
+{ 0 -5 + -> -5 }
+{ -5 0 + -> -5 }
+{ 1 2 + -> 3 }
+{ 1 -2 + -> -1 }
+{ -1 2 + -> 1 }
+{ -1 -2 + -> -3 }
+{ -1 1 + -> 0 }
+{ MID-UINT 1 + -> MID-UINT+1 }
+
+{ 0 5 - -> -5 }
+{ 5 0 - -> 5 }
+{ 0 -5 - -> 5 }
+{ -5 0 - -> -5 }
+{ 1 2 - -> -1 }
+{ 1 -2 - -> 3 }
+{ -1 2 - -> -3 }
+{ -1 -2 - -> 1 }
+{ 0 1 - -> -1 }
+{ MID-UINT+1 1 - -> MID-UINT }
+
+{ 0 1+ -> 1 }
+{ -1 1+ -> 0 }
+{ 1 1+ -> 2 }
+{ MID-UINT 1+ -> MID-UINT+1 }
+
+{ 2 1- -> 1 }
+{ 1 1- -> 0 }
+{ 0 1- -> -1 }
+{ MID-UINT+1 1- -> MID-UINT }
+
+{ 0 NEGATE -> 0 }
+{ 1 NEGATE -> -1 }
+{ -1 NEGATE -> 1 }
+{ 2 NEGATE -> -2 }
+{ -2 NEGATE -> 2 }
+
+{ 0 ABS -> 0 }
+{ 1 ABS -> 1 }
+{ -1 ABS -> 1 }
+{ MIN-INT ABS -> MID-UINT+1 }
+
+\ ------------------------------------------------------------------------
+TESTING MULTIPLY: S>D * M* UM*
+
+{ 0 S>D -> 0 0 }
+{ 1 S>D -> 1 0 }
+{ 2 S>D -> 2 0 }
+{ -1 S>D -> -1 -1 }
+{ -2 S>D -> -2 -1 }
+{ MIN-INT S>D -> MIN-INT -1 }
+{ MAX-INT S>D -> MAX-INT 0 }
+
+{ 0 0 M* -> 0 S>D }
+{ 0 1 M* -> 0 S>D }
+{ 1 0 M* -> 0 S>D }
+{ 1 2 M* -> 2 S>D }
+{ 2 1 M* -> 2 S>D }
+{ 3 3 M* -> 9 S>D }
+{ -3 3 M* -> -9 S>D }
+{ 3 -3 M* -> -9 S>D }
+{ -3 -3 M* -> 9 S>D }
+{ 0 MIN-INT M* -> 0 S>D }
+{ 1 MIN-INT M* -> MIN-INT S>D }
+{ 2 MIN-INT M* -> 0 1S }
+{ 0 MAX-INT M* -> 0 S>D }
+{ 1 MAX-INT M* -> MAX-INT S>D }
+{ 2 MAX-INT M* -> MAX-INT 1 LSHIFT 0 }
+{ MIN-INT MIN-INT M* -> 0 MSB 1 RSHIFT }
+{ MAX-INT MIN-INT M* -> MSB MSB 2/ }
+{ MAX-INT MAX-INT M* -> 1 MSB 2/ INVERT }
+
+{ 0 0 * -> 0 } \ TEST IDENTITIES
+{ 0 1 * -> 0 }
+{ 1 0 * -> 0 }
+{ 1 2 * -> 2 }
+{ 2 1 * -> 2 }
+{ 3 3 * -> 9 }
+{ -3 3 * -> -9 }
+{ 3 -3 * -> -9 }
+{ -3 -3 * -> 9 }
+
+{ MID-UINT+1 1 RSHIFT 2 * -> MID-UINT+1 }
+{ MID-UINT+1 2 RSHIFT 4 * -> MID-UINT+1 }
+{ MID-UINT+1 1 RSHIFT MID-UINT+1 OR 2 * -> MID-UINT+1 }
+
+{ 0 0 UM* -> 0 0 }
+{ 0 1 UM* -> 0 0 }
+{ 1 0 UM* -> 0 0 }
+{ 1 2 UM* -> 2 0 }
+{ 2 1 UM* -> 2 0 }
+{ 3 3 UM* -> 9 0 }
+
+{ MID-UINT+1 1 RSHIFT 2 UM* -> MID-UINT+1 0 }
+{ MID-UINT+1 2 UM* -> 0 1 }
+{ MID-UINT+1 4 UM* -> 0 2 }
+{ 1S 2 UM* -> 1S 1 LSHIFT 1 }
+{ MAX-UINT MAX-UINT UM* -> 1 1 INVERT }
+
+\ ------------------------------------------------------------------------
+TESTING DIVIDE: FM/MOD SM/REM UM/MOD */ */MOD / /MOD MOD
+
+{ 0 S>D 1 FM/MOD -> 0 0 }
+{ 1 S>D 1 FM/MOD -> 0 1 }
+{ 2 S>D 1 FM/MOD -> 0 2 }
+{ -1 S>D 1 FM/MOD -> 0 -1 }
+{ -2 S>D 1 FM/MOD -> 0 -2 }
+{ 0 S>D -1 FM/MOD -> 0 0 }
+{ 1 S>D -1 FM/MOD -> 0 -1 }
+{ 2 S>D -1 FM/MOD -> 0 -2 }
+{ -1 S>D -1 FM/MOD -> 0 1 }
+{ -2 S>D -1 FM/MOD -> 0 2 }
+{ 2 S>D 2 FM/MOD -> 0 1 }
+{ -1 S>D -1 FM/MOD -> 0 1 }
+{ -2 S>D -2 FM/MOD -> 0 1 }
+{ 7 S>D 3 FM/MOD -> 1 2 }
+{ 7 S>D -3 FM/MOD -> -2 -3 }
+{ -7 S>D 3 FM/MOD -> 2 -3 }
+{ -7 S>D -3 FM/MOD -> -1 2 }
+{ MAX-INT S>D 1 FM/MOD -> 0 MAX-INT }
+{ MIN-INT S>D 1 FM/MOD -> 0 MIN-INT }
+{ MAX-INT S>D MAX-INT FM/MOD -> 0 1 }
+{ MIN-INT S>D MIN-INT FM/MOD -> 0 1 }
+{ 1S 1 4 FM/MOD -> 3 MAX-INT }
+{ 1 MIN-INT M* 1 FM/MOD -> 0 MIN-INT }
+{ 1 MIN-INT M* MIN-INT FM/MOD -> 0 1 }
+{ 2 MIN-INT M* 2 FM/MOD -> 0 MIN-INT }
+{ 2 MIN-INT M* MIN-INT FM/MOD -> 0 2 }
+{ 1 MAX-INT M* 1 FM/MOD -> 0 MAX-INT }
+{ 1 MAX-INT M* MAX-INT FM/MOD -> 0 1 }
+{ 2 MAX-INT M* 2 FM/MOD -> 0 MAX-INT }
+{ 2 MAX-INT M* MAX-INT FM/MOD -> 0 2 }
+{ MIN-INT MIN-INT M* MIN-INT FM/MOD -> 0 MIN-INT }
+{ MIN-INT MAX-INT M* MIN-INT FM/MOD -> 0 MAX-INT }
+{ MIN-INT MAX-INT M* MAX-INT FM/MOD -> 0 MIN-INT }
+{ MAX-INT MAX-INT M* MAX-INT FM/MOD -> 0 MAX-INT }
+
+{ 0 S>D 1 SM/REM -> 0 0 }
+{ 1 S>D 1 SM/REM -> 0 1 }
+{ 2 S>D 1 SM/REM -> 0 2 }
+{ -1 S>D 1 SM/REM -> 0 -1 }
+{ -2 S>D 1 SM/REM -> 0 -2 }
+{ 0 S>D -1 SM/REM -> 0 0 }
+{ 1 S>D -1 SM/REM -> 0 -1 }
+{ 2 S>D -1 SM/REM -> 0 -2 }
+{ -1 S>D -1 SM/REM -> 0 1 }
+{ -2 S>D -1 SM/REM -> 0 2 }
+{ 2 S>D 2 SM/REM -> 0 1 }
+{ -1 S>D -1 SM/REM -> 0 1 }
+{ -2 S>D -2 SM/REM -> 0 1 }
+{ 7 S>D 3 SM/REM -> 1 2 }
+{ 7 S>D -3 SM/REM -> 1 -2 }
+{ -7 S>D 3 SM/REM -> -1 -2 }
+{ -7 S>D -3 SM/REM -> -1 2 }
+{ MAX-INT S>D 1 SM/REM -> 0 MAX-INT }
+{ MIN-INT S>D 1 SM/REM -> 0 MIN-INT }
+{ MAX-INT S>D MAX-INT SM/REM -> 0 1 }
+{ MIN-INT S>D MIN-INT SM/REM -> 0 1 }
+{ 1S 1 4 SM/REM -> 3 MAX-INT }
+{ 2 MIN-INT M* 2 SM/REM -> 0 MIN-INT }
+{ 2 MIN-INT M* MIN-INT SM/REM -> 0 2 }
+{ 2 MAX-INT M* 2 SM/REM -> 0 MAX-INT }
+{ 2 MAX-INT M* MAX-INT SM/REM -> 0 2 }
+{ MIN-INT MIN-INT M* MIN-INT SM/REM -> 0 MIN-INT }
+{ MIN-INT MAX-INT M* MIN-INT SM/REM -> 0 MAX-INT }
+{ MIN-INT MAX-INT M* MAX-INT SM/REM -> 0 MIN-INT }
+{ MAX-INT MAX-INT M* MAX-INT SM/REM -> 0 MAX-INT }
+
+{ 0 0 1 UM/MOD -> 0 0 }
+{ 1 0 1 UM/MOD -> 0 1 }
+{ 1 0 2 UM/MOD -> 1 0 }
+{ 3 0 2 UM/MOD -> 1 1 }
+{ MAX-UINT 2 UM* 2 UM/MOD -> 0 MAX-UINT }
+{ MAX-UINT 2 UM* MAX-UINT UM/MOD -> 0 2 }
+{ MAX-UINT MAX-UINT UM* MAX-UINT UM/MOD -> 0 MAX-UINT }
+
+: IFFLOORED
+ [ -3 2 / -2 = INVERT ] LITERAL IF POSTPONE \ THEN ;
+: IFSYM
+ [ -3 2 / -1 = INVERT ] LITERAL IF POSTPONE \ THEN ;
+
+\ THE SYSTEM MIGHT DO EITHER FLOORED OR SYMMETRIC DIVISION.
+\ SINCE WE HAVE ALREADY TESTED M*, FM/MOD, AND SM/REM WE CAN USE THEM IN TEST.
+IFFLOORED : T/MOD >R S>D R> FM/MOD ;
+IFFLOORED : T/ T/MOD SWAP DROP ;
+IFFLOORED : TMOD T/MOD DROP ;
+IFFLOORED : T*/MOD >R M* R> FM/MOD ;
+IFFLOORED : T*/ T*/MOD SWAP DROP ;
+IFSYM : T/MOD >R S>D R> SM/REM ;
+IFSYM : T/ T/MOD SWAP DROP ;
+IFSYM : TMOD T/MOD DROP ;
+IFSYM : T*/MOD >R M* R> SM/REM ;
+IFSYM : T*/ T*/MOD SWAP DROP ;
+
+{ 0 1 /MOD -> 0 1 T/MOD }
+{ 1 1 /MOD -> 1 1 T/MOD }
+{ 2 1 /MOD -> 2 1 T/MOD }
+{ -1 1 /MOD -> -1 1 T/MOD }
+{ -2 1 /MOD -> -2 1 T/MOD }
+{ 0 -1 /MOD -> 0 -1 T/MOD }
+{ 1 -1 /MOD -> 1 -1 T/MOD }
+{ 2 -1 /MOD -> 2 -1 T/MOD }
+{ -1 -1 /MOD -> -1 -1 T/MOD }
+{ -2 -1 /MOD -> -2 -1 T/MOD }
+{ 2 2 /MOD -> 2 2 T/MOD }
+{ -1 -1 /MOD -> -1 -1 T/MOD }
+{ -2 -2 /MOD -> -2 -2 T/MOD }
+{ 7 3 /MOD -> 7 3 T/MOD }
+{ 7 -3 /MOD -> 7 -3 T/MOD }
+{ -7 3 /MOD -> -7 3 T/MOD }
+{ -7 -3 /MOD -> -7 -3 T/MOD }
+{ MAX-INT 1 /MOD -> MAX-INT 1 T/MOD }
+{ MIN-INT 1 /MOD -> MIN-INT 1 T/MOD }
+{ MAX-INT MAX-INT /MOD -> MAX-INT MAX-INT T/MOD }
+{ MIN-INT MIN-INT /MOD -> MIN-INT MIN-INT T/MOD }
+
+{ 0 1 / -> 0 1 T/ }
+{ 1 1 / -> 1 1 T/ }
+{ 2 1 / -> 2 1 T/ }
+{ -1 1 / -> -1 1 T/ }
+{ -2 1 / -> -2 1 T/ }
+{ 0 -1 / -> 0 -1 T/ }
+{ 1 -1 / -> 1 -1 T/ }
+{ 2 -1 / -> 2 -1 T/ }
+{ -1 -1 / -> -1 -1 T/ }
+{ -2 -1 / -> -2 -1 T/ }
+{ 2 2 / -> 2 2 T/ }
+{ -1 -1 / -> -1 -1 T/ }
+{ -2 -2 / -> -2 -2 T/ }
+{ 7 3 / -> 7 3 T/ }
+{ 7 -3 / -> 7 -3 T/ }
+{ -7 3 / -> -7 3 T/ }
+{ -7 -3 / -> -7 -3 T/ }
+{ MAX-INT 1 / -> MAX-INT 1 T/ }
+{ MIN-INT 1 / -> MIN-INT 1 T/ }
+{ MAX-INT MAX-INT / -> MAX-INT MAX-INT T/ }
+{ MIN-INT MIN-INT / -> MIN-INT MIN-INT T/ }
+
+{ 0 1 MOD -> 0 1 TMOD }
+{ 1 1 MOD -> 1 1 TMOD }
+{ 2 1 MOD -> 2 1 TMOD }
+{ -1 1 MOD -> -1 1 TMOD }
+{ -2 1 MOD -> -2 1 TMOD }
+{ 0 -1 MOD -> 0 -1 TMOD }
+{ 1 -1 MOD -> 1 -1 TMOD }
+{ 2 -1 MOD -> 2 -1 TMOD }
+{ -1 -1 MOD -> -1 -1 TMOD }
+{ -2 -1 MOD -> -2 -1 TMOD }
+{ 2 2 MOD -> 2 2 TMOD }
+{ -1 -1 MOD -> -1 -1 TMOD }
+{ -2 -2 MOD -> -2 -2 TMOD }
+{ 7 3 MOD -> 7 3 TMOD }
+{ 7 -3 MOD -> 7 -3 TMOD }
+{ -7 3 MOD -> -7 3 TMOD }
+{ -7 -3 MOD -> -7 -3 TMOD }
+{ MAX-INT 1 MOD -> MAX-INT 1 TMOD }
+{ MIN-INT 1 MOD -> MIN-INT 1 TMOD }
+{ MAX-INT MAX-INT MOD -> MAX-INT MAX-INT TMOD }
+{ MIN-INT MIN-INT MOD -> MIN-INT MIN-INT TMOD }
+
+{ 0 2 1 */ -> 0 2 1 T*/ }
+{ 1 2 1 */ -> 1 2 1 T*/ }
+{ 2 2 1 */ -> 2 2 1 T*/ }
+{ -1 2 1 */ -> -1 2 1 T*/ }
+{ -2 2 1 */ -> -2 2 1 T*/ }
+{ 0 2 -1 */ -> 0 2 -1 T*/ }
+{ 1 2 -1 */ -> 1 2 -1 T*/ }
+{ 2 2 -1 */ -> 2 2 -1 T*/ }
+{ -1 2 -1 */ -> -1 2 -1 T*/ }
+{ -2 2 -1 */ -> -2 2 -1 T*/ }
+{ 2 2 2 */ -> 2 2 2 T*/ }
+{ -1 2 -1 */ -> -1 2 -1 T*/ }
+{ -2 2 -2 */ -> -2 2 -2 T*/ }
+{ 7 2 3 */ -> 7 2 3 T*/ }
+{ 7 2 -3 */ -> 7 2 -3 T*/ }
+{ -7 2 3 */ -> -7 2 3 T*/ }
+{ -7 2 -3 */ -> -7 2 -3 T*/ }
+{ MAX-INT 2 MAX-INT */ -> MAX-INT 2 MAX-INT T*/ }
+{ MIN-INT 2 MIN-INT */ -> MIN-INT 2 MIN-INT T*/ }
+
+{ 0 2 1 */MOD -> 0 2 1 T*/MOD }
+{ 1 2 1 */MOD -> 1 2 1 T*/MOD }
+{ 2 2 1 */MOD -> 2 2 1 T*/MOD }
+{ -1 2 1 */MOD -> -1 2 1 T*/MOD }
+{ -2 2 1 */MOD -> -2 2 1 T*/MOD }
+{ 0 2 -1 */MOD -> 0 2 -1 T*/MOD }
+{ 1 2 -1 */MOD -> 1 2 -1 T*/MOD }
+{ 2 2 -1 */MOD -> 2 2 -1 T*/MOD }
+{ -1 2 -1 */MOD -> -1 2 -1 T*/MOD }
+{ -2 2 -1 */MOD -> -2 2 -1 T*/MOD }
+{ 2 2 2 */MOD -> 2 2 2 T*/MOD }
+{ -1 2 -1 */MOD -> -1 2 -1 T*/MOD }
+{ -2 2 -2 */MOD -> -2 2 -2 T*/MOD }
+{ 7 2 3 */MOD -> 7 2 3 T*/MOD }
+{ 7 2 -3 */MOD -> 7 2 -3 T*/MOD }
+{ -7 2 3 */MOD -> -7 2 3 T*/MOD }
+{ -7 2 -3 */MOD -> -7 2 -3 T*/MOD }
+{ MAX-INT 2 MAX-INT */MOD -> MAX-INT 2 MAX-INT T*/MOD }
+{ MIN-INT 2 MIN-INT */MOD -> MIN-INT 2 MIN-INT T*/MOD }
+
+\ ------------------------------------------------------------------------
+TESTING HERE , @ ! CELL+ CELLS C, C@ C! CHARS 2@ 2! ALIGN ALIGNED +! ALLOT
+
+HERE 1 ALLOT
+HERE
+CONSTANT 2NDA
+CONSTANT 1STA
+{ 1STA 2NDA U< -> <TRUE> } \ HERE MUST GROW WITH ALLOT
+{ 1STA 1+ -> 2NDA } \ ... BY ONE ADDRESS UNIT
+( MISSING TEST: NEGATIVE ALLOT )
+
+HERE 1 ,
+HERE 2 ,
+CONSTANT 2ND
+CONSTANT 1ST
+{ 1ST 2ND U< -> <TRUE> } \ HERE MUST GROW WITH ALLOT
+{ 1ST CELL+ -> 2ND } \ ... BY ONE CELL
+{ 1ST 1 CELLS + -> 2ND }
+{ 1ST @ 2ND @ -> 1 2 }
+{ 5 1ST ! -> }
+{ 1ST @ 2ND @ -> 5 2 }
+{ 6 2ND ! -> }
+{ 1ST @ 2ND @ -> 5 6 }
+{ 1ST 2@ -> 6 5 }
+{ 2 1 1ST 2! -> }
+{ 1ST 2@ -> 2 1 }
+{ 1S 1ST ! 1ST @ -> 1S } \ CAN STORE CELL-WIDE VALUE
+
+HERE 1 C,
+HERE 2 C,
+CONSTANT 2NDC
+CONSTANT 1STC
+{ 1STC 2NDC U< -> <TRUE> } \ HERE MUST GROW WITH ALLOT
+{ 1STC CHAR+ -> 2NDC } \ ... BY ONE CHAR
+{ 1STC 1 CHARS + -> 2NDC }
+{ 1STC C@ 2NDC C@ -> 1 2 }
+{ 3 1STC C! -> }
+{ 1STC C@ 2NDC C@ -> 3 2 }
+{ 4 2NDC C! -> }
+{ 1STC C@ 2NDC C@ -> 3 4 }
+
+ALIGN 1 ALLOT HERE ALIGN HERE 3 CELLS ALLOT
+CONSTANT A-ADDR CONSTANT UA-ADDR
+{ UA-ADDR ALIGNED -> A-ADDR }
+{ 1 A-ADDR C! A-ADDR C@ -> 1 }
+{ 1234 A-ADDR ! A-ADDR @ -> 1234 }
+{ 123 456 A-ADDR 2! A-ADDR 2@ -> 123 456 }
+{ 2 A-ADDR CHAR+ C! A-ADDR CHAR+ C@ -> 2 }
+{ 3 A-ADDR CELL+ C! A-ADDR CELL+ C@ -> 3 }
+{ 1234 A-ADDR CELL+ ! A-ADDR CELL+ @ -> 1234 }
+{ 123 456 A-ADDR CELL+ 2! A-ADDR CELL+ 2@ -> 123 456 }
+
+: BITS ( X -- U )
+ 0 SWAP BEGIN DUP WHILE DUP MSB AND IF >R 1+ R> THEN 2* REPEAT DROP ;
+( CHARACTERS >= 1 AU, <= SIZE OF CELL, >= 8 BITS )
+{ 1 CHARS 1 < -> <FALSE> }
+{ 1 CHARS 1 CELLS > -> <FALSE> }
+( TBD: HOW TO FIND NUMBER OF BITS? )
+
+( CELLS >= 1 AU, INTEGRAL MULTIPLE OF CHAR SIZE, >= 16 BITS )
+{ 1 CELLS 1 < -> <FALSE> }
+{ 1 CELLS 1 CHARS MOD -> 0 }
+{ 1S BITS 10 < -> <FALSE> }
+
+{ 0 1ST ! -> }
+{ 1 1ST +! -> }
+{ 1ST @ -> 1 }
+{ -1 1ST +! 1ST @ -> 0 }
+
+\ ------------------------------------------------------------------------
+TESTING CHAR [CHAR] [ ] BL S"
+
+{ BL -> 20 }
+{ CHAR X -> 58 }
+{ CHAR HELLO -> 48 }
+{ : GC1 [CHAR] X ; -> }
+{ : GC2 [CHAR] HELLO ; -> }
+{ GC1 -> 58 }
+{ GC2 -> 48 }
+{ : GC3 [ GC1 ] LITERAL ; -> }
+{ GC3 -> 58 }
+{ : GC4 S" XY" ; -> }
+{ GC4 SWAP DROP -> 2 }
+{ GC4 DROP DUP C@ SWAP CHAR+ C@ -> 58 59 }
+
+\ ------------------------------------------------------------------------
+TESTING ' ['] FIND EXECUTE IMMEDIATE COUNT LITERAL POSTPONE STATE
+
+{ : GT1 123 ; -> }
+{ ' GT1 EXECUTE -> 123 }
+{ : GT2 ['] GT1 ; IMMEDIATE -> }
+{ GT2 EXECUTE -> 123 }
+
+HERE 3 C, CHAR G C, CHAR T C, CHAR 1 C, CONSTANT GT1STRING
+HERE 3 C, CHAR G C, CHAR T C, CHAR 2 C, CONSTANT GT2STRING
+
+{ GT1STRING FIND -> ' GT1 -1 }
+{ GT2STRING FIND -> ' GT2 1 }
+( HOW TO SEARCH FOR NON-EXISTENT WORD? )
+{ : GT3 GT2 LITERAL ; -> }
+{ GT3 -> ' GT1 }
+{ GT1STRING COUNT -> GT1STRING CHAR+ 3 }
+
+{ : GT4 POSTPONE GT1 ; IMMEDIATE -> }
+{ : GT5 GT4 ; -> }
+{ GT5 -> 123 }
+{ : GT6 345 ; IMMEDIATE -> }
+{ : GT7 POSTPONE GT6 ; -> }
+{ GT7 -> 345 }
+
+{ : GT8 STATE @ ; IMMEDIATE -> }
+{ GT8 -> 0 }
+{ : GT9 GT8 LITERAL ; -> }
+{ GT9 0= -> <FALSE> }
+
+\ ------------------------------------------------------------------------
+TESTING IF ELSE THEN BEGIN WHILE REPEAT UNTIL RECURSE
+
+{ : GI1 IF 123 THEN ; -> }
+{ : GI2 IF 123 ELSE 234 THEN ; -> }
+{ 0 GI1 -> }
+{ 1 GI1 -> 123 }
+{ -1 GI1 -> 123 }
+{ 0 GI2 -> 234 }
+{ 1 GI2 -> 123 }
+{ -1 GI1 -> 123 }
+
+{ : GI3 BEGIN DUP 5 < WHILE DUP 1+ REPEAT ; -> }
+{ 0 GI3 -> 0 1 2 3 4 5 }
+{ 4 GI3 -> 4 5 }
+{ 5 GI3 -> 5 }
+{ 6 GI3 -> 6 }
+
+{ : GI4 BEGIN DUP 1+ DUP 5 > UNTIL ; -> }
+{ 3 GI4 -> 3 4 5 6 }
+{ 5 GI4 -> 5 6 }
+{ 6 GI4 -> 6 7 }
+
+{ : GI5 BEGIN DUP 2 > WHILE DUP 5 < WHILE DUP 1+ REPEAT 123 ELSE 345 THEN ; -> }
+{ 1 GI5 -> 1 345 }
+{ 2 GI5 -> 2 345 }
+{ 3 GI5 -> 3 4 5 123 }
+{ 4 GI5 -> 4 5 123 }
+{ 5 GI5 -> 5 123 }
+
+{ : GI6 ( N -- 0,1,..N ) DUP IF DUP >R 1- RECURSE R> THEN ; -> }
+{ 0 GI6 -> 0 }
+{ 1 GI6 -> 0 1 }
+{ 2 GI6 -> 0 1 2 }
+{ 3 GI6 -> 0 1 2 3 }
+{ 4 GI6 -> 0 1 2 3 4 }
+
+\ ------------------------------------------------------------------------
+TESTING DO LOOP +LOOP I J UNLOOP LEAVE EXIT
+
+{ : GD1 DO I LOOP ; -> }
+{ 4 1 GD1 -> 1 2 3 }
+{ 2 -1 GD1 -> -1 0 1 }
+{ MID-UINT+1 MID-UINT GD1 -> MID-UINT }
+
+{ : GD2 DO I -1 +LOOP ; -> }
+{ 1 4 GD2 -> 4 3 2 1 }
+{ -1 2 GD2 -> 2 1 0 -1 }
+{ MID-UINT MID-UINT+1 GD2 -> MID-UINT+1 MID-UINT }
+
+{ : GD3 DO 1 0 DO J LOOP LOOP ; -> }
+{ 4 1 GD3 -> 1 2 3 }
+{ 2 -1 GD3 -> -1 0 1 }
+{ MID-UINT+1 MID-UINT GD3 -> MID-UINT }
+
+{ : GD4 DO 1 0 DO J LOOP -1 +LOOP ; -> }
+{ 1 4 GD4 -> 4 3 2 1 }
+{ -1 2 GD4 -> 2 1 0 -1 }
+{ MID-UINT MID-UINT+1 GD4 -> MID-UINT+1 MID-UINT }
+
+{ : GD5 123 SWAP 0 DO I 4 > IF DROP 234 LEAVE THEN LOOP ; -> }
+{ 1 GD5 -> 123 }
+{ 5 GD5 -> 123 }
+{ 6 GD5 -> 234 }
+
+{ : GD6 ( PAT: {0 0},{0 0}{1 0}{1 1},{0 0}{1 0}{1 1}{2 0}{2 1}{2 2} )
+ 0 SWAP 0 DO
+ I 1+ 0 DO I J + 3 = IF I UNLOOP I UNLOOP EXIT THEN 1+ LOOP
+ LOOP ; -> }
+{ 1 GD6 -> 1 }
+{ 2 GD6 -> 3 }
+{ 3 GD6 -> 4 1 2 }
+
+\ ------------------------------------------------------------------------
+TESTING DEFINING WORDS: : ; CONSTANT VARIABLE CREATE DOES> >BODY
+
+{ 123 CONSTANT X123 -> }
+{ X123 -> 123 }
+{ : EQU CONSTANT ; -> }
+{ X123 EQU Y123 -> }
+{ Y123 -> 123 }
+
+{ VARIABLE V1 -> }
+{ 123 V1 ! -> }
+{ V1 @ -> 123 }
+
+{ : NOP : POSTPONE ; ; -> }
+{ NOP NOP1 NOP NOP2 -> }
+{ NOP1 -> }
+{ NOP2 -> }
+
+{ : DOES1 DOES> @ 1 + ; -> }
+{ : DOES2 DOES> @ 2 + ; -> }
+{ CREATE CR1 -> }
+{ CR1 -> HERE }
+{ ' CR1 >BODY -> HERE }
+{ 1 , -> }
+{ CR1 @ -> 1 }
+{ DOES1 -> }
+{ CR1 -> 2 }
+{ DOES2 -> }
+{ CR1 -> 3 }
+
+{ : WEIRD: CREATE DOES> 1 + DOES> 2 + ; -> }
+{ WEIRD: W1 -> }
+{ ' W1 >BODY -> HERE }
+{ W1 -> HERE 1 + }
+{ W1 -> HERE 2 + }
+
+\ ------------------------------------------------------------------------
+TESTING EVALUATE
+
+: GE1 S" 123" ; IMMEDIATE
+: GE2 S" 123 1+" ; IMMEDIATE
+: GE3 S" : GE4 345 ;" ;
+: GE5 EVALUATE ; IMMEDIATE
+
+{ GE1 EVALUATE -> 123 } ( TEST EVALUATE IN INTERP. STATE )
+{ GE2 EVALUATE -> 124 }
+{ GE3 EVALUATE -> }
+{ GE4 -> 345 }
+
+{ : GE6 GE1 GE5 ; -> } ( TEST EVALUATE IN COMPILE STATE )
+{ GE6 -> 123 }
+{ : GE7 GE2 GE5 ; -> }
+{ GE7 -> 124 }
+
+\ ------------------------------------------------------------------------
+TESTING SOURCE >IN WORD
+
+: GS1 S" SOURCE" 2DUP EVALUATE
+ >R SWAP >R = R> R> = ;
+{ GS1 -> <TRUE> <TRUE> }
+
+VARIABLE SCANS
+: RESCAN? -1 SCANS +! SCANS @ IF 0 >IN ! THEN ;
+
+{ 2 SCANS !
+345 RESCAN?
+-> 345 345 }
+: GS2 5 SCANS ! S" 123 RESCAN?" EVALUATE ;
+{ GS2 -> 123 123 123 123 123 }
+
+: GS3 WORD COUNT SWAP C@ ;
+{ BL GS3 HELLO -> 5 CHAR H }
+{ CHAR " GS3 GOODBYE" -> 7 CHAR G }
+{ BL GS3
+DROP -> 0 } \ BLANK LINE RETURN ZERO-LENGTH STRING
+
+: GS4 SOURCE >IN ! DROP ;
+{ GS4 123 456
+-> }
+
+\ ------------------------------------------------------------------------
+TESTING <# # #S #> HOLD SIGN BASE >NUMBER HEX DECIMAL
+
+: S= \ ( ADDR1 C1 ADDR2 C2 -- T/F ) COMPARE TWO STRINGS.
+ >R SWAP R@ = IF \ MAKE SURE STRINGS HAVE SAME LENGTH
+ R> ?DUP IF \ IF NON-EMPTY STRINGS
+ 0 DO
+ OVER C@ OVER C@ - IF 2DROP <FALSE> UNLOOP EXIT THEN
+ SWAP CHAR+ SWAP CHAR+
+ LOOP
+ THEN
+ 2DROP <TRUE> \ IF WE GET HERE, STRINGS MATCH
+ ELSE
+ R> DROP 2DROP <FALSE> \ LENGTHS MISMATCH
+ THEN ;
+
+: GP1 <# 41 HOLD 42 HOLD 0 0 #> S" BA" S= ;
+{ GP1 -> <TRUE> }
+
+: GP2 <# -1 SIGN 0 SIGN -1 SIGN 0 0 #> S" --" S= ;
+{ GP2 -> <TRUE> }
+
+: GP3 <# 1 0 # # #> S" 01" S= ;
+{ GP3 -> <TRUE> }
+
+: GP4 <# 1 0 #S #> S" 1" S= ;
+{ GP4 -> <TRUE> }
+
+24 CONSTANT MAX-BASE \ BASE 2 .. 36
+: COUNT-BITS
+ 0 0 INVERT BEGIN DUP WHILE >R 1+ R> 2* REPEAT DROP ;
+COUNT-BITS 2* CONSTANT #BITS-UD \ NUMBER OF BITS IN UD
+
+: GP5
+ BASE @ <TRUE>
+ MAX-BASE 1+ 2 DO \ FOR EACH POSSIBLE BASE
+ I BASE ! \ TBD: ASSUMES BASE WORKS
+ I 0 <# #S #> S" 10" S= AND
+ LOOP
+ SWAP BASE ! ;
+{ GP5 -> <TRUE> }
+
+: GP6
+ BASE @ >R 2 BASE !
+ MAX-UINT MAX-UINT <# #S #> \ MAXIMUM UD TO BINARY
+ R> BASE ! \ S: C-ADDR U
+ DUP #BITS-UD = SWAP
+ 0 DO \ S: C-ADDR FLAG
+ OVER C@ [CHAR] 1 = AND \ ALL ONES
+ >R CHAR+ R>
+ LOOP SWAP DROP ;
+{ GP6 -> <TRUE> }
+
+: GP7
+ BASE @ >R MAX-BASE BASE !
+ <TRUE>
+ A 0 DO
+ I 0 <# #S #>
+ 1 = SWAP C@ I 30 + = AND AND
+ LOOP
+ MAX-BASE A DO
+ I 0 <# #S #>
+ 1 = SWAP C@ 41 I A - + = AND AND
+ LOOP
+ R> BASE ! ;
+
+{ GP7 -> <TRUE> }
+
+\ >NUMBER TESTS
+CREATE GN-BUF 0 C,
+: GN-STRING GN-BUF 1 ;
+: GN-CONSUMED GN-BUF CHAR+ 0 ;
+: GN' [CHAR] ' WORD CHAR+ C@ GN-BUF C! GN-STRING ;
+
+{ 0 0 GN' 0' >NUMBER -> 0 0 GN-CONSUMED }
+{ 0 0 GN' 1' >NUMBER -> 1 0 GN-CONSUMED }
+{ 1 0 GN' 1' >NUMBER -> BASE @ 1+ 0 GN-CONSUMED }
+{ 0 0 GN' -' >NUMBER -> 0 0 GN-STRING } \ SHOULD FAIL TO CONVERT THESE
+{ 0 0 GN' +' >NUMBER -> 0 0 GN-STRING }
+{ 0 0 GN' .' >NUMBER -> 0 0 GN-STRING }
+
+: >NUMBER-BASED
+ BASE @ >R BASE ! >NUMBER R> BASE ! ;
+
+{ 0 0 GN' 2' 10 >NUMBER-BASED -> 2 0 GN-CONSUMED }
+{ 0 0 GN' 2' 2 >NUMBER-BASED -> 0 0 GN-STRING }
+{ 0 0 GN' F' 10 >NUMBER-BASED -> F 0 GN-CONSUMED }
+{ 0 0 GN' G' 10 >NUMBER-BASED -> 0 0 GN-STRING }
+{ 0 0 GN' G' MAX-BASE >NUMBER-BASED -> 10 0 GN-CONSUMED }
+{ 0 0 GN' Z' MAX-BASE >NUMBER-BASED -> 23 0 GN-CONSUMED }
+
+: GN1 \ ( UD BASE -- UD' LEN ) UD SHOULD EQUAL UD' AND LEN SHOULD BE ZERO.
+ BASE @ >R BASE !
+ <# #S #>
+ 0 0 2SWAP >NUMBER SWAP DROP \ RETURN LENGTH ONLY
+ R> BASE ! ;
+{ 0 0 2 GN1 -> 0 0 0 }
+{ MAX-UINT 0 2 GN1 -> MAX-UINT 0 0 }
+{ MAX-UINT DUP 2 GN1 -> MAX-UINT DUP 0 }
+{ 0 0 MAX-BASE GN1 -> 0 0 0 }
+{ MAX-UINT 0 MAX-BASE GN1 -> MAX-UINT 0 0 }
+{ MAX-UINT DUP MAX-BASE GN1 -> MAX-UINT DUP 0 }
+
+: GN2 \ ( -- 16 10 )
+ BASE @ >R HEX BASE @ DECIMAL BASE @ R> BASE ! ;
+{ GN2 -> 10 A }
+
+\ ------------------------------------------------------------------------
+TESTING FILL MOVE
+
+CREATE FBUF 00 C, 00 C, 00 C,
+CREATE SBUF 12 C, 34 C, 56 C,
+: SEEBUF FBUF C@ FBUF CHAR+ C@ FBUF CHAR+ CHAR+ C@ ;
+
+{ FBUF 0 20 FILL -> }
+{ SEEBUF -> 00 00 00 }
+
+{ FBUF 1 20 FILL -> }
+{ SEEBUF -> 20 00 00 }
+
+{ FBUF 3 20 FILL -> }
+{ SEEBUF -> 20 20 20 }
+
+{ FBUF FBUF 3 CHARS MOVE -> } \ BIZARRE SPECIAL CASE
+{ SEEBUF -> 20 20 20 }
+
+{ SBUF FBUF 0 CHARS MOVE -> }
+{ SEEBUF -> 20 20 20 }
+
+{ SBUF FBUF 1 CHARS MOVE -> }
+{ SEEBUF -> 12 20 20 }
+
+{ SBUF FBUF 3 CHARS MOVE -> }
+{ SEEBUF -> 12 34 56 }
+
+{ FBUF FBUF CHAR+ 2 CHARS MOVE -> }
+{ SEEBUF -> 12 12 34 }
+
+{ FBUF CHAR+ FBUF 2 CHARS MOVE -> }
+{ SEEBUF -> 12 34 34 }
+
+\ ------------------------------------------------------------------------
+TESTING OUTPUT: . ." CR EMIT SPACE SPACES TYPE U.
+
+: OUTPUT-TEST
+ ." YOU SHOULD SEE THE STANDARD GRAPHIC CHARACTERS:" CR
+ 41 BL DO I EMIT LOOP CR
+ 61 41 DO I EMIT LOOP CR
+ 7F 61 DO I EMIT LOOP CR
+ ." YOU SHOULD SEE 0-9 SEPARATED BY A SPACE:" CR
+ 9 1+ 0 DO I . LOOP CR
+ ." YOU SHOULD SEE 0-9 (WITH NO SPACES):" CR
+ [CHAR] 9 1+ [CHAR] 0 DO I 0 SPACES EMIT LOOP CR
+ ." YOU SHOULD SEE A-G SEPARATED BY A SPACE:" CR
+ [CHAR] G 1+ [CHAR] A DO I EMIT SPACE LOOP CR
+ ." YOU SHOULD SEE 0-5 SEPARATED BY TWO SPACES:" CR
+ 5 1+ 0 DO I [CHAR] 0 + EMIT 2 SPACES LOOP CR
+ ." YOU SHOULD SEE TWO SEPARATE LINES:" CR
+ S" LINE 1" TYPE CR S" LINE 2" TYPE CR
+ ." YOU SHOULD SEE THE NUMBER RANGES OF SIGNED AND UNSIGNED NUMBERS:" CR
+ ." SIGNED: " MIN-INT . MAX-INT . CR
+ ." UNSIGNED: " 0 U. MAX-UINT U. CR
+;
+
+{ OUTPUT-TEST -> }
+
+\ ------------------------------------------------------------------------
+TESTING INPUT: ACCEPT
+
+CREATE ABUF 80 CHARS ALLOT
+
+: ACCEPT-TEST
+ CR ." PLEASE TYPE UP TO 80 CHARACTERS:" CR
+ ABUF 80 ACCEPT
+ CR ." RECEIVED: " [CHAR] " EMIT
+ ABUF SWAP TYPE [CHAR] " EMIT CR
+;
+
+{ ACCEPT-TEST -> }
+
+\ ------------------------------------------------------------------------
+TESTING DICTIONARY SEARCH RULES
+
+{ : GDX 123 ; : GDX GDX 234 ; -> }
+
+{ GDX -> 123 234 }
+
+
diff --git a/test/fib.fr b/test/fib.fr
new file mode 100644
index 000000000000..24780a73b264
--- /dev/null
+++ b/test/fib.fr
@@ -0,0 +1,12 @@
+: fib ( n1 -- n2 )
+ dup 1 > if
+ dup
+ 1- recurse
+ swap 2 - recurse
+ +
+ then ;
+
+
+35 value nfibs
+: fibtest nfibs fib . cr ;
+
diff --git a/test/ficltest.fr b/test/ficltest.fr
new file mode 100644
index 000000000000..c67a0f753f5c
--- /dev/null
+++ b/test/ficltest.fr
@@ -0,0 +1,106 @@
+\ test file for ficl
+\ test ANSI CORE stuff first...
+-1 set-order
+
+\ set up local variable regressions before { gets redefined!
+: local1 { a b c | clr -- c b a 0 }
+ c b a clr
+;
+
+: local2 { | clr -- 0 } clr ;
+: local3 { a b | c }
+ a to c
+ b to a
+ c to b
+ a b
+;
+
+load tester.fr
+load core.fr
+
+{ -> }
+\ test double stuff
+testing 2>r 2r> 2r@
+: 2r1 2>r r> r> swap ;
+: 2r2 swap >r >r 2r> ;
+: 2r3 2>r 2r@ R> R> 2DUP >R >R SWAP 2r> ;
+
+{ 1 2 2r1 -> 1 2 }
+{ 1 2 2r2 -> 1 2 }
+{ 1 2 2r3 -> 1 2 1 2 1 2 }
+{ -> }
+
+\ Now test ficl extras and optional word-sets
+testing locals
+{ 1 2 3 local1 -> 3 2 1 0 }
+{ local2 -> 0 }
+{ 1 local2 -> 1 0 }
+{ 1 2 local3 -> 2 1 }
+
+testing :noname
+{ :noname 1 ; execute -> 1 }
+{ 1 2 3 -rot -> 3 1 2 }
+
+testing default search order
+{ get-order -> forth-wordlist 1 }
+{ only definitions get-order -> forth-wordlist 1 }
+
+testing forget
+here constant fence
+{ fence forget fence -> here }
+
+testing within
+{ -1 1 0 within -> true }
+{ 0 1s 2 within -> true }
+{ -100 0 -1 within -> true }
+{ -1 1 2 within -> false }
+{ -1 1 -2 within -> false }
+{ 1 -5 5 within -> true }
+{ 33000 32000 34000 within -> true }
+{ 0x80000000 0x7f000000 0x81000000 within -> true }
+
+testing exception words
+: exc1 1 throw ;
+: exctest1 [ ' exc1 ] literal catch ;
+: exc2 exctest1 1 = if 2 throw endif ;
+: exctest2 [ ' exc2 ] literal catch ;
+: exctest? ' catch ;
+
+{ exctest1 -> 1 }
+{ exctest2 -> 2 }
+{ exctest? abort -> -1 }
+
+testing refill
+\ from file loading
+0 [if]
+.( Error )
+[else]
+1 [if]
+[else]
+.( Error )
+[then]
+[then]
+
+\ refill from evaluate string
+{ -> }
+{ s" 1 refill 2 " evaluate -> 1 0 2 }
+
+
+testing prefixes
+{ 0x10 -> decimal 16 }
+{ hex 0d10 -> decimal 10 }
+{ hex 100
+-> decimal 256 }
+
+testing number builder
+{ 1 -> 1 }
+{ 3. -> 0 3 }
+
+
+s" ficlwin" environment?
+[if]
+drop
+testing OOP support
+load ooptest.fr
+[endif]
+
diff --git a/test/ooptest.fr b/test/ooptest.fr
new file mode 100644
index 000000000000..54abb8a22ce2
--- /dev/null
+++ b/test/ooptest.fr
@@ -0,0 +1,73 @@
+\ OOP test stuff
+
+only
+also oop definitions
+
+object subclass c-aggregate
+c-byte obj: m0
+c-byte obj: m1
+c-4byte obj: m2
+c-2byte obj: m3
+end-class
+
+object --> sub class1
+
+cell: .a
+cell: .b
+: init
+ locals| class inst |
+ 0 inst class --> .a !
+ 1 inst class --> .b !
+;
+end-class
+
+class1 --> new c1inst
+
+class1 --> sub class2
+cell: .c
+cell: .d
+
+: init
+ locals| class inst |
+ inst class --> super --> init
+ 2 inst class --> .c !
+ 3 inst class --> .d !
+;
+end-class
+
+class2 --> new c2inst
+
+object subclass c-list
+c-list ref: link
+c-ref obj: payload
+end-class
+
+\ test stuff from ficl.html
+.( metaclass methods ) cr
+metaclass --> methods
+
+cr .( c-foo class ) cr
+object --> sub c-foo
+cell: m_cell1
+ 4 chars: m_chars
+ : init ( inst class -- )
+ locals| class inst |
+ 0 inst class --> m_cell1 !
+ inst class --> m_chars 4 0 fill
+ ." initializing an instance of c_foo at " inst x. cr
+ ;
+end-class
+
+.( c-foo instance methods... ) cr
+c-foo --> new foo-instance
+cr
+foo-instance --> methods
+foo-instance --> pedigree
+cr
+foo-instance 2dup
+ --> methods
+ --> pedigree
+cr
+c-foo --> see init
+cr
+foo-instance --> class --> see init
diff --git a/test/prefix.fr b/test/prefix.fr
new file mode 100644
index 000000000000..58c66ddbf452
--- /dev/null
+++ b/test/prefix.fr
@@ -0,0 +1,8 @@
+: 0x { | old-base -- n }
+ base @ to old-base
+ 16 base !
+ 0 0 parse-word >number 2drop drop
+ old-base base !
+;
+
+ \ No newline at end of file
diff --git a/test/sarray.fr b/test/sarray.fr
new file mode 100644
index 000000000000..5793af1e5e82
--- /dev/null
+++ b/test/sarray.fr
@@ -0,0 +1,17 @@
+\ test file for ficl
+\ string array...
+: $array ( caddr u ... caddr u n -- )
+ create 0 ?do , , loop
+ does> swap 2* cells + 2@ type
+;
+
+: s
+ s" string 3"
+ s" string 2"
+ s" string 1"
+ s" string 0"
+ 4
+;
+
+s $array s
+
diff --git a/test/testcase.fr b/test/testcase.fr
new file mode 100644
index 000000000000..13775d20a99c
--- /dev/null
+++ b/test/testcase.fr
@@ -0,0 +1,83 @@
+
+
+1 2 3
+.s
+
+: test-case ( n -- )
+ case
+ 0 of
+ ." zero"
+ endof
+ 1 of
+ ." one"
+ endof
+ ." something else"
+ endcase
+ cr
+ ;
+
+
+see test-case
+
+.( You should see 1 2 3 ->)
+.s
+.( <-) cr
+
+.( You should see "zero": )
+0 test-case
+
+.( You should see "one": )
+1 test-case
+
+.( You should see "something else": )
+324 test-case
+
+.( You should still see 1 2 3 ->)
+.s
+.( <-) cr
+
+
+: test-case-2 ( n -- )
+ case
+ 0 of
+ ." zero"
+ fallthrough
+ 1 of
+ ." one"
+ endof
+ 2 of
+ ." two"
+ fallthrough
+ ." something else"
+ endcase
+ cr
+ ;
+
+
+see test-case-2
+
+cr
+
+.( You should once more see 1 2 3 ->)
+.s
+.( <-) cr
+
+.( You should see "zeroone": )
+0 test-case-2
+
+.( You should see "one": )
+1 test-case-2
+
+.( You should see "two": )
+2 test-case-2
+
+.( You should see "something else": )
+324 test-case-2
+
+.( You should still see 1 2 3 ->)
+.s
+.( <-) cr
+
+
+
+bye
diff --git a/test/tester.fr b/test/tester.fr
new file mode 100644
index 000000000000..6e239fb049dc
--- /dev/null
+++ b/test/tester.fr
@@ -0,0 +1,59 @@
+\ From: John Hayes S1I
+\ Subject: tester.fr
+\ Date: Mon, 27 Nov 95 13:10:09 PST
+\ john.hayes@jhuapl.edu
+\ (C) 1995 JOHNS HOPKINS UNIVERSITY / APPLIED PHYSICS LABORATORY
+\ MAY BE DISTRIBUTED FREELY AS LONG AS THIS COPYRIGHT NOTICE REMAINS.
+\ VERSION 1.1
+
+\ jws notes: <> is a core ext word
+
+HEX
+
+\ SET THE FOLLOWING FLAG TO TRUE FOR MORE VERBOSE OUTPUT; THIS MAY
+\ ALLOW YOU TO TELL WHICH TEST CAUSED YOUR SYSTEM TO HANG.
+VARIABLE VERBOSE
+ TRUE VERBOSE !
+
+: EMPTY-STACK \ ( ... -- ) EMPTY STACK: HANDLES UNDERFLOWED STACK TOO.
+ DEPTH ?DUP IF DUP 0< IF NEGATE 0 DO 0 LOOP ELSE 0 DO DROP LOOP THEN THEN ;
+
+: ERROR \ ( C-ADDR U -- ) DISPLAY AN ERROR MESSAGE FOLLOWED BY
+ \ THE LINE THAT HAD THE ERROR.
+ TYPE SOURCE TYPE CR \ DISPLAY LINE CORRESPONDING TO ERROR
+ EMPTY-STACK \ THROW AWAY EVERY THING ELSE
+ break \ jws
+;
+
+VARIABLE ACTUAL-DEPTH \ STACK RECORD
+
+CREATE ACTUAL-RESULTS 20 CELLS ALLOT
+
+: { \ ( -- ) SYNTACTIC SUGAR.
+ ;
+
+: -> \ ( ... -- ) RECORD DEPTH AND CONTENT OF STACK.
+ DEPTH DUP ACTUAL-DEPTH ! \ RECORD DEPTH
+ ?DUP IF \ IF THERE IS SOMETHING ON STACK
+ 0 DO ACTUAL-RESULTS I CELLS + ! LOOP \ SAVE THEM
+ THEN ;
+
+: } \ ( ... -- ) COMPARE STACK (EXPECTED) CONTENTS WITH SAVED
+ \ (ACTUAL) CONTENTS.
+ DEPTH ACTUAL-DEPTH @ = IF \ IF DEPTHS MATCH
+ DEPTH ?DUP IF \ IF THERE IS SOMETHING ON THE STACK
+ 0 DO \ FOR EACH STACK ITEM
+ ACTUAL-RESULTS I CELLS + @ \ COMPARE ACTUAL WITH EXPECTED
+ <> IF S" INCORRECT RESULT: " ERROR LEAVE THEN
+ LOOP
+ THEN
+ ELSE \ DEPTH MISMATCH
+ S" WRONG NUMBER OF RESULTS: " ERROR
+ THEN ;
+
+: TESTING \ ( -- ) TALKING COMMENT.
+ SOURCE VERBOSE @
+ IF DUP >R TYPE CR R> >IN !
+ ELSE >IN ! DROP
+ THEN ;
+
diff --git a/test/vocab.fr b/test/vocab.fr
new file mode 100644
index 000000000000..538257c37a79
--- /dev/null
+++ b/test/vocab.fr
@@ -0,0 +1,32 @@
+\ Here is an implementation of ALSO/ONLY in terms of the
+\ primitive search-order word set.
+\
+WORDLIST CONSTANT ROOT ROOT SET-CURRENT
+
+: DO-VOCABULARY ( -- ) \ Implementation factor
+ DOES> @ >R ( ) ( R: widnew )
+ GET-ORDER SWAP DROP ( wid1 ... widn-1 n )
+ R> SWAP SET-ORDER
+;
+
+: DISCARD ( x1 .. xu u - ) \ Implementation factor
+ 0 ?DO DROP LOOP \ DROP u+1 stack items
+;
+
+CREATE FORTH FORTH-WORDLIST , DO-VOCABULARY
+
+: VOCABULARY ( name -- ) WORDLIST CREATE , DO-VOCABULARY ;
+
+: ALSO ( -- ) GET-ORDER OVER SWAP 1+ SET-ORDER ;
+
+: PREVIOUS ( -- ) GET-ORDER SWAP DROP 1- SET-ORDER ;
+
+: DEFINITIONS ( -- ) GET-ORDER OVER SET-CURRENT DISCARD ;
+
+: ONLY ( -- ) ROOT ROOT 2 SET-ORDER ;
+
+\ Forth-83 version; just removes ONLY
+: SEAL ( -- ) GET-ORDER 1- SET-ORDER DROP ;
+
+\ F83 and F-PC version; leaves only CONTEXT
+: SEAL ( -- ) GET-ORDER OVER 1 SET-ORDER DISCARD ;
diff --git a/testmain.c b/testmain.c
new file mode 100644
index 000000000000..e41f77cea47c
--- /dev/null
+++ b/testmain.c
@@ -0,0 +1,367 @@
+/*
+** stub main for testing FICL under Win32
+** $Id: testmain.c,v 1.11 2001-10-28 10:59:19-08 jsadler Exp jsadler $
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#if defined (_WIN32)
+#include <direct.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef linux
+#include <unistd.h>
+#endif
+
+#include "ficl.h"
+
+/*
+** Ficl interface to _getcwd (Win32)
+** Prints the current working directory using the VM's
+** textOut method...
+*/
+static void ficlGetCWD(FICL_VM *pVM)
+{
+ char *cp;
+
+#if defined (_WIN32)
+ cp = _getcwd(NULL, 80);
+#else
+ cp = getcwd(NULL, 80);
+#endif
+ vmTextOut(pVM, cp, 1);
+ free(cp);
+ return;
+}
+
+/*
+** Ficl interface to _chdir (Win32)
+** Gets a newline (or NULL) delimited string from the input
+** and feeds it to the Win32 chdir function...
+** Example:
+** cd c:\tmp
+*/
+static void ficlChDir(FICL_VM *pVM)
+{
+ FICL_STRING *pFS = (FICL_STRING *)pVM->pad;
+ vmGetString(pVM, pFS, '\n');
+ if (pFS->count > 0)
+ {
+ int err = chdir(pFS->text);
+ if (err)
+ {
+ vmTextOut(pVM, "Error: path not found", 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+ }
+ else
+ {
+ vmTextOut(pVM, "Warning (chdir): nothing happened", 1);
+ }
+ return;
+}
+
+/*
+** Ficl interface to system (ANSI)
+** Gets a newline (or NULL) delimited string from the input
+** and feeds it to the Win32 system function...
+** Example:
+** system del *.*
+** \ ouch!
+*/
+static void ficlSystem(FICL_VM *pVM)
+{
+ FICL_STRING *pFS = (FICL_STRING *)pVM->pad;
+
+ vmGetString(pVM, pFS, '\n');
+ if (pFS->count > 0)
+ {
+ int err = system(pFS->text);
+ if (err)
+ {
+ sprintf(pVM->pad, "System call returned %d", err);
+ vmTextOut(pVM, pVM->pad, 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+ }
+ else
+ {
+ vmTextOut(pVM, "Warning (system): nothing happened", 1);
+ }
+ return;
+}
+
+/*
+** Ficl add-in to load a text file and execute it...
+** Cheesy, but illustrative.
+** Line oriented... filename is newline (or NULL) delimited.
+** Example:
+** load test.ficl
+*/
+#define nLINEBUF 256
+static void ficlLoad(FICL_VM *pVM)
+{
+ char cp[nLINEBUF];
+ char filename[nLINEBUF];
+ FICL_STRING *pFilename = (FICL_STRING *)filename;
+ int nLine = 0;
+ FILE *fp;
+ int result;
+ CELL id;
+ struct stat buf;
+
+
+ vmGetString(pVM, pFilename, '\n');
+
+ if (pFilename->count <= 0)
+ {
+ vmTextOut(pVM, "Warning (load): nothing happened", 1);
+ return;
+ }
+
+ /*
+ ** get the file's size and make sure it exists
+ */
+ result = stat( pFilename->text, &buf );
+
+ if (result != 0)
+ {
+ vmTextOut(pVM, "Unable to stat file: ", 0);
+ vmTextOut(pVM, pFilename->text, 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+
+ fp = fopen(pFilename->text, "r");
+ if (!fp)
+ {
+ vmTextOut(pVM, "Unable to open file ", 0);
+ vmTextOut(pVM, pFilename->text, 1);
+ vmThrow(pVM, VM_QUIT);
+ }
+
+ id = pVM->sourceID;
+ pVM->sourceID.p = (void *)fp;
+
+ /* feed each line to ficlExec */
+ while (fgets(cp, nLINEBUF, fp))
+ {
+ int len = strlen(cp) - 1;
+
+ nLine++;
+ if (len <= 0)
+ continue;
+
+ if (cp[len] == '\n')
+ cp[len] = '\0';
+
+ result = ficlExec(pVM, cp);
+ /* handle "bye" in loaded files. --lch */
+ switch (result)
+ {
+ case VM_OUTOFTEXT:
+ case VM_USEREXIT:
+ break;
+
+ default:
+ pVM->sourceID = id;
+ fclose(fp);
+ vmThrowErr(pVM, "Error loading file <%s> line %d", pFilename->text, nLine);
+ break;
+ }
+ }
+ /*
+ ** Pass an empty line with SOURCE-ID == -1 to flush
+ ** any pending REFILLs (as required by FILE wordset)
+ */
+ pVM->sourceID.i = -1;
+ ficlExec(pVM, "");
+
+ pVM->sourceID = id;
+ fclose(fp);
+
+ /* handle "bye" in loaded files. --lch */
+ if (result == VM_USEREXIT)
+ vmThrow(pVM, VM_USEREXIT);
+ return;
+}
+
+/*
+** Dump a tab delimited file that summarizes the contents of the
+** dictionary hash table by hashcode...
+*/
+static void spewHash(FICL_VM *pVM)
+{
+ FICL_HASH *pHash = vmGetDict(pVM)->pForthWords;
+ FICL_WORD *pFW;
+ FILE *pOut;
+ unsigned i;
+ unsigned nHash = pHash->size;
+
+ if (!vmGetWordToPad(pVM))
+ vmThrow(pVM, VM_OUTOFTEXT);
+
+ pOut = fopen(pVM->pad, "w");
+ if (!pOut)
+ {
+ vmTextOut(pVM, "unable to open file", 1);
+ return;
+ }
+
+ for (i=0; i < nHash; i++)
+ {
+ int n = 0;
+
+ pFW = pHash->table[i];
+ while (pFW)
+ {
+ n++;
+ pFW = pFW->link;
+ }
+
+ fprintf(pOut, "%d\t%d", i, n);
+
+ pFW = pHash->table[i];
+ while (pFW)
+ {
+ fprintf(pOut, "\t%s", pFW->name);
+ pFW = pFW->link;
+ }
+
+ fprintf(pOut, "\n");
+ }
+
+ fclose(pOut);
+ return;
+}
+
+static void ficlBreak(FICL_VM *pVM)
+{
+ pVM->state = pVM->state;
+ return;
+}
+
+static void ficlClock(FICL_VM *pVM)
+{
+ clock_t now = clock();
+ stackPushUNS(pVM->pStack, (FICL_UNS)now);
+ return;
+}
+
+static void clocksPerSec(FICL_VM *pVM)
+{
+ stackPushUNS(pVM->pStack, CLOCKS_PER_SEC);
+ return;
+}
+
+
+void buildTestInterface(FICL_SYSTEM *pSys)
+{
+ ficlBuild(pSys, "break", ficlBreak, FW_DEFAULT);
+ ficlBuild(pSys, "clock", ficlClock, FW_DEFAULT);
+ ficlBuild(pSys, "cd", ficlChDir, FW_DEFAULT);
+ ficlBuild(pSys, "load", ficlLoad, FW_DEFAULT);
+ ficlBuild(pSys, "pwd", ficlGetCWD, FW_DEFAULT);
+ ficlBuild(pSys, "system", ficlSystem, FW_DEFAULT);
+ ficlBuild(pSys, "spewhash", spewHash, FW_DEFAULT);
+ ficlBuild(pSys, "clocks/sec",
+ clocksPerSec, FW_DEFAULT);
+
+ return;
+}
+
+
+#if !defined (_WINDOWS)
+#define nINBUF 256
+
+#if !defined (_WIN32)
+#define __try
+#define __except(i) if (0)
+#endif
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ char in[nINBUF];
+ FICL_VM *pVM;
+ FICL_SYSTEM *pSys;
+
+ pSys = ficlInitSystem(10000);
+ buildTestInterface(pSys);
+ pVM = ficlNewVM(pSys);
+
+ ret = ficlEvaluate(pVM, ".ver .( " __DATE__ " ) cr quit");
+
+ /*
+ ** load file from cmd line...
+ */
+ if (argc > 1)
+ {
+ sprintf(in, ".( loading %s ) cr load %s\n cr", argv[1], argv[1]);
+ __try
+ {
+ ret = ficlEvaluate(pVM, in);
+ }
+ __except(1)
+ {
+ vmTextOut(pVM, "exception -- cleaning up", 1);
+ vmReset(pVM);
+ }
+ }
+
+ while (ret != VM_USEREXIT)
+ {
+ fgets(in, nINBUF, stdin);
+ __try
+ {
+ ret = ficlExec(pVM, in);
+ }
+ __except(1)
+ {
+ vmTextOut(pVM, "exception -- cleaning up", 1);
+ vmReset(pVM);
+ }
+ }
+
+ ficlTermSystem(pSys);
+ return 0;
+}
+
+#endif
+
diff --git a/tools.c b/tools.c
new file mode 100644
index 000000000000..ad734e4b8834
--- /dev/null
+++ b/tools.c
@@ -0,0 +1,893 @@
+/*******************************************************************
+** t o o l s . c
+** Forth Inspired Command Language - programming tools
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 20 June 2000
+** $Id: tools.c,v 1.11 2001-12-04 17:58:14-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+/*
+** NOTES:
+** SEE needs information about the addresses of functions that
+** are the CFAs of colon definitions, constants, variables, DOES>
+** words, and so on. It gets this information from a table and supporting
+** functions in words.c.
+** colonParen doDoes createParen variableParen userParen constantParen
+**
+** Step and break debugger for Ficl
+** debug ( xt -- ) Start debugging an xt
+** Set a breakpoint
+** Specify breakpoint default action
+*/
+
+#include <stdlib.h>
+#include <stdio.h> /* sprintf */
+#include <string.h>
+#include <ctype.h>
+#include "ficl.h"
+
+
+#if 0
+/*
+** nBREAKPOINTS sizes the breakpoint array. One breakpoint (bp 0) is reserved
+** for the STEP command. The rest are user programmable.
+*/
+#define nBREAKPOINTS 32
+
+#endif
+
+
+/**************************************************************************
+ v m S e t B r e a k
+** Set a breakpoint at the current value of IP by
+** storing that address in a BREAKPOINT record
+**************************************************************************/
+static void vmSetBreak(FICL_VM *pVM, FICL_BREAKPOINT *pBP)
+{
+ FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");
+ assert(pStep);
+
+ pBP->address = pVM->ip;
+ pBP->origXT = *pVM->ip;
+ *pVM->ip = pStep;
+}
+
+
+/**************************************************************************
+** d e b u g P r o m p t
+**************************************************************************/
+static void debugPrompt(FICL_VM *pVM)
+{
+ vmTextOut(pVM, "dbg> ", 0);
+}
+
+
+/**************************************************************************
+** i s A F i c l W o r d
+** Vet a candidate pointer carefully to make sure
+** it's not some chunk o' inline data...
+** It has to have a name, and it has to look
+** like it's in the dictionary address range.
+** NOTE: this excludes :noname words!
+**************************************************************************/
+int isAFiclWord(FICL_DICT *pd, FICL_WORD *pFW)
+{
+
+ if (!dictIncludes(pd, pFW))
+ return 0;
+
+ if (!dictIncludes(pd, pFW->name))
+ return 0;
+
+ if ((pFW->link != NULL) && !dictIncludes(pd, pFW->link))
+ return 0;
+
+ if ((pFW->nName <= 0) || (pFW->name[pFW->nName] != '\0'))
+ return 0;
+
+ if (strlen(pFW->name) != pFW->nName)
+ return 0;
+
+ return 1;
+}
+
+
+#if 0
+static int isPrimitive(FICL_WORD *pFW)
+{
+ WORDKIND wk = ficlWordClassify(pFW);
+ return ((wk != COLON) && (wk != DOES));
+}
+#endif
+
+
+/**************************************************************************
+ f i n d E n c l o s i n g W o r d
+** Given a pointer to something, check to make sure it's an address in the
+** dictionary. If so, search backwards until we find something that looks
+** like a dictionary header. If successful, return the address of the
+** FICL_WORD found. Otherwise return NULL.
+** nSEARCH_CELLS sets the maximum neighborhood this func will search before giving up
+**************************************************************************/
+#define nSEARCH_CELLS 100
+
+static FICL_WORD *findEnclosingWord(FICL_VM *pVM, CELL *cp)
+{
+ FICL_WORD *pFW;
+ FICL_DICT *pd = vmGetDict(pVM);
+ int i;
+
+ if (!dictIncludes(pd, (void *)cp))
+ return NULL;
+
+ for (i = nSEARCH_CELLS; i > 0; --i, --cp)
+ {
+ pFW = (FICL_WORD *)(cp + 1 - (sizeof (FICL_WORD) / sizeof (CELL)));
+ if (isAFiclWord(pd, pFW))
+ return pFW;
+ }
+
+ return NULL;
+}
+
+
+/**************************************************************************
+ s e e
+** TOOLS ( "<spaces>name" -- )
+** Display a human-readable representation of the named word's definition.
+** The source of the representation (object-code decompilation, source
+** block, etc.) and the particular form of the display is implementation
+** defined.
+**************************************************************************/
+/*
+** seeColon (for proctologists only)
+** Walks a colon definition, decompiling
+** on the fly. Knows about primitive control structures.
+*/
+static void seeColon(FICL_VM *pVM, CELL *pc)
+{
+ char *cp;
+ CELL *param0 = pc;
+ FICL_DICT *pd = vmGetDict(pVM);
+ FICL_WORD *pSemiParen = ficlLookup(pVM->pSys, "(;)");
+ assert(pSemiParen);
+
+ for (; pc->p != pSemiParen; pc++)
+ {
+ FICL_WORD *pFW = (FICL_WORD *)(pc->p);
+
+ cp = pVM->pad;
+ if ((void *)pc == (void *)pVM->ip)
+ *cp++ = '>';
+ else
+ *cp++ = ' ';
+ cp += sprintf(cp, "%3d ", pc-param0);
+
+ if (isAFiclWord(pd, pFW))
+ {
+ WORDKIND kind = ficlWordClassify(pFW);
+ CELL c;
+
+ switch (kind)
+ {
+ case LITERAL:
+ c = *++pc;
+ if (isAFiclWord(pd, c.p))
+ {
+ FICL_WORD *pLit = (FICL_WORD *)c.p;
+ sprintf(cp, "%.*s ( %#lx literal )",
+ pLit->nName, pLit->name, c.u);
+ }
+ else
+ sprintf(cp, "literal %ld (%#lx)", c.i, c.u);
+ break;
+ case STRINGLIT:
+ {
+ FICL_STRING *sp = (FICL_STRING *)(void *)++pc;
+ pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;
+ sprintf(cp, "s\" %.*s\"", sp->count, sp->text);
+ }
+ break;
+ case CSTRINGLIT:
+ {
+ FICL_STRING *sp = (FICL_STRING *)(void *)++pc;
+ pc = (CELL *)alignPtr(sp->text + sp->count + 1) - 1;
+ sprintf(cp, "c\" %.*s\"", sp->count, sp->text);
+ }
+ break;
+ case IF:
+ c = *++pc;
+ if (c.i > 0)
+ sprintf(cp, "if / while (branch %d)", pc+c.i-param0);
+ else
+ sprintf(cp, "until (branch %d)", pc+c.i-param0);
+ break;
+ case BRANCH:
+ c = *++pc;
+ if (c.i == 0)
+ sprintf(cp, "repeat (branch %d)", pc+c.i-param0);
+ else if (c.i == 1)
+ sprintf(cp, "else (branch %d)", pc+c.i-param0);
+ else
+ sprintf(cp, "endof (branch %d)", pc+c.i-param0);
+ break;
+
+ case OF:
+ c = *++pc;
+ sprintf(cp, "of (branch %d)", pc+c.i-param0);
+ break;
+
+ case QDO:
+ c = *++pc;
+ sprintf(cp, "?do (leave %d)", (CELL *)c.p-param0);
+ break;
+ case DO:
+ c = *++pc;
+ sprintf(cp, "do (leave %d)", (CELL *)c.p-param0);
+ break;
+ case LOOP:
+ c = *++pc;
+ sprintf(cp, "loop (branch %d)", pc+c.i-param0);
+ break;
+ case PLOOP:
+ c = *++pc;
+ sprintf(cp, "+loop (branch %d)", pc+c.i-param0);
+ break;
+ default:
+ sprintf(cp, "%.*s", pFW->nName, pFW->name);
+ break;
+ }
+
+ }
+ else /* probably not a word - punt and print value */
+ {
+ sprintf(cp, "%ld ( %#lx )", pc->i, pc->u);
+ }
+
+ vmTextOut(pVM, pVM->pad, 1);
+ }
+
+ vmTextOut(pVM, ";", 1);
+}
+
+/*
+** Here's the outer part of the decompiler. It's
+** just a big nested conditional that checks the
+** CFA of the word to decompile for each kind of
+** known word-builder code, and tries to do
+** something appropriate. If the CFA is not recognized,
+** just indicate that it is a primitive.
+*/
+static void seeXT(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+ WORDKIND kind;
+
+ pFW = (FICL_WORD *)stackPopPtr(pVM->pStack);
+ kind = ficlWordClassify(pFW);
+
+ switch (kind)
+ {
+ case COLON:
+ sprintf(pVM->pad, ": %.*s", pFW->nName, pFW->name);
+ vmTextOut(pVM, pVM->pad, 1);
+ seeColon(pVM, pFW->param);
+ break;
+
+ case DOES:
+ vmTextOut(pVM, "does>", 1);
+ seeColon(pVM, (CELL *)pFW->param->p);
+ break;
+
+ case CREATE:
+ vmTextOut(pVM, "create", 1);
+ break;
+
+ case VARIABLE:
+ sprintf(pVM->pad, "variable = %ld (%#lx)", pFW->param->i, pFW->param->u);
+ vmTextOut(pVM, pVM->pad, 1);
+ break;
+
+#if FICL_WANT_USER
+ case USER:
+ sprintf(pVM->pad, "user variable %ld (%#lx)", pFW->param->i, pFW->param->u);
+ vmTextOut(pVM, pVM->pad, 1);
+ break;
+#endif
+
+ case CONSTANT:
+ sprintf(pVM->pad, "constant = %ld (%#lx)", pFW->param->i, pFW->param->u);
+ vmTextOut(pVM, pVM->pad, 1);
+
+ default:
+ sprintf(pVM->pad, "%.*s is a primitive", pFW->nName, pFW->name);
+ vmTextOut(pVM, pVM->pad, 1);
+ break;
+ }
+
+ if (pFW->flags & FW_IMMEDIATE)
+ {
+ vmTextOut(pVM, "immediate", 1);
+ }
+
+ if (pFW->flags & FW_COMPILE)
+ {
+ vmTextOut(pVM, "compile-only", 1);
+ }
+
+ return;
+}
+
+
+static void see(FICL_VM *pVM)
+{
+ ficlTick(pVM);
+ seeXT(pVM);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l D e b u g X T
+** debug ( xt -- )
+** Given an xt of a colon definition or a word defined by DOES>, set the
+** VM up to debug the word: push IP, set the xt as the next thing to execute,
+** set a breakpoint at its first instruction, and run to the breakpoint.
+** Note: the semantics of this word are equivalent to "step in"
+**************************************************************************/
+void ficlDebugXT(FICL_VM *pVM)
+{
+ FICL_WORD *xt = stackPopPtr(pVM->pStack);
+ WORDKIND wk = ficlWordClassify(xt);
+
+ stackPushPtr(pVM->pStack, xt);
+ seeXT(pVM);
+
+ switch (wk)
+ {
+ case COLON:
+ case DOES:
+ /*
+ ** Run the colon code and set a breakpoint at the next instruction
+ */
+ vmExecute(pVM, xt);
+ vmSetBreak(pVM, &(pVM->pSys->bpStep));
+ break;
+
+ default:
+ vmExecute(pVM, xt);
+ break;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ s t e p I n
+** FICL
+** Execute the next instruction, stepping into it if it's a colon definition
+** or a does> word. This is the easy kind of step.
+**************************************************************************/
+void stepIn(FICL_VM *pVM)
+{
+ /*
+ ** Do one step of the inner loop
+ */
+ {
+ M_VM_STEP(pVM)
+ }
+
+ /*
+ ** Now set a breakpoint at the next instruction
+ */
+ vmSetBreak(pVM, &(pVM->pSys->bpStep));
+
+ return;
+}
+
+
+/**************************************************************************
+ s t e p O v e r
+** FICL
+** Execute the next instruction atomically. This requires some insight into
+** the memory layout of compiled code. Set a breakpoint at the next instruction
+** in this word, and run until we hit it
+**************************************************************************/
+void stepOver(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+ WORDKIND kind;
+ FICL_WORD *pStep = ficlLookup(pVM->pSys, "step-break");
+ assert(pStep);
+
+ pFW = *pVM->ip;
+ kind = ficlWordClassify(pFW);
+
+ switch (kind)
+ {
+ case COLON:
+ case DOES:
+ /*
+ ** assume that the next cell holds an instruction
+ ** set a breakpoint there and return to the inner interp
+ */
+ pVM->pSys->bpStep.address = pVM->ip + 1;
+ pVM->pSys->bpStep.origXT = pVM->ip[1];
+ pVM->ip[1] = pStep;
+ break;
+
+ default:
+ stepIn(pVM);
+ break;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ s t e p - b r e a k
+** FICL
+** Handles breakpoints for stepped execution.
+** Upon entry, bpStep contains the address and replaced instruction
+** of the current breakpoint.
+** Clear the breakpoint
+** Get a command from the console.
+** i (step in) - execute the current instruction and set a new breakpoint
+** at the IP
+** o (step over) - execute the current instruction to completion and set
+** a new breakpoint at the IP
+** g (go) - execute the current instruction and exit
+** q (quit) - abort current word
+** b (toggle breakpoint)
+**************************************************************************/
+void stepBreak(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW;
+ FICL_WORD *pOnStep;
+
+ if (!pVM->fRestart)
+ {
+ assert(pVM->pSys->bpStep.address);
+ assert(pVM->pSys->bpStep.origXT);
+ /*
+ ** Clear the breakpoint that caused me to run
+ ** Restore the original instruction at the breakpoint,
+ ** and restore the IP
+ */
+ pVM->ip = (IPTYPE)(pVM->pSys->bpStep.address);
+ *pVM->ip = pVM->pSys->bpStep.origXT;
+
+ /*
+ ** If there's an onStep, do it
+ */
+ pOnStep = ficlLookup(pVM->pSys, "on-step");
+ if (pOnStep)
+ ficlExecXT(pVM, pOnStep);
+
+ /*
+ ** Print the name of the next instruction
+ */
+ pFW = pVM->pSys->bpStep.origXT;
+ sprintf(pVM->pad, "next: %.*s", pFW->nName, pFW->name);
+#if 0
+ if (isPrimitive(pFW))
+ {
+ strcat(pVM->pad, " ( primitive )");
+ }
+#endif
+
+ vmTextOut(pVM, pVM->pad, 1);
+ debugPrompt(pVM);
+ }
+ else
+ {
+ pVM->fRestart = 0;
+ }
+
+ si = vmGetWord(pVM);
+
+ if (!strincmp(si.cp, "i", si.count))
+ {
+ stepIn(pVM);
+ }
+ else if (!strincmp(si.cp, "g", si.count))
+ {
+ return;
+ }
+ else if (!strincmp(si.cp, "l", si.count))
+ {
+ FICL_WORD *xt;
+ xt = findEnclosingWord(pVM, (CELL *)(pVM->ip));
+ if (xt)
+ {
+ stackPushPtr(pVM->pStack, xt);
+ seeXT(pVM);
+ }
+ else
+ {
+ vmTextOut(pVM, "sorry - can't do that", 1);
+ }
+ vmThrow(pVM, VM_RESTART);
+ }
+ else if (!strincmp(si.cp, "o", si.count))
+ {
+ stepOver(pVM);
+ }
+ else if (!strincmp(si.cp, "q", si.count))
+ {
+ ficlTextOut(pVM, FICL_PROMPT, 0);
+ vmThrow(pVM, VM_ABORT);
+ }
+ else if (!strincmp(si.cp, "x", si.count))
+ {
+ /*
+ ** Take whatever's left in the TIB and feed it to a subordinate ficlExec
+ */
+ int ret;
+ char *cp = pVM->tib.cp + pVM->tib.index;
+ int count = pVM->tib.end - cp;
+ FICL_WORD *oldRun = pVM->runningWord;
+
+ ret = ficlExecC(pVM, cp, count);
+
+ if (ret == VM_OUTOFTEXT)
+ {
+ ret = VM_RESTART;
+ pVM->runningWord = oldRun;
+ vmTextOut(pVM, "", 1);
+ }
+
+ vmThrow(pVM, ret);
+ }
+ else
+ {
+ vmTextOut(pVM, "i -- step In", 1);
+ vmTextOut(pVM, "o -- step Over", 1);
+ vmTextOut(pVM, "g -- Go (execute to completion)", 1);
+ vmTextOut(pVM, "l -- List source code", 1);
+ vmTextOut(pVM, "q -- Quit (stop debugging and abort)", 1);
+ vmTextOut(pVM, "x -- eXecute the rest of the line as ficl words", 1);
+ debugPrompt(pVM);
+ vmThrow(pVM, VM_RESTART);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ b y e
+** TOOLS
+** Signal the system to shut down - this causes ficlExec to return
+** VM_USEREXIT. The rest is up to you.
+**************************************************************************/
+static void bye(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_USEREXIT);
+ return;
+}
+
+
+/**************************************************************************
+ d i s p l a y S t a c k
+** TOOLS
+** Display the parameter stack (code for ".s")
+**************************************************************************/
+static void displayPStack(FICL_VM *pVM)
+{
+ FICL_STACK *pStk = pVM->pStack;
+ int d = stackDepth(pStk);
+ int i;
+ CELL *pCell;
+
+ vmCheckStack(pVM, 0, 0);
+
+ if (d == 0)
+ vmTextOut(pVM, "(Stack Empty) ", 0);
+ else
+ {
+ pCell = pStk->base;
+ for (i = 0; i < d; i++)
+ {
+ vmTextOut(pVM, ltoa((*pCell++).i, pVM->pad, pVM->base), 0);
+ vmTextOut(pVM, " ", 0);
+ }
+ }
+ return;
+}
+
+
+static void displayRStack(FICL_VM *pVM)
+{
+ FICL_STACK *pStk = pVM->rStack;
+ int d = stackDepth(pStk);
+ int i;
+ CELL *pCell;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ vmCheckStack(pVM, 0, 0);
+
+ if (d == 0)
+ vmTextOut(pVM, "(Stack Empty) ", 0);
+ else
+ {
+ pCell = pStk->base;
+ for (i = 0; i < d; i++)
+ {
+ CELL c = *pCell++;
+ /*
+ ** Attempt to find the word that contains the
+ ** stacked address (as if it is part of a colon definition).
+ ** If this works, print the name of the word. Otherwise print
+ ** the value as a number.
+ */
+ if (dictIncludes(dp, c.p))
+ {
+ FICL_WORD *pFW = findEnclosingWord(pVM, c.p);
+ if (pFW)
+ {
+ int offset = (CELL *)c.p - &pFW->param[0];
+ sprintf(pVM->pad, "%s+%d ", pFW->name, offset);
+ vmTextOut(pVM, pVM->pad, 0);
+ continue; /* no need to print the numeric value */
+ }
+ }
+ vmTextOut(pVM, ltoa(c.i, pVM->pad, pVM->base), 0);
+ vmTextOut(pVM, " ", 0);
+ }
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ f o r g e t - w i d
+**
+**************************************************************************/
+static void forgetWid(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_HASH *pHash;
+
+ pHash = (FICL_HASH *)stackPopPtr(pVM->pStack);
+ hashForget(pHash, pDict->here);
+
+ return;
+}
+
+
+/**************************************************************************
+ f o r g e t
+** TOOLS EXT ( "<spaces>name" -- )
+** Skip leading space delimiters. Parse name delimited by a space.
+** Find name, then delete name from the dictionary along with all
+** words added to the dictionary after name. An ambiguous
+** condition exists if name cannot be found.
+**
+** If the Search-Order word set is present, FORGET searches the
+** compilation word list. An ambiguous condition exists if the
+** compilation word list is deleted.
+**************************************************************************/
+static void forget(FICL_VM *pVM)
+{
+ void *where;
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_HASH *pHash = pDict->pCompile;
+
+ ficlTick(pVM);
+ where = ((FICL_WORD *)stackPopPtr(pVM->pStack))->name;
+ hashForget(pHash, where);
+ pDict->here = PTRtoCELL where;
+
+ return;
+}
+
+
+/**************************************************************************
+ l i s t W o r d s
+**
+**************************************************************************/
+#define nCOLWIDTH 8
+static void listWords(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_HASH *pHash = dp->pSearch[dp->nLists - 1];
+ FICL_WORD *wp;
+ int nChars = 0;
+ int len;
+ unsigned i;
+ int nWords = 0;
+ char *cp;
+ char *pPad = pVM->pad;
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)
+ {
+ if (wp->nName == 0) /* ignore :noname defs */
+ continue;
+
+ cp = wp->name;
+ nChars += sprintf(pPad + nChars, "%s", cp);
+
+ if (nChars > 70)
+ {
+ pPad[nChars] = '\0';
+ nChars = 0;
+ vmTextOut(pVM, pPad, 1);
+ }
+ else
+ {
+ len = nCOLWIDTH - nChars % nCOLWIDTH;
+ while (len-- > 0)
+ pPad[nChars++] = ' ';
+ }
+
+ if (nChars > 70)
+ {
+ pPad[nChars] = '\0';
+ nChars = 0;
+ vmTextOut(pVM, pPad, 1);
+ }
+ }
+ }
+
+ if (nChars > 0)
+ {
+ pPad[nChars] = '\0';
+ nChars = 0;
+ vmTextOut(pVM, pPad, 1);
+ }
+
+ sprintf(pVM->pad, "Dictionary: %d words, %ld cells used of %u total",
+ nWords, (long) (dp->here - dp->dict), dp->size);
+ vmTextOut(pVM, pVM->pad, 1);
+ return;
+}
+
+
+/**************************************************************************
+ l i s t E n v
+** Print symbols defined in the environment
+**************************************************************************/
+static void listEnv(FICL_VM *pVM)
+{
+ FICL_DICT *dp = pVM->pSys->envp;
+ FICL_HASH *pHash = dp->pForthWords;
+ FICL_WORD *wp;
+ unsigned i;
+ int nWords = 0;
+
+ for (i = 0; i < pHash->size; i++)
+ {
+ for (wp = pHash->table[i]; wp != NULL; wp = wp->link, nWords++)
+ {
+ vmTextOut(pVM, wp->name, 1);
+ }
+ }
+
+ sprintf(pVM->pad, "Environment: %d words, %ld cells used of %u total",
+ nWords, (long) (dp->here - dp->dict), dp->size);
+ vmTextOut(pVM, pVM->pad, 1);
+ return;
+}
+
+
+/**************************************************************************
+ e n v C o n s t a n t
+** Ficl interface to ficlSetEnv and ficlSetEnvD - allow ficl code to set
+** environment constants...
+**************************************************************************/
+static void envConstant(FICL_VM *pVM)
+{
+ unsigned value;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ vmGetWordToPad(pVM);
+ value = POPUNS();
+ ficlSetEnv(pVM->pSys, pVM->pad, (FICL_UNS)value);
+ return;
+}
+
+static void env2Constant(FICL_VM *pVM)
+{
+ unsigned v1, v2;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ vmGetWordToPad(pVM);
+ v2 = POPUNS();
+ v1 = POPUNS();
+ ficlSetEnvD(pVM->pSys, pVM->pad, v1, v2);
+ return;
+}
+
+
+/**************************************************************************
+ f i c l C o m p i l e T o o l s
+** Builds wordset for debugger and TOOLS optional word set
+**************************************************************************/
+
+void ficlCompileTools(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ /*
+ ** TOOLS and TOOLS EXT
+ */
+ dictAppendWord(dp, ".s", displayPStack, FW_DEFAULT);
+ dictAppendWord(dp, "bye", bye, FW_DEFAULT);
+ dictAppendWord(dp, "forget", forget, FW_DEFAULT);
+ dictAppendWord(dp, "see", see, FW_DEFAULT);
+ dictAppendWord(dp, "words", listWords, FW_DEFAULT);
+
+ /*
+ ** Set TOOLS environment query values
+ */
+ ficlSetEnv(pSys, "tools", FICL_TRUE);
+ ficlSetEnv(pSys, "tools-ext", FICL_FALSE);
+
+ /*
+ ** Ficl extras
+ */
+ dictAppendWord(dp, "r.s", displayRStack, FW_DEFAULT); /* guy carver */
+ dictAppendWord(dp, ".env", listEnv, FW_DEFAULT);
+ dictAppendWord(dp, "env-constant",
+ envConstant, FW_DEFAULT);
+ dictAppendWord(dp, "env-2constant",
+ env2Constant, FW_DEFAULT);
+ dictAppendWord(dp, "debug-xt", ficlDebugXT, FW_DEFAULT);
+ dictAppendWord(dp, "parse-order",
+ ficlListParseSteps,
+ FW_DEFAULT);
+ dictAppendWord(dp, "step-break",stepBreak, FW_DEFAULT);
+ dictAppendWord(dp, "forget-wid",forgetWid, FW_DEFAULT);
+ dictAppendWord(dp, "see-xt", seeXT, FW_DEFAULT);
+
+ return;
+}
+
diff --git a/unix.c b/unix.c
new file mode 100644
index 000000000000..3cf7862329b6
--- /dev/null
+++ b/unix.c
@@ -0,0 +1,21 @@
+#include <string.h>
+#include <netinet/in.h>
+
+#include "ficl.h"
+
+
+
+unsigned long ficlNtohl(unsigned long number)
+{
+ return ntohl(number);
+}
+
+
+
+
+void ficlCompilePlatform(FICL_DICT *dp)
+{
+ return;
+}
+
+
diff --git a/vm.c b/vm.c
new file mode 100644
index 000000000000..a36196df19a9
--- /dev/null
+++ b/vm.c
@@ -0,0 +1,799 @@
+/*******************************************************************
+** v m . c
+** Forth Inspired Command Language - virtual machine methods
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: vm.c,v 1.12 2001-12-04 17:58:14-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** This file implements the virtual machine of FICL. Each virtual
+** machine retains the state of an interpreter. A virtual machine
+** owns a pair of stacks for parameters and return addresses, as
+** well as a pile of state variables and the two dedicated registers
+** of the interp.
+*/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include "ficl.h"
+
+static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+
+/**************************************************************************
+ v m B r a n c h R e l a t i v e
+**
+**************************************************************************/
+void vmBranchRelative(FICL_VM *pVM, int offset)
+{
+ pVM->ip += offset;
+ return;
+}
+
+
+/**************************************************************************
+ v m C r e a t e
+** Creates a virtual machine either from scratch (if pVM is NULL on entry)
+** or by resizing and reinitializing an existing VM to the specified stack
+** sizes.
+**************************************************************************/
+FICL_VM *vmCreate(FICL_VM *pVM, unsigned nPStack, unsigned nRStack)
+{
+ if (pVM == NULL)
+ {
+ pVM = (FICL_VM *)ficlMalloc(sizeof (FICL_VM));
+ assert (pVM);
+ memset(pVM, 0, sizeof (FICL_VM));
+ }
+
+ if (pVM->pStack)
+ stackDelete(pVM->pStack);
+ pVM->pStack = stackCreate(nPStack);
+
+ if (pVM->rStack)
+ stackDelete(pVM->rStack);
+ pVM->rStack = stackCreate(nRStack);
+
+#if FICL_WANT_FLOAT
+ if (pVM->fStack)
+ stackDelete(pVM->fStack);
+ pVM->fStack = stackCreate(nPStack);
+#endif
+
+ pVM->textOut = ficlTextOut;
+
+ vmReset(pVM);
+ return pVM;
+}
+
+
+/**************************************************************************
+ v m D e l e t e
+** Free all memory allocated to the specified VM and its subordinate
+** structures.
+**************************************************************************/
+void vmDelete (FICL_VM *pVM)
+{
+ if (pVM)
+ {
+ ficlFree(pVM->pStack);
+ ficlFree(pVM->rStack);
+#if FICL_WANT_FLOAT
+ ficlFree(pVM->fStack);
+#endif
+ ficlFree(pVM);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ v m E x e c u t e
+** Sets up the specified word to be run by the inner interpreter.
+** Executes the word's code part immediately, but in the case of
+** colon definition, the definition itself needs the inner interp
+** to complete. This does not happen until control reaches ficlExec
+**************************************************************************/
+void vmExecute(FICL_VM *pVM, FICL_WORD *pWord)
+{
+ pVM->runningWord = pWord;
+ pWord->code(pVM);
+ return;
+}
+
+
+/**************************************************************************
+ v m I n n e r L o o p
+** the mysterious inner interpreter...
+** This loop is the address interpreter that makes colon definitions
+** work. Upon entry, it assumes that the IP points to an entry in
+** a definition (the body of a colon word). It runs one word at a time
+** until something does vmThrow. The catcher for this is expected to exist
+** in the calling code.
+** vmThrow gets you out of this loop with a longjmp()
+** Visual C++ 5 chokes on this loop in Release mode. Aargh.
+**************************************************************************/
+#if INLINE_INNER_LOOP == 0
+void vmInnerLoop(FICL_VM *pVM)
+{
+ M_INNER_LOOP(pVM);
+}
+#endif
+#if 0
+/*
+** Recast inner loop that inlines tokens for control structures, arithmetic and stack operations,
+** as well as create does> : ; and various literals
+*/
+typedef enum
+{
+ PATCH = 0,
+ L0,
+ L1,
+ L2,
+ LMINUS1,
+ LMINUS2,
+ DROP,
+ SWAP,
+ DUP,
+ PICK,
+ ROLL,
+ FETCH,
+ STORE,
+ BRANCH,
+ CBRANCH,
+ LEAVE,
+ TO_R,
+ R_FROM,
+ EXIT;
+} OPCODE;
+
+typedef CELL *IPTYPE;
+
+void vmInnerLoop(FICL_VM *pVM)
+{
+ IPTYPE ip = pVM->ip;
+ FICL_STACK *pStack = pVM->pStack;
+
+ for (;;)
+ {
+ OPCODE o = (*ip++).i;
+ CELL c;
+ switch (o)
+ {
+ case L0:
+ stackPushINT(pStack, 0);
+ break;
+ case L1:
+ stackPushINT(pStack, 1);
+ break;
+ case L2:
+ stackPushINT(pStack, 2);
+ break;
+ case LMINUS1:
+ stackPushINT(pStack, -1);
+ break;
+ case LMINUS2:
+ stackPushINT(pStack, -2);
+ break;
+ case DROP:
+ stackDrop(pStack, 1);
+ break;
+ case SWAP:
+ stackRoll(pStack, 1);
+ break;
+ case DUP:
+ stackPick(pStack, 0);
+ break;
+ case PICK:
+ c = *ip++;
+ stackPick(pStack, c.i);
+ break;
+ case ROLL:
+ c = *ip++;
+ stackRoll(pStack, c.i);
+ break;
+ case EXIT:
+ return;
+ }
+ }
+
+ return;
+}
+#endif
+
+
+
+/**************************************************************************
+ v m G e t D i c t
+** Returns the address dictionary for this VM's system
+**************************************************************************/
+FICL_DICT *vmGetDict(FICL_VM *pVM)
+{
+ assert(pVM);
+ return pVM->pSys->dp;
+}
+
+
+/**************************************************************************
+ v m G e t S t r i n g
+** Parses a string out of the VM input buffer and copies up to the first
+** FICL_STRING_MAX characters to the supplied destination buffer, a
+** FICL_STRING. The destination string is NULL terminated.
+**
+** Returns the address of the first unused character in the dest buffer.
+**************************************************************************/
+char *vmGetString(FICL_VM *pVM, FICL_STRING *spDest, char delimiter)
+{
+ STRINGINFO si = vmParseStringEx(pVM, delimiter, 0);
+
+ if (SI_COUNT(si) > FICL_STRING_MAX)
+ {
+ SI_SETLEN(si, FICL_STRING_MAX);
+ }
+
+ strncpy(spDest->text, SI_PTR(si), SI_COUNT(si));
+ spDest->text[SI_COUNT(si)] = '\0';
+ spDest->count = (FICL_COUNT)SI_COUNT(si);
+
+ return spDest->text + SI_COUNT(si) + 1;
+}
+
+
+/**************************************************************************
+ v m G e t W o r d
+** vmGetWord calls vmGetWord0 repeatedly until it gets a string with
+** non-zero length.
+**************************************************************************/
+STRINGINFO vmGetWord(FICL_VM *pVM)
+{
+ STRINGINFO si = vmGetWord0(pVM);
+
+ if (SI_COUNT(si) == 0)
+ {
+ vmThrow(pVM, VM_RESTART);
+ }
+
+ return si;
+}
+
+
+/**************************************************************************
+ v m G e t W o r d 0
+** Skip leading whitespace and parse a space delimited word from the tib.
+** Returns the start address and length of the word. Updates the tib
+** to reflect characters consumed, including the trailing delimiter.
+** If there's nothing of interest in the tib, returns zero. This function
+** does not use vmParseString because it uses isspace() rather than a
+** single delimiter character.
+**************************************************************************/
+STRINGINFO vmGetWord0(FICL_VM *pVM)
+{
+ char *pSrc = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ STRINGINFO si;
+ FICL_UNS count = 0;
+ char ch = 0;
+
+ pSrc = skipSpace(pSrc, pEnd);
+ SI_SETPTR(si, pSrc);
+
+/*
+ for (ch = *pSrc; (pEnd != pSrc) && !isspace(ch); ch = *++pSrc)
+ {
+ count++;
+ }
+*/
+
+ /* Changed to make Purify happier. --lch */
+ for (;;)
+ {
+ if (pEnd == pSrc)
+ break;
+ ch = *pSrc;
+ if (isspace(ch))
+ break;
+ count++;
+ pSrc++;
+ }
+
+ SI_SETLEN(si, count);
+
+ if ((pEnd != pSrc) && isspace(ch)) /* skip one trailing delimiter */
+ pSrc++;
+
+ vmUpdateTib(pVM, pSrc);
+
+ return si;
+}
+
+
+/**************************************************************************
+ v m G e t W o r d T o P a d
+** Does vmGetWord and copies the result to the pad as a NULL terminated
+** string. Returns the length of the string. If the string is too long
+** to fit in the pad, it is truncated.
+**************************************************************************/
+int vmGetWordToPad(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ char *cp = (char *)pVM->pad;
+ si = vmGetWord(pVM);
+
+ if (SI_COUNT(si) > nPAD)
+ SI_SETLEN(si, nPAD);
+
+ strncpy(cp, SI_PTR(si), SI_COUNT(si));
+ cp[SI_COUNT(si)] = '\0';
+ return (int)(SI_COUNT(si));
+}
+
+
+/**************************************************************************
+ v m P a r s e S t r i n g
+** Parses a string out of the input buffer using the delimiter
+** specified. Skips leading delimiters, marks the start of the string,
+** and counts characters to the next delimiter it encounters. It then
+** updates the vm input buffer to consume all these chars, including the
+** trailing delimiter.
+** Returns the address and length of the parsed string, not including the
+** trailing delimiter.
+**************************************************************************/
+STRINGINFO vmParseString(FICL_VM *pVM, char delim)
+{
+ return vmParseStringEx(pVM, delim, 1);
+}
+
+STRINGINFO vmParseStringEx(FICL_VM *pVM, char delim, char fSkipLeading)
+{
+ STRINGINFO si;
+ char *pSrc = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ char ch;
+
+ if (fSkipLeading)
+ { /* skip lead delimiters */
+ while ((pSrc != pEnd) && (*pSrc == delim))
+ pSrc++;
+ }
+
+ SI_SETPTR(si, pSrc); /* mark start of text */
+
+ for (ch = *pSrc; (pSrc != pEnd)
+ && (ch != delim)
+ && (ch != '\r')
+ && (ch != '\n'); ch = *++pSrc)
+ {
+ ; /* find next delimiter or end of line */
+ }
+
+ /* set length of result */
+ SI_SETLEN(si, pSrc - SI_PTR(si));
+
+ if ((pSrc != pEnd) && (*pSrc == delim)) /* gobble trailing delimiter */
+ pSrc++;
+
+ vmUpdateTib(pVM, pSrc);
+ return si;
+}
+
+
+/**************************************************************************
+ v m P o p
+**
+**************************************************************************/
+CELL vmPop(FICL_VM *pVM)
+{
+ return stackPop(pVM->pStack);
+}
+
+
+/**************************************************************************
+ v m P u s h
+**
+**************************************************************************/
+void vmPush(FICL_VM *pVM, CELL c)
+{
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+
+/**************************************************************************
+ v m P o p I P
+**
+**************************************************************************/
+void vmPopIP(FICL_VM *pVM)
+{
+ pVM->ip = (IPTYPE)(stackPopPtr(pVM->rStack));
+ return;
+}
+
+
+/**************************************************************************
+ v m P u s h I P
+**
+**************************************************************************/
+void vmPushIP(FICL_VM *pVM, IPTYPE newIP)
+{
+ stackPushPtr(pVM->rStack, (void *)pVM->ip);
+ pVM->ip = newIP;
+ return;
+}
+
+
+/**************************************************************************
+ v m P u s h T i b
+** Binds the specified input string to the VM and clears >IN (the index)
+**************************************************************************/
+void vmPushTib(FICL_VM *pVM, char *text, FICL_INT nChars, TIB *pSaveTib)
+{
+ if (pSaveTib)
+ {
+ *pSaveTib = pVM->tib;
+ }
+
+ pVM->tib.cp = text;
+ pVM->tib.end = text + nChars;
+ pVM->tib.index = 0;
+}
+
+
+void vmPopTib(FICL_VM *pVM, TIB *pTib)
+{
+ if (pTib)
+ {
+ pVM->tib = *pTib;
+ }
+ return;
+}
+
+
+/**************************************************************************
+ v m Q u i t
+**
+**************************************************************************/
+void vmQuit(FICL_VM *pVM)
+{
+ stackReset(pVM->rStack);
+ pVM->fRestart = 0;
+ pVM->ip = NULL;
+ pVM->runningWord = NULL;
+ pVM->state = INTERPRET;
+ pVM->tib.cp = NULL;
+ pVM->tib.end = NULL;
+ pVM->tib.index = 0;
+ pVM->pad[0] = '\0';
+ pVM->sourceID.i = 0;
+ return;
+}
+
+
+/**************************************************************************
+ v m R e s e t
+**
+**************************************************************************/
+void vmReset(FICL_VM *pVM)
+{
+ vmQuit(pVM);
+ stackReset(pVM->pStack);
+#if FICL_WANT_FLOAT
+ stackReset(pVM->fStack);
+#endif
+ pVM->base = 10;
+ return;
+}
+
+
+/**************************************************************************
+ v m S e t T e x t O u t
+** Binds the specified output callback to the vm. If you pass NULL,
+** binds the default output function (ficlTextOut)
+**************************************************************************/
+void vmSetTextOut(FICL_VM *pVM, OUTFUNC textOut)
+{
+ if (textOut)
+ pVM->textOut = textOut;
+ else
+ pVM->textOut = ficlTextOut;
+
+ return;
+}
+
+
+/**************************************************************************
+ v m T e x t O u t
+** Feeds text to the vm's output callback
+**************************************************************************/
+void vmTextOut(FICL_VM *pVM, char *text, int fNewline)
+{
+ assert(pVM);
+ assert(pVM->textOut);
+ (pVM->textOut)(pVM, text, fNewline);
+
+ return;
+}
+
+
+/**************************************************************************
+ v m T h r o w
+**
+**************************************************************************/
+void vmThrow(FICL_VM *pVM, int except)
+{
+ if (pVM->pState)
+ longjmp(*(pVM->pState), except);
+}
+
+
+void vmThrowErr(FICL_VM *pVM, char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ vsprintf(pVM->pad, fmt, va);
+ vmTextOut(pVM, pVM->pad, 1);
+ va_end(va);
+ longjmp(*(pVM->pState), VM_ERREXIT);
+}
+
+
+/**************************************************************************
+ w o r d I s I m m e d i a t e
+**
+**************************************************************************/
+int wordIsImmediate(FICL_WORD *pFW)
+{
+ return ((pFW != NULL) && (pFW->flags & FW_IMMEDIATE));
+}
+
+
+/**************************************************************************
+ w o r d I s C o m p i l e O n l y
+**
+**************************************************************************/
+int wordIsCompileOnly(FICL_WORD *pFW)
+{
+ return ((pFW != NULL) && (pFW->flags & FW_COMPILE));
+}
+
+
+/**************************************************************************
+ s t r r e v
+**
+**************************************************************************/
+char *strrev( char *string )
+{ /* reverse a string in-place */
+ int i = strlen(string);
+ char *p1 = string; /* first char of string */
+ char *p2 = string + i - 1; /* last non-NULL char of string */
+ char c;
+
+ if (i > 1)
+ {
+ while (p1 < p2)
+ {
+ c = *p2;
+ *p2 = *p1;
+ *p1 = c;
+ p1++; p2--;
+ }
+ }
+
+ return string;
+}
+
+
+/**************************************************************************
+ d i g i t _ t o _ c h a r
+**
+**************************************************************************/
+char digit_to_char(int value)
+{
+ return digits[value];
+}
+
+
+/**************************************************************************
+ i s P o w e r O f T w o
+** Tests whether supplied argument is an integer power of 2 (2**n)
+** where 32 > n > 1, and returns n if so. Otherwise returns zero.
+**************************************************************************/
+int isPowerOfTwo(FICL_UNS u)
+{
+ int i = 1;
+ FICL_UNS t = 2;
+
+ for (; ((t <= u) && (t != 0)); i++, t <<= 1)
+ {
+ if (u == t)
+ return i;
+ }
+
+ return 0;
+}
+
+
+/**************************************************************************
+ l t o a
+**
+**************************************************************************/
+char *ltoa( FICL_INT value, char *string, int radix )
+{ /* convert long to string, any base */
+ char *cp = string;
+ int sign = ((radix == 10) && (value < 0));
+ int pwr;
+
+ assert(radix > 1);
+ assert(radix < 37);
+ assert(string);
+
+ pwr = isPowerOfTwo((FICL_UNS)radix);
+
+ if (sign)
+ value = -value;
+
+ if (value == 0)
+ *cp++ = '0';
+ else if (pwr != 0)
+ {
+ FICL_UNS v = (FICL_UNS) value;
+ FICL_UNS mask = (FICL_UNS) ~(-1 << pwr);
+ while (v)
+ {
+ *cp++ = digits[v & mask];
+ v >>= pwr;
+ }
+ }
+ else
+ {
+ UNSQR result;
+ DPUNS v;
+ v.hi = 0;
+ v.lo = (FICL_UNS)value;
+ while (v.lo)
+ {
+ result = ficlLongDiv(v, (FICL_UNS)radix);
+ *cp++ = digits[result.rem];
+ v.lo = result.quot;
+ }
+ }
+
+ if (sign)
+ *cp++ = '-';
+
+ *cp++ = '\0';
+
+ return strrev(string);
+}
+
+
+/**************************************************************************
+ u l t o a
+**
+**************************************************************************/
+char *ultoa(FICL_UNS value, char *string, int radix )
+{ /* convert long to string, any base */
+ char *cp = string;
+ DPUNS ud;
+ UNSQR result;
+
+ assert(radix > 1);
+ assert(radix < 37);
+ assert(string);
+
+ if (value == 0)
+ *cp++ = '0';
+ else
+ {
+ ud.hi = 0;
+ ud.lo = value;
+ result.quot = value;
+
+ while (ud.lo)
+ {
+ result = ficlLongDiv(ud, (FICL_UNS)radix);
+ ud.lo = result.quot;
+ *cp++ = digits[result.rem];
+ }
+ }
+
+ *cp++ = '\0';
+
+ return strrev(string);
+}
+
+
+/**************************************************************************
+ c a s e F o l d
+** Case folds a NULL terminated string in place. All characters
+** get converted to lower case.
+**************************************************************************/
+char *caseFold(char *cp)
+{
+ char *oldCp = cp;
+
+ while (*cp)
+ {
+ if (isupper(*cp))
+ *cp = (char)tolower(*cp);
+ cp++;
+ }
+
+ return oldCp;
+}
+
+
+/**************************************************************************
+ s t r i n c m p
+** (jws) simplified the code a bit in hopes of appeasing Purify
+**************************************************************************/
+int strincmp(char *cp1, char *cp2, FICL_UNS count)
+{
+ int i = 0;
+
+ for (; 0 < count; ++cp1, ++cp2, --count)
+ {
+ i = tolower(*cp1) - tolower(*cp2);
+ if (i != 0)
+ return i;
+ else if (*cp1 == '\0')
+ return 0;
+ }
+ return 0;
+}
+
+/**************************************************************************
+ s k i p S p a c e
+** Given a string pointer, returns a pointer to the first non-space
+** char of the string, or to the NULL terminator if no such char found.
+** If the pointer reaches "end" first, stop there. Pass NULL to
+** suppress this behavior.
+**************************************************************************/
+char *skipSpace(char *cp, char *end)
+{
+ assert(cp);
+
+ while ((cp != end) && isspace(*cp))
+ cp++;
+
+ return cp;
+}
+
+
diff --git a/win32.c b/win32.c
new file mode 100644
index 000000000000..7b9c44b38db9
--- /dev/null
+++ b/win32.c
@@ -0,0 +1,406 @@
+/*
+ * win32.c
+ * submitted to Ficl by Larry Hastings, larry@hastings.org
+ * Additional Win32 words by Guy Carver
+ *
+ * adds calling arbitrary DLL function calls from inside Ficl.
+ *
+ * note that Microsoft's own header files won't compile without
+ * "language extensions" (anonymous structs/unions) turned on.
+ * and even with that, it still gives a warning in rpcasync.h
+ * for something that compiles clean in C++. I turned it off.
+ *
+ */
+#pragma warning(disable : 4115)
+#include <stdio.h>
+#include <windows.h>
+#include <string.h>
+#include <direct.h>
+
+#include "ficl.h"
+
+static void loadLibrary(FICL_VM *pVM) /* ( address length -- hmodule ) */
+{
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *buf = (char *)_alloca(length + 1);
+ memcpy(buf, address, length);
+ buf[length] = 0;
+
+ stackPushINT(pVM->pStack, (int)LoadLibrary(buf));
+}
+
+static void getProcAddress(FICL_VM *pVM) /* ( address length hmodule -- ) */
+{
+ HMODULE hModule = (HMODULE)stackPopINT(pVM->pStack);
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *buf = (char *)_alloca(length + 1);
+ memcpy(buf, address, length);
+ buf[length] = 0;
+
+ stackPushINT(pVM->pStack, (int)GetProcAddress(hModule, buf));
+}
+
+
+static void freeLibrary(FICL_VM *pVM) /* ( hmodule -- ) */
+{
+ HMODULE hModule = (HMODULE)stackPopINT(pVM->pStack);
+ FreeLibrary(hModule);
+}
+
+
+static void uAddrToCString(FICL_VM *pVM) /* ( address length -- c-string ) */
+{
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *buf = (char *)malloc(length + 1);
+ memcpy(buf, address, length);
+ buf[length] = 0;
+ stackPushPtr(pVM->pStack, buf);
+ return;
+}
+
+
+static void callNativeFunction(FICL_VM *pVM) /* ( ... argcount fnaddress popstack -- returnvalue ) */
+{
+ int popstack = stackPopINT(pVM->pStack);
+ int fnaddress = stackPopINT(pVM->pStack);
+ int argcount = stackPopINT(pVM->pStack);
+ int returnvalue;
+
+ int i;
+ for (i = 0; i < argcount; i++)
+ {
+ int nextarg = stackPopINT(pVM->pStack);
+ __asm
+ {
+ mov eax, nextarg
+ push eax
+ }
+ }
+
+
+ __asm
+ {
+ call fnaddress
+ mov returnvalue, eax
+ }
+
+ /*
+ * if popstack is nonzero,
+ * the arguments are popped off the stack after calling
+ */
+ if (popstack)
+ {
+ argcount *= 4;
+ __asm add esp, argcount
+ }
+ stackPushINT(pVM->pStack, returnvalue);
+ return;
+}
+
+
+/**************************************************************************
+ v c a l l
+** Call a class method. (Contributed by Guy Carver)
+** FORTH: (params inst paramcnt vtableindex -- res )
+** INFO: paramcnt has msb set if return value is desired.
+**************************************************************************/
+static void VCall(FICL_VM *pVM)
+{
+ int ind,p,paramCnt;
+ void *instance;
+ int I;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,1);
+#endif
+
+ ind = POPINT() * 4;
+ paramCnt = POPINT();
+ instance = POPPTR(); //Get instance of class.
+
+ __asm push ecx //Save ecx.
+ __asm push esp //Save stack.
+
+ I = paramCnt & 0xFF; //Strip off any flags.
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,I,0);
+#endif
+
+ while(I--) //Loop for parameter count.
+ {
+ p = POPINT();
+ __asm
+ {
+ mov eax,p
+ push eax //Push on stack.
+ }
+ }
+ __asm
+ {
+ mov ecx,instance //Set ecx to instance.
+ mov eax,[ecx] //Get method pointer.
+ add eax,ind
+ call [eax] //Call method.
+ mov p,eax //Put result in p.
+ pop esp
+ pop ecx //Restore ecx and esp.
+ }
+ if (paramCnt & 0x80000000) //If supposed to return a result.
+ PUSHINT(p); //Store result.
+}
+
+
+#if 0
+//**************************************************************
+//Load forth file.
+//ENTER: pVM = Pointer to forth virtual machine.
+//FORTH: ( -<FileName>- )
+//**************************************************************
+static void ForthLoad(FICL_VM *pVM)
+{
+ char cp[256];
+ char fileName[256];
+ FILE *fp;
+ int result = 0;
+ CELL id;
+ int nLine = 0;
+ FICL_STRING *pFileName = (FICL_STRING *)fileName;
+
+ vmGetString(pVM,pFileName, ' ');
+
+ if (pFileName->count <= 0)
+ {
+ vmTextOut(pVM,"Type fload filename", 1);
+ return;
+ }
+
+ fp = fopen(pFileName->text, "r");
+ if (fp)
+ {
+ id = pVM->sourceID;
+ pVM->sourceID.p = (void *)fp; //Set input source id.
+
+ while (fgets(cp,256,fp)) //Read line.
+ {
+ int len = strlen(cp) - 1; //Get length.
+
+ nLine++; //Inc line count.
+ if (len > 0) //if length.
+ {
+ cp[len] = 0; //Make sure null terminated.
+ result = ficlExec(pVM,cp); //Execute line.
+ if ((result == VM_ERREXIT) //If exit.
+ || (result == VM_USEREXIT)
+ || (result == VM_QUIT))
+ {
+ pVM->sourceID = id;
+ fclose(fp);
+ vmThrowErr(pVM, "Error loading file <%s> line %d", pFileName->text, nLine);
+ break;
+ }
+ }
+ }
+ pVM->sourceID.i = -1;
+ ficlExec(pVM,""); //Empty line to flush any pending refills.
+ pVM->sourceID = id; //Reset source ID.
+ fclose(fp);
+ if (result == VM_USEREXIT) //If user exit.
+ vmThrow(pVM,VM_USEREXIT); //Resend user exit code.
+ }
+ else
+ {
+ vmTextOut(pVM,"Unable to open file: ", 0);
+ vmTextOut(pVM, pFileName->text,1);
+ }
+}
+
+//********************************************************************************
+//
+//********************************************************************************
+static STRINGINFO parseFileName(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ char *pSrc = vmGetInBuf(pVM);
+ si.cp = pSrc; /* mark start of text */
+ while ((*pSrc != ' ') && (*pSrc != 0) && (*pSrc != '\n'))
+ {
+ if (*(pSrc++) == '\\') /* find next delimiter or end */
+ si.cp = pSrc;
+ }
+ si.count = pSrc - si.cp; /* set length of result */
+ return(si);
+}
+
+//********************************************************************************
+//check for included file and load if not loaded.
+//********************************************************************************
+static void include(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW;
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_CODE pCreateParen = ficlLookup(pVM->pSys, "(create)")->code;
+
+ si = parseFileName(pVM);
+
+ if (si.count)
+ {
+ pFW = dictLookup(dp, si);
+ if (!pFW) //Forget word.
+ {
+ dictAppendWord2(dp, si, pCreateParen, FW_DEFAULT);
+ dictAllotCells(dp, 1);
+ ForthLoad(pVM);
+ }
+ }
+}
+
+//********************************************************************************
+//check for included file and kill it if its included to reload.
+//********************************************************************************
+static void reinclude(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW;
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_CODE pCreateParen = ficlLookup(pVM->pSys, "(create)")->code;
+
+ si = parseFileName(pVM);
+
+ if (si.count)
+ {
+ pFW = dictLookup(dp, si);
+ if (pFW) //Forget word.
+ {
+ hashForget(dp->pCompile,pFW->name);
+ dp->here = PTRtoCELL (pFW->name);
+ }
+
+ dictAppendWord2(dp, si, pCreateParen, FW_DEFAULT);
+ dictAllotCells(dp, 1);
+ ForthLoad(pVM);
+ }
+}
+
+#endif /* 0 */
+
+
+static void ficlWordGetTickCount(FICL_VM *pVM) /* ( -- ms ) */
+{
+ stackPushINT(pVM->pStack, (int)GetTickCount());
+}
+
+
+static void ficlDebugBreak(FICL_VM *pVM) /* ( -- ) */
+{
+ DebugBreak();
+ pVM = pVM;
+}
+
+
+static void ficlOutputDebugString(FICL_VM *pVM) /* ( c-addr u -- ) */
+{
+ int length = stackPopINT(pVM->pStack);
+ void *address = (void *)stackPopPtr(pVM->pStack);
+
+ char *buf = (char *)_alloca(length + 1);
+ memcpy(buf, address, length);
+ buf[length] = 0;
+
+ OutputDebugString(buf);
+}
+
+
+
+/**************************************************************************
+ f i c l C o m p i l e P l a t f o r m
+** Build Win32 platform extensions into the system dictionary
+**************************************************************************/
+void ficlCompilePlatform(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+ dictAppendWord(dp, "loadlibrary", loadLibrary, FW_DEFAULT);
+ dictAppendWord(dp, "getprocaddress", getProcAddress, FW_DEFAULT);
+ dictAppendWord(dp, "freelibrary", freeLibrary, FW_DEFAULT);
+ dictAppendWord(dp, "uaddr->cstring", uAddrToCString, FW_DEFAULT);
+ dictAppendWord(dp, "callnativefunction",
+ callNativeFunction,
+ FW_DEFAULT);
+ dictAppendWord(dp, "vcall", VCall, FW_DEFAULT);
+/*
+ dictAppendWord(dp, "include", include, FW_DEFAULT);
+ dictAppendWord(dp, "reinclude", reinclude, FW_DEFAULT);
+*/
+ dictAppendWord(dp, "GetTickCount", ficlWordGetTickCount, FW_DEFAULT);
+
+ dictAppendWord(dp, "debug-break", ficlDebugBreak, FW_DEFAULT);
+ dictAppendWord(dp, "output-debug-string", ficlOutputDebugString, FW_DEFAULT);
+
+ return;
+}
+
+
+
+
+/*
+**
+** Heavy, undocumented wizardry here.
+**
+** In Win32, like most OSes, the buffered file I/O functions in the
+** C API (functions that take a FILE * like fopen()) are implemented
+** on top of the raw file I/O functions (functions that take an int,
+** like open()). However, in Win32, these functions in turn are
+** implemented on top of the Win32 native file I/O functions (functions
+** that take a HANDLE, like CreateFile()). This behavior is undocumented
+** but easy to deduce by reading the CRT/SRC directory.
+**
+** The below mishmash of typedefs and defines were copied from
+** CRT/SRC/INTERNAL.H.
+**
+** --lch
+*/
+typedef struct {
+ long osfhnd; /* underlying OS file HANDLE */
+ char osfile; /* attributes of file (e.g., open in text mode?) */
+ char pipech; /* one char buffer for handles opened on pipes */
+#ifdef _MT
+ int lockinitflag;
+ CRITICAL_SECTION lock;
+#endif /* _MT */
+ } ioinfo;
+extern _CRTIMP ioinfo * __pioinfo[];
+
+#define IOINFO_L2E 5
+#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
+#define _pioinfo(i) ( __pioinfo[(i) >> IOINFO_L2E] + ((i) & (IOINFO_ARRAY_ELTS - \
+ 1)) )
+#define _osfhnd(i) ( _pioinfo(i)->osfhnd )
+
+
+int ftruncate(int fileno, size_t size)
+{
+ HANDLE hFile = (HANDLE)_osfhnd(fileno);
+ if (SetFilePointer(hFile, size, NULL, FILE_BEGIN) != size)
+ return 0;
+ return !SetEndOfFile(hFile);
+}
+
+#if 0
+unsigned long ficlNtohl(unsigned long number)
+{
+ return ntohl(number);
+}
+#endif
+
+
+
+
diff --git a/words.c b/words.c
new file mode 100644
index 000000000000..4d1a5a3fa5b7
--- /dev/null
+++ b/words.c
@@ -0,0 +1,5201 @@
+/*******************************************************************
+** w o r d s . c
+** Forth Inspired Command Language
+** ANS Forth CORE word-set written in C
+** Author: John Sadler (john_sadler@alum.mit.edu)
+** Created: 19 July 1997
+** $Id: words.c,v 1.17 2001-12-04 17:58:10-08 jsadler Exp jsadler $
+*******************************************************************/
+/*
+** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu)
+** All rights reserved.
+**
+** Get the latest Ficl release at http://ficl.sourceforge.net
+**
+** I am interested in hearing from anyone who uses ficl. If you have
+** a problem, a success story, a defect, an enhancement request, or
+** if you would like to contribute to the ficl release, please
+** contact me by email at the address above.
+**
+** L I C E N S E and D I S C L A I M E R
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "ficl.h"
+#include "math64.h"
+
+static void colonParen(FICL_VM *pVM);
+static void literalIm(FICL_VM *pVM);
+static int ficlParseWord(FICL_VM *pVM, STRINGINFO si);
+
+/*
+** Control structure building words use these
+** strings' addresses as markers on the stack to
+** check for structure completion.
+*/
+static char doTag[] = "do";
+static char colonTag[] = "colon";
+static char leaveTag[] = "leave";
+
+static char destTag[] = "target";
+static char origTag[] = "origin";
+
+static char caseTag[] = "case";
+static char ofTag[] = "of";
+static char fallthroughTag[] = "fallthrough";
+
+#if FICL_WANT_LOCALS
+static void doLocalIm(FICL_VM *pVM);
+static void do2LocalIm(FICL_VM *pVM);
+#endif
+
+
+/*
+** C O N T R O L S T R U C T U R E B U I L D E R S
+**
+** Push current dict location for later branch resolution.
+** The location may be either a branch target or a patch address...
+*/
+static void markBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ PUSHPTR(dp->here);
+ PUSHPTR(tag);
+ return;
+}
+
+static void markControlTag(FICL_VM *pVM, char *tag)
+{
+ PUSHPTR(tag);
+ return;
+}
+
+static void matchControlTag(FICL_VM *pVM, char *tag)
+{
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ cp = (char *)stackPopPtr(pVM->pStack);
+ /*
+ ** Changed the code below to compare the pointers first (by popular demand)
+ */
+ if ( (cp != tag) && strcmp(cp, tag) )
+ {
+ vmThrowErr(pVM, "Error -- unmatched control structure \"%s\"", tag);
+ }
+
+ return;
+}
+
+/*
+** Expect a branch target address on the param stack,
+** compile a literal offset from the current dict location
+** to the target address
+*/
+static void resolveBackBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ FICL_INT offset;
+ CELL *patchAddr;
+
+ matchControlTag(pVM, tag);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ offset = patchAddr - dp->here;
+ dictAppendCell(dp, LVALUEtoCELL(offset));
+
+ return;
+}
+
+
+/*
+** Expect a branch patch address on the param stack,
+** compile a literal offset from the patch location
+** to the current dict location
+*/
+static void resolveForwardBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ FICL_INT offset;
+ CELL *patchAddr;
+
+ matchControlTag(pVM, tag);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+
+ return;
+}
+
+/*
+** Match the tag to the top of the stack. If success,
+** sopy "here" address into the cell whose address is next
+** on the stack. Used by do..leave..loop.
+*/
+static void resolveAbsBranch(FICL_DICT *dp, FICL_VM *pVM, char *tag)
+{
+ CELL *patchAddr;
+ char *cp;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ cp = stackPopPtr(pVM->pStack);
+ /*
+ ** Changed the comparison below to compare the pointers first (by popular demand)
+ */
+ if ((cp != tag) && strcmp(cp, tag))
+ {
+ vmTextOut(pVM, "Warning -- Unmatched control word: ", 0);
+ vmTextOut(pVM, tag, 1);
+ }
+
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ *patchAddr = LVALUEtoCELL(dp->here);
+
+ return;
+}
+
+
+/**************************************************************************
+ f i c l P a r s e N u m b e r
+** Attempts to convert the NULL terminated string in the VM's pad to
+** a number using the VM's current base. If successful, pushes the number
+** onto the param stack and returns TRUE. Otherwise, returns FALSE.
+** (jws 8/01) Trailing decimal point causes a zero cell to be pushed. (See
+** the standard for DOUBLE wordset.
+**************************************************************************/
+
+int ficlParseNumber(FICL_VM *pVM, STRINGINFO si)
+{
+ FICL_INT accum = 0;
+ char isNeg = FALSE;
+ char hasDP = FALSE;
+ unsigned base = pVM->base;
+ char *cp = SI_PTR(si);
+ FICL_COUNT count= (FICL_COUNT)SI_COUNT(si);
+ unsigned ch;
+ unsigned digit;
+
+ if (count > 1)
+ {
+ switch (*cp)
+ {
+ case '-':
+ cp++;
+ count--;
+ isNeg = TRUE;
+ break;
+ case '+':
+ cp++;
+ count--;
+ isNeg = FALSE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((count > 0) && (cp[count-1] == '.')) /* detect & remove trailing decimal */
+ {
+ hasDP = TRUE;
+ count--;
+ }
+
+ if (count == 0) /* detect "+", "-", ".", "+." etc */
+ return FALSE;
+
+ while ((count--) && ((ch = *cp++) != '\0'))
+ {
+ if (!isalnum(ch))
+ return FALSE;
+
+ digit = ch - '0';
+
+ if (digit > 9)
+ digit = tolower(ch) - 'a' + 10;
+
+ if (digit >= base)
+ return FALSE;
+
+ accum = accum * base + digit;
+ }
+
+ if (hasDP) /* simple (required) DOUBLE support */
+ PUSHINT(0);
+
+ if (isNeg)
+ accum = -accum;
+
+ PUSHINT(accum);
+ if (pVM->state == COMPILE)
+ literalIm(pVM);
+
+ return TRUE;
+}
+
+
+/**************************************************************************
+ a d d & f r i e n d s
+**
+**************************************************************************/
+
+static void add(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i += stackGetTop(pVM->pStack).i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void sub(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i = stackGetTop(pVM->pStack).i - i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void mul(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i *= stackGetTop(pVM->pStack).i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void negate(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = -stackPopINT(pVM->pStack);
+ PUSHINT(i);
+ return;
+}
+
+static void ficlDiv(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ i = stackPopINT(pVM->pStack);
+ i = stackGetTop(pVM->pStack).i / i;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+/*
+** slash-mod CORE ( n1 n2 -- n3 n4 )
+** Divide n1 by n2, giving the single-cell remainder n3 and the single-cell
+** quotient n4. An ambiguous condition exists if n2 is zero. If n1 and n2
+** differ in sign, the implementation-defined result returned will be the
+** same as that returned by either the phrase
+** >R S>D R> FM/MOD or the phrase >R S>D R> SM/REM .
+** NOTE: Ficl complies with the second phrase (symmetric division)
+*/
+static void slashMod(FICL_VM *pVM)
+{
+ DPINT n1;
+ FICL_INT n2;
+ INTQR qr;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+ n2 = stackPopINT(pVM->pStack);
+ n1.lo = stackPopINT(pVM->pStack);
+ i64Extend(n1);
+
+ qr = m64SymmetricDivI(n1, n2);
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+static void onePlus(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i += 1;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void oneMinus(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i -= 1;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void twoMul(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i *= 2;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void twoDiv(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ i = stackGetTop(pVM->pStack).i;
+ i >>= 1;
+ stackSetTop(pVM->pStack, LVALUEtoCELL(i));
+ return;
+}
+
+static void mulDiv(FICL_VM *pVM)
+{
+ FICL_INT x, y, z;
+ DPINT prod;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 1);
+#endif
+ z = stackPopINT(pVM->pStack);
+ y = stackPopINT(pVM->pStack);
+ x = stackPopINT(pVM->pStack);
+
+ prod = m64MulI(x,y);
+ x = m64SymmetricDivI(prod, z).quot;
+
+ PUSHINT(x);
+ return;
+}
+
+
+static void mulDivRem(FICL_VM *pVM)
+{
+ FICL_INT x, y, z;
+ DPINT prod;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 2);
+#endif
+ z = stackPopINT(pVM->pStack);
+ y = stackPopINT(pVM->pStack);
+ x = stackPopINT(pVM->pStack);
+
+ prod = m64MulI(x,y);
+ qr = m64SymmetricDivI(prod, z);
+
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+
+/**************************************************************************
+ c o l o n d e f i n i t i o n s
+** Code to begin compiling a colon definition
+** This function sets the state to COMPILE, then creates a
+** new word whose name is the next word in the input stream
+** and whose code is colonParen.
+**************************************************************************/
+
+static void colon(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ pVM->state = COMPILE;
+ markControlTag(pVM, colonTag);
+ dictAppendWord2(dp, si, colonParen, FW_DEFAULT | FW_SMUDGE);
+#if FICL_WANT_LOCALS
+ pVM->pSys->nLocals = 0;
+#endif
+ return;
+}
+
+
+/**************************************************************************
+ c o l o n P a r e n
+** This is the code that executes a colon definition. It assumes that the
+** virtual machine is running a "next" loop (See the vm.c
+** for its implementation of member function vmExecute()). The colon
+** code simply copies the address of the first word in the list of words
+** to interpret into IP after saving its old value. When we return to the
+** "next" loop, the virtual machine will call the code for each word in
+** turn.
+**
+**************************************************************************/
+
+static void colonParen(FICL_VM *pVM)
+{
+ IPTYPE tempIP = (IPTYPE) (pVM->runningWord->param);
+ vmPushIP(pVM, tempIP);
+
+ return;
+}
+
+
+/**************************************************************************
+ s e m i c o l o n C o I m
+**
+** IMMEDIATE code for ";". This function sets the state to INTERPRET and
+** terminates a word under compilation by appending code for "(;)" to
+** the definition. TO DO: checks for leftover branch target tags on the
+** return stack and complains if any are found.
+**************************************************************************/
+static void semiParen(FICL_VM *pVM)
+{
+ vmPopIP(pVM);
+ return;
+}
+
+
+static void semicolonCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pSemiParen);
+ matchControlTag(pVM, colonTag);
+
+#if FICL_WANT_LOCALS
+ assert(pVM->pSys->pUnLinkParen);
+ if (pVM->pSys->nLocals > 0)
+ {
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ dictEmpty(pLoc, pLoc->pForthWords->size);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen));
+ }
+ pVM->pSys->nLocals = 0;
+#endif
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pSemiParen));
+ pVM->state = INTERPRET;
+ dictUnsmudge(dp);
+ return;
+}
+
+
+/**************************************************************************
+ e x i t
+** CORE
+** This function simply pops the previous instruction
+** pointer and returns to the "next" loop. Used for exiting from within
+** a definition. Note that exitParen is identical to semiParen - they
+** are in two different functions so that "see" can correctly identify
+** the end of a colon definition, even if it uses "exit".
+**************************************************************************/
+static void exitParen(FICL_VM *pVM)
+{
+ vmPopIP(pVM);
+ return;
+}
+
+static void exitCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ assert(pVM->pSys->pExitParen);
+ IGNORE(pVM);
+
+#if FICL_WANT_LOCALS
+ if (pVM->pSys->nLocals > 0)
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen));
+ }
+#endif
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pExitParen));
+ return;
+}
+
+
+/**************************************************************************
+ c o n s t a n t P a r e n
+** This is the run-time code for "constant". It simply returns the
+** contents of its word's first data cell.
+**
+**************************************************************************/
+
+void constantParen(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = pVM->runningWord;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ stackPush(pVM->pStack, pFW->param[0]);
+ return;
+}
+
+void twoConstParen(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = pVM->runningWord;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ stackPush(pVM->pStack, pFW->param[0]); /* lo */
+ stackPush(pVM->pStack, pFW->param[1]); /* hi */
+ return;
+}
+
+
+/**************************************************************************
+ c o n s t a n t
+** IMMEDIATE
+** Compiles a constant into the dictionary. Constants return their
+** value when invoked. Expects a value on top of the parm stack.
+**************************************************************************/
+
+static void constant(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ dictAppendWord2(dp, si, constantParen, FW_DEFAULT);
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ return;
+}
+
+
+static void twoConstant(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+ CELL c;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ c = stackPop(pVM->pStack);
+ dictAppendWord2(dp, si, twoConstParen, FW_DEFAULT);
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ dictAppendCell(dp, c);
+ return;
+}
+
+
+/**************************************************************************
+ d i s p l a y C e l l
+** Drop and print the contents of the cell at the top of the param
+** stack
+**************************************************************************/
+
+static void displayCell(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ c = stackPop(pVM->pStack);
+ ltoa((c).i, pVM->pad, pVM->base);
+ strcat(pVM->pad, " ");
+ vmTextOut(pVM, pVM->pad, 0);
+ return;
+}
+
+static void uDot(FICL_VM *pVM)
+{
+ FICL_UNS u;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ u = stackPopUNS(pVM->pStack);
+ ultoa(u, pVM->pad, pVM->base);
+ strcat(pVM->pad, " ");
+ vmTextOut(pVM, pVM->pad, 0);
+ return;
+}
+
+
+static void hexDot(FICL_VM *pVM)
+{
+ FICL_UNS u;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ u = stackPopUNS(pVM->pStack);
+ ultoa(u, pVM->pad, 16);
+ strcat(pVM->pad, " ");
+ vmTextOut(pVM, pVM->pad, 0);
+ return;
+}
+
+
+/**************************************************************************
+ s t r l e n
+** FICL ( c-string -- length )
+**
+** Returns the length of a C-style (zero-terminated) string.
+**
+** --lch
+**/
+static void ficlStrlen(FICL_VM *ficlVM)
+ {
+ char *address = (char *)stackPopPtr(ficlVM->pStack);
+ stackPushINT(ficlVM->pStack, strlen(address));
+ }
+
+
+/**************************************************************************
+ s p r i n t f
+** FICL ( i*x c-addr-fmt u-fmt c-addr-buffer u-buffer -- c-addr-buffer u-written success-flag )
+** Similar to the C sprintf() function. It formats into a buffer based on
+** a "format" string. Each character in the format string is copied verbatim
+** to the output buffer, until SPRINTF encounters a percent sign ("%").
+** SPRINTF then skips the percent sign, and examines the next character
+** (the "format character"). Here are the valid format characters:
+** s - read a C-ADDR U-LENGTH string from the stack and copy it to
+** the buffer
+** d - read a cell from the stack, format it as a string (base-10,
+** signed), and copy it to the buffer
+** x - same as d, except in base-16
+** u - same as d, but unsigned
+** % - output a literal percent-sign to the buffer
+** SPRINTF returns the c-addr-buffer argument unchanged, the number of bytes
+** written, and a flag indicating whether or not it ran out of space while
+** writing to the output buffer (TRUE if it ran out of space).
+**
+** If SPRINTF runs out of space in the buffer to store the formatted string,
+** it still continues parsing, in an effort to preserve your stack (otherwise
+** it might leave uneaten arguments behind).
+**
+** --lch
+**************************************************************************/
+static void ficlSprintf(FICL_VM *pVM) /* */
+{
+ int bufferLength = stackPopINT(pVM->pStack);
+ char *buffer = (char *)stackPopPtr(pVM->pStack);
+ char *bufferStart = buffer;
+
+ int formatLength = stackPopINT(pVM->pStack);
+ char *format = (char *)stackPopPtr(pVM->pStack);
+ char *formatStop = format + formatLength;
+
+ int base = 10;
+ int unsignedInteger = FALSE;
+
+ int append = FICL_TRUE;
+
+ while (format < formatStop)
+ {
+ char scratch[64];
+ char *source;
+ int actualLength;
+ int desiredLength;
+ int leadingZeroes;
+
+
+ if (*format != '%')
+ {
+ source = format;
+ actualLength = desiredLength = 1;
+ leadingZeroes = 0;
+ }
+ else
+ {
+ format++;
+ if (format == formatStop)
+ break;
+
+ leadingZeroes = (*format == '0');
+ if (leadingZeroes)
+ {
+ format++;
+ if (format == formatStop)
+ break;
+ }
+
+ desiredLength = isdigit(*format);
+ if (desiredLength)
+ {
+ desiredLength = strtoul(format, &format, 10);
+ if (format == formatStop)
+ break;
+ }
+ else if (*format == '*')
+ {
+ desiredLength = stackPopINT(pVM->pStack);
+ format++;
+ if (format == formatStop)
+ break;
+ }
+
+
+ switch (*format)
+ {
+ case 's':
+ case 'S':
+ {
+ actualLength = stackPopINT(pVM->pStack);
+ source = (char *)stackPopPtr(pVM->pStack);
+ break;
+ }
+ case 'x':
+ case 'X':
+ base = 16;
+ case 'u':
+ case 'U':
+ unsignedInteger = TRUE;
+ case 'd':
+ case 'D':
+ {
+ int integer = stackPopINT(pVM->pStack);
+ if (unsignedInteger)
+ ultoa(integer, scratch, base);
+ else
+ ltoa(integer, scratch, base);
+ base = 10;
+ unsignedInteger = FALSE;
+ source = scratch;
+ actualLength = strlen(scratch);
+ break;
+ }
+ case '%':
+ source = format;
+ actualLength = 1;
+ default:
+ continue;
+ }
+ }
+
+ if (append == FICL_TRUE)
+ {
+ if (!desiredLength)
+ desiredLength = actualLength;
+ if (desiredLength > bufferLength)
+ {
+ append = FICL_FALSE;
+ desiredLength = bufferLength;
+ }
+ while (desiredLength > actualLength)
+ {
+ *buffer++ = (char)((leadingZeroes) ? '0' : ' ');
+ bufferLength--;
+ desiredLength--;
+ }
+ memcpy(buffer, source, actualLength);
+ buffer += actualLength;
+ bufferLength -= actualLength;
+ }
+
+ format++;
+ }
+
+ stackPushPtr(pVM->pStack, bufferStart);
+ stackPushINT(pVM->pStack, buffer - bufferStart);
+ stackPushINT(pVM->pStack, append);
+}
+
+
+/**************************************************************************
+ d u p & f r i e n d s
+**
+**************************************************************************/
+
+static void depth(FICL_VM *pVM)
+{
+ int i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ i = stackDepth(pVM->pStack);
+ PUSHINT(i);
+ return;
+}
+
+
+static void drop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ stackDrop(pVM->pStack, 1);
+ return;
+}
+
+
+static void twoDrop(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ stackDrop(pVM->pStack, 2);
+ return;
+}
+
+
+static void dup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+ stackPick(pVM->pStack, 0);
+ return;
+}
+
+
+static void twoDup(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 4);
+#endif
+ stackPick(pVM->pStack, 1);
+ stackPick(pVM->pStack, 1);
+ return;
+}
+
+
+static void over(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 3);
+#endif
+ stackPick(pVM->pStack, 1);
+ return;
+}
+
+static void twoOver(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 6);
+#endif
+ stackPick(pVM->pStack, 3);
+ stackPick(pVM->pStack, 3);
+ return;
+}
+
+
+static void pick(FICL_VM *pVM)
+{
+ CELL c = stackPop(pVM->pStack);
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, c.i+1, c.i+2);
+#endif
+ stackPick(pVM->pStack, c.i);
+ return;
+}
+
+
+static void questionDup(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+ c = stackGetTop(pVM->pStack);
+
+ if (c.i != 0)
+ stackPick(pVM->pStack, 0);
+
+ return;
+}
+
+
+static void roll(FICL_VM *pVM)
+{
+ int i = stackPop(pVM->pStack).i;
+ i = (i > 0) ? i : 0;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, i+1, i+1);
+#endif
+ stackRoll(pVM->pStack, i);
+ return;
+}
+
+
+static void minusRoll(FICL_VM *pVM)
+{
+ int i = stackPop(pVM->pStack).i;
+ i = (i > 0) ? i : 0;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, i+1, i+1);
+#endif
+ stackRoll(pVM->pStack, -i);
+ return;
+}
+
+
+static void rot(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 3);
+#endif
+ stackRoll(pVM->pStack, 2);
+ return;
+}
+
+
+static void swap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+ stackRoll(pVM->pStack, 1);
+ return;
+}
+
+
+static void twoSwap(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 4);
+#endif
+ stackRoll(pVM->pStack, 3);
+ stackRoll(pVM->pStack, 3);
+ return;
+}
+
+
+/**************************************************************************
+ e m i t & f r i e n d s
+**
+**************************************************************************/
+
+static void emit(FICL_VM *pVM)
+{
+ char *cp = pVM->pad;
+ int i;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ i = stackPopINT(pVM->pStack);
+ cp[0] = (char)i;
+ cp[1] = '\0';
+ vmTextOut(pVM, cp, 0);
+ return;
+}
+
+
+static void cr(FICL_VM *pVM)
+{
+ vmTextOut(pVM, "", 1);
+ return;
+}
+
+
+static void commentLine(FICL_VM *pVM)
+{
+ char *cp = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ char ch = *cp;
+
+ while ((cp != pEnd) && (ch != '\r') && (ch != '\n'))
+ {
+ ch = *++cp;
+ }
+
+ /*
+ ** Cope with DOS or UNIX-style EOLs -
+ ** Check for /r, /n, /r/n, or /n/r end-of-line sequences,
+ ** and point cp to next char. If EOL is \0, we're done.
+ */
+ if (cp != pEnd)
+ {
+ cp++;
+
+ if ( (cp != pEnd) && (ch != *cp)
+ && ((*cp == '\r') || (*cp == '\n')) )
+ cp++;
+ }
+
+ vmUpdateTib(pVM, cp);
+ return;
+}
+
+
+/*
+** paren CORE
+** Compilation: Perform the execution semantics given below.
+** Execution: ( "ccc<paren>" -- )
+** Parse ccc delimited by ) (right parenthesis). ( is an immediate word.
+** The number of characters in ccc may be zero to the number of characters
+** in the parse area.
+**
+*/
+static void commentHang(FICL_VM *pVM)
+{
+ vmParseStringEx(pVM, ')', 0);
+ return;
+}
+
+
+/**************************************************************************
+ F E T C H & S T O R E
+**
+**************************************************************************/
+
+static void fetch(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ stackPush(pVM->pStack, *pCell);
+ return;
+}
+
+/*
+** two-fetch CORE ( a-addr -- x1 x2 )
+** Fetch the cell pair x1 x2 stored at a-addr. x2 is stored at a-addr and
+** x1 at the next consecutive cell. It is equivalent to the sequence
+** DUP CELL+ @ SWAP @ .
+*/
+static void twoFetch(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ stackPush(pVM->pStack, *pCell++);
+ stackPush(pVM->pStack, *pCell);
+ swap(pVM);
+ return;
+}
+
+/*
+** store CORE ( x a-addr -- )
+** Store x at a-addr.
+*/
+static void store(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ *pCell = stackPop(pVM->pStack);
+}
+
+/*
+** two-store CORE ( x1 x2 a-addr -- )
+** Store the cell pair x1 x2 at a-addr, with x2 at a-addr and x1 at the
+** next consecutive cell. It is equivalent to the sequence
+** SWAP OVER ! CELL+ ! .
+*/
+static void twoStore(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 3, 0);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ *pCell++ = stackPop(pVM->pStack);
+ *pCell = stackPop(pVM->pStack);
+}
+
+static void plusStore(FICL_VM *pVM)
+{
+ CELL *pCell;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pCell = (CELL *)stackPopPtr(pVM->pStack);
+ pCell->i += stackPop(pVM->pStack).i;
+}
+
+
+static void quadFetch(FICL_VM *pVM)
+{
+ UNS32 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pw = (UNS32 *)stackPopPtr(pVM->pStack);
+ PUSHUNS((FICL_UNS)*pw);
+ return;
+}
+
+static void quadStore(FICL_VM *pVM)
+{
+ UNS32 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pw = (UNS32 *)stackPopPtr(pVM->pStack);
+ *pw = (UNS32)(stackPop(pVM->pStack).u);
+}
+
+static void wFetch(FICL_VM *pVM)
+{
+ UNS16 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pw = (UNS16 *)stackPopPtr(pVM->pStack);
+ PUSHUNS((FICL_UNS)*pw);
+ return;
+}
+
+static void wStore(FICL_VM *pVM)
+{
+ UNS16 *pw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pw = (UNS16 *)stackPopPtr(pVM->pStack);
+ *pw = (UNS16)(stackPop(pVM->pStack).u);
+}
+
+static void cFetch(FICL_VM *pVM)
+{
+ UNS8 *pc;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ pc = (UNS8 *)stackPopPtr(pVM->pStack);
+ PUSHUNS((FICL_UNS)*pc);
+ return;
+}
+
+static void cStore(FICL_VM *pVM)
+{
+ UNS8 *pc;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ pc = (UNS8 *)stackPopPtr(pVM->pStack);
+ *pc = (UNS8)(stackPop(pVM->pStack).u);
+}
+
+
+/**************************************************************************
+ b r a n c h P a r e n
+**
+** Runtime for "(branch)" -- expects a literal offset in the next
+** compilation address, and branches to that location.
+**************************************************************************/
+
+static void branchParen(FICL_VM *pVM)
+{
+ vmBranchRelative(pVM, *(int *)(pVM->ip));
+ return;
+}
+
+
+/**************************************************************************
+ b r a n c h 0
+** Runtime code for "(branch0)"; pop a flag from the stack,
+** branch if 0. fall through otherwise. The heart of "if" and "until".
+**************************************************************************/
+
+static void branch0(FICL_VM *pVM)
+{
+ FICL_UNS flag;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ flag = stackPopUNS(pVM->pStack);
+
+ if (flag)
+ { /* fall through */
+ vmBranchRelative(pVM, 1);
+ }
+ else
+ { /* take branch (to else/endif/begin) */
+ vmBranchRelative(pVM, *(int *)(pVM->ip));
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ i f C o I m
+** IMMEDIATE COMPILE-ONLY
+** Compiles code for a conditional branch into the dictionary
+** and pushes the branch patch address on the stack for later
+** patching by ELSE or THEN/ENDIF.
+**************************************************************************/
+
+static void ifCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranch0);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0));
+ markBranch(dp, pVM, origTag);
+ dictAppendUNS(dp, 1);
+ return;
+}
+
+
+/**************************************************************************
+ e l s e C o I m
+**
+** IMMEDIATE COMPILE-ONLY
+** compiles an "else"...
+** 1) Compile a branch and a patch address; the address gets patched
+** by "endif" to point past the "else" code.
+** 2) Pop the the "if" patch address
+** 3) Patch the "if" branch to point to the current compile address.
+** 4) Push the "else" patch address. ("endif" patches this to jump past
+** the "else" code.
+**************************************************************************/
+
+static void elseCoIm(FICL_VM *pVM)
+{
+ CELL *patchAddr;
+ FICL_INT offset;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranchParen);
+ /* (1) compile branch runtime */
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+ matchControlTag(pVM, origTag);
+ patchAddr =
+ (CELL *)stackPopPtr(pVM->pStack); /* (2) pop "if" patch addr */
+ markBranch(dp, pVM, origTag); /* (4) push "else" patch addr */
+ dictAppendUNS(dp, 1); /* (1) compile patch placeholder */
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset); /* (3) Patch "if" */
+
+ return;
+}
+
+
+/**************************************************************************
+ e n d i f C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void endifCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ resolveForwardBranch(dp, pVM, origTag);
+ return;
+}
+
+
+/**************************************************************************
+ c a s e C o I m
+** IMMEDIATE COMPILE-ONLY
+**
+**
+** At compile-time, a CASE-SYS (see DPANS94 6.2.0873) looks like this:
+** i*addr i caseTag
+** and an OF-SYS (see DPANS94 6.2.1950) looks like this:
+** i*addr i caseTag addr ofTag
+** The integer under caseTag is the count of fixup addresses that branch
+** to ENDCASE.
+**************************************************************************/
+
+static void caseCoIm(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+
+ PUSHUNS(0);
+ markControlTag(pVM, caseTag);
+ return;
+}
+
+
+/**************************************************************************
+ e n d c a s eC o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void endcaseCoIm(FICL_VM *pVM)
+{
+ FICL_UNS fixupCount;
+ FICL_DICT *dp;
+ CELL *patchAddr;
+ FICL_INT offset;
+
+ assert(pVM->pSys->pDrop);
+
+ /*
+ ** if the last OF ended with FALLTHROUGH,
+ ** just add the FALLTHROUGH fixup to the
+ ** ENDOF fixups
+ */
+ if (stackGetTop(pVM->pStack).p == fallthroughTag)
+ {
+ matchControlTag(pVM, fallthroughTag);
+ patchAddr = POPPTR();
+ matchControlTag(pVM, caseTag);
+ fixupCount = POPUNS();
+ PUSHPTR(patchAddr);
+ PUSHUNS(fixupCount + 1);
+ markControlTag(pVM, caseTag);
+ }
+
+ matchControlTag(pVM, caseTag);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ fixupCount = POPUNS();
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, fixupCount, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDrop));
+
+ while (fixupCount--)
+ {
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+ }
+ return;
+}
+
+
+static void ofParen(FICL_VM *pVM)
+{
+ FICL_UNS a, b;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+
+ a = POPUNS();
+ b = stackGetTop(pVM->pStack).u;
+
+ if (a == b)
+ { /* fall through */
+ stackDrop(pVM->pStack, 1);
+ vmBranchRelative(pVM, 1);
+ }
+ else
+ { /* take branch to next of or endswitch */
+ vmBranchRelative(pVM, *(int *)(pVM->ip));
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ o f C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void ofCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ CELL *fallthroughFixup = NULL;
+
+ assert(pVM->pSys->pBranch0);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 3);
+#endif
+
+ if (stackGetTop(pVM->pStack).p == fallthroughTag)
+ {
+ matchControlTag(pVM, fallthroughTag);
+ fallthroughFixup = POPPTR();
+ }
+
+ matchControlTag(pVM, caseTag);
+
+ markControlTag(pVM, caseTag);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pOfParen));
+ markBranch(dp, pVM, ofTag);
+ dictAppendUNS(dp, 2);
+
+ if (fallthroughFixup != NULL)
+ {
+ FICL_INT offset = dp->here - fallthroughFixup;
+ *fallthroughFixup = LVALUEtoCELL(offset);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ e n d o f C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void endofCoIm(FICL_VM *pVM)
+{
+ CELL *patchAddr;
+ FICL_UNS fixupCount;
+ FICL_INT offset;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 3);
+#endif
+
+ assert(pVM->pSys->pBranchParen);
+
+ /* ensure we're in an OF, */
+ matchControlTag(pVM, ofTag);
+ /* grab the address of the branch location after the OF */
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ /* ensure we're also in a "case" */
+ matchControlTag(pVM, caseTag);
+ /* grab the current number of ENDOF fixups */
+ fixupCount = POPUNS();
+
+ /* compile branch runtime */
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* push a new ENDOF fixup, the updated count of ENDOF fixups, and the caseTag */
+ PUSHPTR(dp->here);
+ PUSHUNS(fixupCount + 1);
+ markControlTag(pVM, caseTag);
+
+ /* reserve space for the ENDOF fixup */
+ dictAppendUNS(dp, 2);
+
+ /* and patch the original OF */
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+}
+
+
+/**************************************************************************
+ f a l l t h r o u g h C o I m
+** IMMEDIATE COMPILE-ONLY
+**************************************************************************/
+
+static void fallthroughCoIm(FICL_VM *pVM)
+{
+ CELL *patchAddr;
+ FICL_INT offset;
+ FICL_DICT *dp = vmGetDict(pVM);
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 4, 3);
+#endif
+
+ /* ensure we're in an OF, */
+ matchControlTag(pVM, ofTag);
+ /* grab the address of the branch location after the OF */
+ patchAddr = (CELL *)stackPopPtr(pVM->pStack);
+ /* ensure we're also in a "case" */
+ matchControlTag(pVM, caseTag);
+
+ /* okay, here we go. put the case tag back. */
+ markControlTag(pVM, caseTag);
+
+ /* compile branch runtime */
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* push a new FALLTHROUGH fixup and the fallthroughTag */
+ PUSHPTR(dp->here);
+ markControlTag(pVM, fallthroughTag);
+
+ /* reserve space for the FALLTHROUGH fixup */
+ dictAppendUNS(dp, 2);
+
+ /* and patch the original OF */
+ offset = dp->here - patchAddr;
+ *patchAddr = LVALUEtoCELL(offset);
+}
+
+/**************************************************************************
+ h a s h
+** hash ( c-addr u -- code)
+** calculates hashcode of specified string and leaves it on the stack
+**************************************************************************/
+
+static void hash(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ SI_SETLEN(si, stackPopUNS(pVM->pStack));
+ SI_SETPTR(si, stackPopPtr(pVM->pStack));
+ PUSHUNS(hashHashCode(si));
+ return;
+}
+
+
+/**************************************************************************
+ i n t e r p r e t
+** This is the "user interface" of a Forth. It does the following:
+** while there are words in the VM's Text Input Buffer
+** Copy next word into the pad (vmGetWord)
+** Attempt to find the word in the dictionary (dictLookup)
+** If successful, execute the word.
+** Otherwise, attempt to convert the word to a number (isNumber)
+** If successful, push the number onto the parameter stack.
+** Otherwise, print an error message and exit loop...
+** End Loop
+**
+** From the standard, section 3.4
+** Text interpretation (see 6.1.1360 EVALUATE and 6.1.2050 QUIT) shall
+** repeat the following steps until either the parse area is empty or an
+** ambiguous condition exists:
+** a) Skip leading spaces and parse a name (see 3.4.1);
+**************************************************************************/
+
+static void interpret(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ int i;
+ FICL_SYSTEM *pSys;
+
+ assert(pVM);
+
+ pSys = pVM->pSys;
+ si = vmGetWord0(pVM);
+
+ /*
+ ** Get next word...if out of text, we're done.
+ */
+ if (si.count == 0)
+ {
+ vmThrow(pVM, VM_OUTOFTEXT);
+ }
+
+ /*
+ ** Attempt to find the incoming token in the dictionary. If that fails...
+ ** run the parse chain against the incoming token until somebody eats it.
+ ** Otherwise emit an error message and give up.
+ ** Although ficlParseWord could be part of the parse list, I've hard coded it
+ ** in for robustness. ficlInitSystem adds the other default steps to the list.
+ */
+ if (ficlParseWord(pVM, si))
+ return;
+
+ for (i=0; i < FICL_MAX_PARSE_STEPS; i++)
+ {
+ FICL_WORD *pFW = pSys->parseList[i];
+
+ if (pFW == NULL)
+ break;
+
+ if (pFW->code == parseStepParen)
+ {
+ FICL_PARSE_STEP pStep;
+ pStep = (FICL_PARSE_STEP)(pFW->param->fn);
+ if ((*pStep)(pVM, si))
+ return;
+ }
+ else
+ {
+ stackPushPtr(pVM->pStack, SI_PTR(si));
+ stackPushUNS(pVM->pStack, SI_COUNT(si));
+ ficlExecXT(pVM, pFW);
+ if (stackPopINT(pVM->pStack))
+ return;
+ }
+ }
+
+ i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si));
+
+ return; /* back to inner interpreter */
+}
+
+
+/**************************************************************************
+ f i c l P a r s e W o r d
+** From the standard, section 3.4
+** b) Search the dictionary name space (see 3.4.2). If a definition name
+** matching the string is found:
+** 1.if interpreting, perform the interpretation semantics of the definition
+** (see 3.4.3.2), and continue at a);
+** 2.if compiling, perform the compilation semantics of the definition
+** (see 3.4.3.3), and continue at a).
+**
+** c) If a definition name matching the string is not found, attempt to
+** convert the string to a number (see 3.4.1.3). If successful:
+** 1.if interpreting, place the number on the data stack, and continue at a);
+** 2.if compiling, compile code that when executed will place the number on
+** the stack (see 6.1.1780 LITERAL), and continue at a);
+**
+** d) If unsuccessful, an ambiguous condition exists (see 3.4.4).
+**
+** (jws 4/01) Modified to be a FICL_PARSE_STEP
+**************************************************************************/
+static int ficlParseWord(FICL_VM *pVM, STRINGINFO si)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *tempFW;
+
+#if FICL_ROBUST
+ dictCheck(dp, pVM, 0);
+ vmCheckStack(pVM, 0, 0);
+#endif
+
+#if FICL_WANT_LOCALS
+ if (pVM->pSys->nLocals > 0)
+ {
+ tempFW = ficlLookupLoc(pVM->pSys, si);
+ }
+ else
+#endif
+ tempFW = dictLookup(dp, si);
+
+ if (pVM->state == INTERPRET)
+ {
+ if (tempFW != NULL)
+ {
+ if (wordIsCompileOnly(tempFW))
+ {
+ vmThrowErr(pVM, "Error: Compile only!");
+ }
+
+ vmExecute(pVM, tempFW);
+ return FICL_TRUE;
+ }
+ }
+
+ else /* (pVM->state == COMPILE) */
+ {
+ if (tempFW != NULL)
+ {
+ if (wordIsImmediate(tempFW))
+ {
+ vmExecute(pVM, tempFW);
+ }
+ else
+ {
+ dictAppendCell(dp, LVALUEtoCELL(tempFW));
+ }
+ return FICL_TRUE;
+ }
+ }
+
+ return FICL_FALSE;
+}
+
+
+/*
+** Surrogate precompiled parse step for ficlParseWord (this step is hard coded in
+** INTERPRET)
+*/
+static void lookup(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ SI_SETLEN(si, stackPopUNS(pVM->pStack));
+ SI_SETPTR(si, stackPopPtr(pVM->pStack));
+ stackPushINT(pVM->pStack, ficlParseWord(pVM, si));
+ return;
+}
+
+
+/**************************************************************************
+ p a r e n P a r s e S t e p
+** (parse-step) ( c-addr u -- flag )
+** runtime for a precompiled parse step - pop a counted string off the
+** stack, run the parse step against it, and push the result flag (FICL_TRUE
+** if success, FICL_FALSE otherwise).
+**************************************************************************/
+
+void parseStepParen(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ FICL_WORD *pFW = pVM->runningWord;
+ FICL_PARSE_STEP pStep = (FICL_PARSE_STEP)(pFW->param->fn);
+
+ SI_SETLEN(si, stackPopINT(pVM->pStack));
+ SI_SETPTR(si, stackPopPtr(pVM->pStack));
+
+ PUSHINT((*pStep)(pVM, si));
+
+ return;
+}
+
+
+static void addParseStep(FICL_VM *pVM)
+{
+ FICL_WORD *pStep;
+ FICL_DICT *pd = vmGetDict(pVM);
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ pStep = (FICL_WORD *)(stackPop(pVM->pStack).p);
+ if ((pStep != NULL) && isAFiclWord(pd, pStep))
+ ficlAddParseStep(pVM->pSys, pStep);
+ return;
+}
+
+
+/**************************************************************************
+ l i t e r a l P a r e n
+**
+** This is the runtime for (literal). It assumes that it is part of a colon
+** definition, and that the next CELL contains a value to be pushed on the
+** parameter stack at runtime. This code is compiled by "literal".
+**
+**************************************************************************/
+
+static void literalParen(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ PUSHINT(*(FICL_INT *)(pVM->ip));
+ vmBranchRelative(pVM, 1);
+ return;
+}
+
+static void twoLitParen(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ PUSHINT(*((FICL_INT *)(pVM->ip)+1));
+ PUSHINT(*(FICL_INT *)(pVM->ip));
+ vmBranchRelative(pVM, 2);
+ return;
+}
+
+
+/**************************************************************************
+ l i t e r a l I m
+**
+** IMMEDIATE code for "literal". This function gets a value from the stack
+** and compiles it into the dictionary preceded by the code for "(literal)".
+** IMMEDIATE
+**************************************************************************/
+
+static void literalIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ assert(pVM->pSys->pLitParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pLitParen));
+ dictAppendCell(dp, stackPop(pVM->pStack));
+
+ return;
+}
+
+
+static void twoLiteralIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ assert(pVM->pSys->pTwoLitParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pTwoLitParen));
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ dictAppendCell(dp, stackPop(pVM->pStack));
+
+ return;
+}
+
+/**************************************************************************
+ l o g i c a n d c o m p a r i s o n s
+**
+**************************************************************************/
+
+static void zeroEquals(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ c.i = FICL_BOOL(stackPopINT(pVM->pStack) == 0);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void zeroLess(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ c.i = FICL_BOOL(stackPopINT(pVM->pStack) < 0);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void zeroGreater(FICL_VM *pVM)
+{
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ c.i = FICL_BOOL(stackPopINT(pVM->pStack) > 0);
+ stackPush(pVM->pStack, c);
+ return;
+}
+
+static void isEqual(FICL_VM *pVM)
+{
+ CELL x, y;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(FICL_BOOL(x.i == y.i));
+ return;
+}
+
+static void isLess(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ y = stackPop(pVM->pStack);
+ x = stackPop(pVM->pStack);
+ PUSHINT(FICL_BOOL(x.i < y.i));
+ return;
+}
+
+static void uIsLess(FICL_VM *pVM)
+{
+ FICL_UNS u1, u2;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ u2 = stackPopUNS(pVM->pStack);
+ u1 = stackPopUNS(pVM->pStack);
+ PUSHINT(FICL_BOOL(u1 < u2));
+ return;
+}
+
+static void isGreater(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ y = stackPop(pVM->pStack);
+ x = stackPop(pVM->pStack);
+ PUSHINT(FICL_BOOL(x.i > y.i));
+ return;
+}
+
+static void bitwiseAnd(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(x.i & y.i);
+ return;
+}
+
+static void bitwiseOr(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(x.i | y.i);
+ return;
+}
+
+static void bitwiseXor(FICL_VM *pVM)
+{
+ CELL x, y;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ y = stackPop(pVM->pStack);
+ PUSHINT(x.i ^ y.i);
+ return;
+}
+
+static void bitwiseNot(FICL_VM *pVM)
+{
+ CELL x;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+ x = stackPop(pVM->pStack);
+ PUSHINT(~x.i);
+ return;
+}
+
+
+/**************************************************************************
+ D o / L o o p
+** do -- IMMEDIATE COMPILE ONLY
+** Compiles code to initialize a loop: compile (do),
+** allot space to hold the "leave" address, push a branch
+** target address for the loop.
+** (do) -- runtime for "do"
+** pops index and limit from the p stack and moves them
+** to the r stack, then skips to the loop body.
+** loop -- IMMEDIATE COMPILE ONLY
+** +loop
+** Compiles code for the test part of a loop:
+** compile (loop), resolve forward branch from "do", and
+** copy "here" address to the "leave" address allotted by "do"
+** i,j,k -- COMPILE ONLY
+** Runtime: Push loop indices on param stack (i is innermost loop...)
+** Note: each loop has three values on the return stack:
+** ( R: leave limit index )
+** "leave" is the absolute address of the next cell after the loop
+** limit and index are the loop control variables.
+** leave -- COMPILE ONLY
+** Runtime: pop the loop control variables, then pop the
+** "leave" address and jump (absolute) there.
+**************************************************************************/
+
+static void doCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pDoParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDoParen));
+ /*
+ ** Allot space for a pointer to the end
+ ** of the loop - "leave" uses this...
+ */
+ markBranch(dp, pVM, leaveTag);
+ dictAppendUNS(dp, 0);
+ /*
+ ** Mark location of head of loop...
+ */
+ markBranch(dp, pVM, doTag);
+
+ return;
+}
+
+
+static void doParen(FICL_VM *pVM)
+{
+ CELL index, limit;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ index = stackPop(pVM->pStack);
+ limit = stackPop(pVM->pStack);
+
+ /* copy "leave" target addr to stack */
+ stackPushPtr(pVM->rStack, *(pVM->ip++));
+ stackPush(pVM->rStack, limit);
+ stackPush(pVM->rStack, index);
+
+ return;
+}
+
+
+static void qDoCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pQDoParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pQDoParen));
+ /*
+ ** Allot space for a pointer to the end
+ ** of the loop - "leave" uses this...
+ */
+ markBranch(dp, pVM, leaveTag);
+ dictAppendUNS(dp, 0);
+ /*
+ ** Mark location of head of loop...
+ */
+ markBranch(dp, pVM, doTag);
+
+ return;
+}
+
+
+static void qDoParen(FICL_VM *pVM)
+{
+ CELL index, limit;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ index = stackPop(pVM->pStack);
+ limit = stackPop(pVM->pStack);
+
+ /* copy "leave" target addr to stack */
+ stackPushPtr(pVM->rStack, *(pVM->ip++));
+
+ if (limit.u == index.u)
+ {
+ vmPopIP(pVM);
+ }
+ else
+ {
+ stackPush(pVM->rStack, limit);
+ stackPush(pVM->rStack, index);
+ }
+
+ return;
+}
+
+
+/*
+** Runtime code to break out of a do..loop construct
+** Drop the loop control variables; the branch address
+** past "loop" is next on the return stack.
+*/
+static void leaveCo(FICL_VM *pVM)
+{
+ /* almost unloop */
+ stackDrop(pVM->rStack, 2);
+ /* exit */
+ vmPopIP(pVM);
+ return;
+}
+
+
+static void unloopCo(FICL_VM *pVM)
+{
+ stackDrop(pVM->rStack, 3);
+ return;
+}
+
+
+static void loopCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pLoopParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pLoopParen));
+ resolveBackBranch(dp, pVM, doTag);
+ resolveAbsBranch(dp, pVM, leaveTag);
+ return;
+}
+
+
+static void plusLoopCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pPLoopParen);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pPLoopParen));
+ resolveBackBranch(dp, pVM, doTag);
+ resolveAbsBranch(dp, pVM, leaveTag);
+ return;
+}
+
+
+static void loopParen(FICL_VM *pVM)
+{
+ FICL_INT index = stackGetTop(pVM->rStack).i;
+ FICL_INT limit = stackFetch(pVM->rStack, 1).i;
+
+ index++;
+
+ if (index >= limit)
+ {
+ stackDrop(pVM->rStack, 3); /* nuke the loop indices & "leave" addr */
+ vmBranchRelative(pVM, 1); /* fall through the loop */
+ }
+ else
+ { /* update index, branch to loop head */
+ stackSetTop(pVM->rStack, LVALUEtoCELL(index));
+ vmBranchRelative(pVM, *(int *)(pVM->ip));
+ }
+
+ return;
+}
+
+
+static void plusLoopParen(FICL_VM *pVM)
+{
+ FICL_INT index,limit,increment;
+ int flag;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ index = stackGetTop(pVM->rStack).i;
+ limit = stackFetch(pVM->rStack, 1).i;
+ increment = POP().i;
+
+ index += increment;
+
+ if (increment < 0)
+ flag = (index < limit);
+ else
+ flag = (index >= limit);
+
+ if (flag)
+ {
+ stackDrop(pVM->rStack, 3); /* nuke the loop indices & "leave" addr */
+ vmBranchRelative(pVM, 1); /* fall through the loop */
+ }
+ else
+ { /* update index, branch to loop head */
+ stackSetTop(pVM->rStack, LVALUEtoCELL(index));
+ vmBranchRelative(pVM, *(int *)(pVM->ip));
+ }
+
+ return;
+}
+
+
+static void loopICo(FICL_VM *pVM)
+{
+ CELL index = stackGetTop(pVM->rStack);
+ stackPush(pVM->pStack, index);
+
+ return;
+}
+
+
+static void loopJCo(FICL_VM *pVM)
+{
+ CELL index = stackFetch(pVM->rStack, 3);
+ stackPush(pVM->pStack, index);
+
+ return;
+}
+
+
+static void loopKCo(FICL_VM *pVM)
+{
+ CELL index = stackFetch(pVM->rStack, 6);
+ stackPush(pVM->pStack, index);
+
+ return;
+}
+
+
+/**************************************************************************
+ r e t u r n s t a c k
+**
+**************************************************************************/
+static void toRStack(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ stackPush(pVM->rStack, POP());
+}
+
+static void fromRStack(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ PUSH(stackPop(pVM->rStack));
+}
+
+static void fetchRStack(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ PUSH(stackGetTop(pVM->rStack));
+}
+
+static void twoToR(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+ stackRoll(pVM->pStack, 1);
+ stackPush(pVM->rStack, stackPop(pVM->pStack));
+ stackPush(pVM->rStack, stackPop(pVM->pStack));
+ return;
+}
+
+static void twoRFrom(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ stackPush(pVM->pStack, stackPop(pVM->rStack));
+ stackPush(pVM->pStack, stackPop(pVM->rStack));
+ stackRoll(pVM->pStack, 1);
+ return;
+}
+
+static void twoRFetch(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+ stackPush(pVM->pStack, stackFetch(pVM->rStack, 1));
+ stackPush(pVM->pStack, stackFetch(pVM->rStack, 0));
+ return;
+}
+
+
+/**************************************************************************
+ v a r i a b l e
+**
+**************************************************************************/
+
+static void variableParen(FICL_VM *pVM)
+{
+ FICL_WORD *fw;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ fw = pVM->runningWord;
+ PUSHPTR(fw->param);
+}
+
+
+static void variable(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ dictAppendWord2(dp, si, variableParen, FW_DEFAULT);
+ dictAllotCells(dp, 1);
+ return;
+}
+
+
+static void twoVariable(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ dictAppendWord2(dp, si, variableParen, FW_DEFAULT);
+ dictAllotCells(dp, 2);
+ return;
+}
+
+
+/**************************************************************************
+ b a s e & f r i e n d s
+**
+**************************************************************************/
+
+static void base(FICL_VM *pVM)
+{
+ CELL *pBase;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pBase = (CELL *)(&pVM->base);
+ stackPush(pVM->pStack, LVALUEtoCELL(pBase));
+ return;
+}
+
+
+static void decimal(FICL_VM *pVM)
+{
+ pVM->base = 10;
+ return;
+}
+
+
+static void hex(FICL_VM *pVM)
+{
+ pVM->base = 16;
+ return;
+}
+
+
+/**************************************************************************
+ a l l o t & f r i e n d s
+**
+**************************************************************************/
+
+static void allot(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ i = POPINT();
+
+#if FICL_ROBUST
+ dictCheck(dp, pVM, i);
+#endif
+
+ dictAllot(dp, i);
+ return;
+}
+
+
+static void here(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ dp = vmGetDict(pVM);
+ PUSHPTR(dp->here);
+ return;
+}
+
+static void comma(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ CELL c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ c = POP();
+ dictAppendCell(dp, c);
+ return;
+}
+
+static void cComma(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ char c;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ c = (char)POPINT();
+ dictAppendChar(dp, c);
+ return;
+}
+
+static void cells(FICL_VM *pVM)
+{
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ i = POPINT();
+ PUSHINT(i * (FICL_INT)sizeof (CELL));
+ return;
+}
+
+static void cellPlus(FICL_VM *pVM)
+{
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ cp = POPPTR();
+ PUSHPTR(cp + sizeof (CELL));
+ return;
+}
+
+
+
+/**************************************************************************
+ t i c k
+** tick CORE ( "<spaces>name" -- xt )
+** Skip leading space delimiters. Parse name delimited by a space. Find
+** name and return xt, the execution token for name. An ambiguous condition
+** exists if name is not found.
+**************************************************************************/
+void ficlTick(FICL_VM *pVM)
+{
+ FICL_WORD *pFW = NULL;
+ STRINGINFO si = vmGetWord(pVM);
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pFW = dictLookup(vmGetDict(pVM), si);
+ if (!pFW)
+ {
+ int i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si));
+ }
+ PUSHPTR(pFW);
+ return;
+}
+
+
+static void bracketTickCoIm(FICL_VM *pVM)
+{
+ ficlTick(pVM);
+ literalIm(pVM);
+
+ return;
+}
+
+
+/**************************************************************************
+ p o s t p o n e
+** Lookup the next word in the input stream and compile code to
+** insert it into definitions created by the resulting word
+** (defers compilation, even of immediate words)
+**************************************************************************/
+
+static void postponeCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pFW;
+ FICL_WORD *pComma = ficlLookup(pVM->pSys, ",");
+ assert(pComma);
+
+ ficlTick(pVM);
+ pFW = stackGetTop(pVM->pStack).p;
+ if (wordIsImmediate(pFW))
+ {
+ dictAppendCell(dp, stackPop(pVM->pStack));
+ }
+ else
+ {
+ literalIm(pVM);
+ dictAppendCell(dp, LVALUEtoCELL(pComma));
+ }
+
+ return;
+}
+
+
+
+/**************************************************************************
+ e x e c u t e
+** Pop an execution token (pointer to a word) off the stack and
+** run it
+**************************************************************************/
+
+static void execute(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ pFW = stackPopPtr(pVM->pStack);
+ vmExecute(pVM, pFW);
+
+ return;
+}
+
+
+/**************************************************************************
+ i m m e d i a t e
+** Make the most recently compiled word IMMEDIATE -- it executes even
+** in compile state (most often used for control compiling words
+** such as IF, THEN, etc)
+**************************************************************************/
+
+static void immediate(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ dictSetImmediate(vmGetDict(pVM));
+ return;
+}
+
+
+static void compileOnly(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ dictSetFlags(vmGetDict(pVM), FW_COMPILE, 0);
+ return;
+}
+
+
+static void setObjectFlag(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ dictSetFlags(vmGetDict(pVM), FW_ISOBJECT, 0);
+ return;
+}
+
+static void isObject(FICL_VM *pVM)
+{
+ int flag;
+ FICL_WORD *pFW = (FICL_WORD *)stackPopPtr(pVM->pStack);
+
+ flag = ((pFW != NULL) && (pFW->flags & FW_ISOBJECT)) ? FICL_TRUE : FICL_FALSE;
+ stackPushINT(pVM->pStack, flag);
+ return;
+}
+
+static void cstringLit(FICL_VM *pVM)
+{
+ FICL_STRING *sp = (FICL_STRING *)(pVM->ip);
+
+ char *cp = sp->text;
+ cp += sp->count + 1;
+ cp = alignPtr(cp);
+ pVM->ip = (IPTYPE)(void *)cp;
+
+ stackPushPtr(pVM->pStack, sp);
+ return;
+}
+
+
+static void cstringQuoteIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ if (pVM->state == INTERPRET)
+ {
+ FICL_STRING *sp = (FICL_STRING *) dp->here;
+ vmGetString(pVM, sp, '\"');
+ stackPushPtr(pVM->pStack, sp);
+ /* move HERE past string so it doesn't get overwritten. --lch */
+ dictAllot(dp, sp->count + sizeof(FICL_COUNT));
+ }
+ else /* COMPILE state */
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pCStringLit));
+ dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"');
+ dictAlign(dp);
+ }
+
+ return;
+}
+
+/**************************************************************************
+ d o t Q u o t e
+** IMMEDIATE word that compiles a string literal for later display
+** Compile stringLit, then copy the bytes of the string from the TIB
+** to the dictionary. Backpatch the count byte and align the dictionary.
+**
+** stringlit: Fetch the count from the dictionary, then push the address
+** and count on the stack. Finally, update ip to point to the first
+** aligned address after the string text.
+**************************************************************************/
+
+static void stringLit(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ FICL_COUNT count;
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 2);
+#endif
+
+ sp = (FICL_STRING *)(pVM->ip);
+ count = sp->count;
+ cp = sp->text;
+ PUSHPTR(cp);
+ PUSHUNS(count);
+ cp += count + 1;
+ cp = alignPtr(cp);
+ pVM->ip = (IPTYPE)(void *)cp;
+}
+
+static void dotQuoteCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pType = ficlLookup(pVM->pSys, "type");
+ assert(pType);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit));
+ dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"');
+ dictAlign(dp);
+ dictAppendCell(dp, LVALUEtoCELL(pType));
+ return;
+}
+
+
+static void dotParen(FICL_VM *pVM)
+{
+ char *pSrc = vmGetInBuf(pVM);
+ char *pEnd = vmGetInBufEnd(pVM);
+ char *pDest = pVM->pad;
+ char ch;
+
+ /*
+ ** Note: the standard does not want leading spaces skipped (apparently)
+ */
+ for (ch = *pSrc; (pEnd != pSrc) && (ch != ')'); ch = *++pSrc)
+ *pDest++ = ch;
+
+ *pDest = '\0';
+ if ((pEnd != pSrc) && (ch == ')'))
+ pSrc++;
+
+ vmTextOut(pVM, pVM->pad, 0);
+ vmUpdateTib(pVM, pSrc);
+
+ return;
+}
+
+
+/**************************************************************************
+ s l i t e r a l
+** STRING
+** Interpretation: Interpretation semantics for this word are undefined.
+** Compilation: ( c-addr1 u -- )
+** Append the run-time semantics given below to the current definition.
+** Run-time: ( -- c-addr2 u )
+** Return c-addr2 u describing a string consisting of the characters
+** specified by c-addr1 u during compilation. A program shall not alter
+** the returned string.
+**************************************************************************/
+static void sLiteralCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp;
+ char *cp, *cpDest;
+ FICL_UNS u;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ dp = vmGetDict(pVM);
+ u = POPUNS();
+ cp = POPPTR();
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit));
+ cpDest = (char *) dp->here;
+ *cpDest++ = (char) u;
+
+ for (; u > 0; --u)
+ {
+ *cpDest++ = *cp++;
+ }
+
+ *cpDest++ = 0;
+ dp->here = PTRtoCELL alignPtr(cpDest);
+ return;
+}
+
+
+/**************************************************************************
+ s t a t e
+** Return the address of the VM's state member (must be sized the
+** same as a CELL for this reason)
+**************************************************************************/
+static void state(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+ PUSHPTR(&pVM->state);
+ return;
+}
+
+
+/**************************************************************************
+ c r e a t e . . . d o e s >
+** Make a new word in the dictionary with the run-time effect of
+** a variable (push my address), but with extra space allotted
+** for use by does> .
+**************************************************************************/
+
+static void createParen(FICL_VM *pVM)
+{
+ CELL *pCell;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pCell = pVM->runningWord->param;
+ PUSHPTR(pCell+1);
+ return;
+}
+
+
+static void create(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+
+ dictAppendWord2(dp, si, createParen, FW_DEFAULT);
+ dictAllotCells(dp, 1);
+ return;
+}
+
+
+static void doDoes(FICL_VM *pVM)
+{
+ CELL *pCell;
+ IPTYPE tempIP;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 0, 1);
+#endif
+
+ pCell = pVM->runningWord->param;
+ tempIP = (IPTYPE)((*pCell).p);
+ PUSHPTR(pCell+1);
+ vmPushIP(pVM, tempIP);
+ return;
+}
+
+
+static void doesParen(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ dp->smudge->code = doDoes;
+ dp->smudge->param[0] = LVALUEtoCELL(pVM->ip);
+ vmPopIP(pVM);
+ return;
+}
+
+
+static void doesCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+#if FICL_WANT_LOCALS
+ assert(pVM->pSys->pUnLinkParen);
+ if (pVM->pSys->nLocals > 0)
+ {
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ dictEmpty(pLoc, pLoc->pForthWords->size);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pUnLinkParen));
+ }
+
+ pVM->pSys->nLocals = 0;
+#endif
+ IGNORE(pVM);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pDoesParen));
+ return;
+}
+
+
+/**************************************************************************
+ t o b o d y
+** to-body CORE ( xt -- a-addr )
+** a-addr is the data-field address corresponding to xt. An ambiguous
+** condition exists if xt is not for a word defined via CREATE.
+**************************************************************************/
+static void toBody(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+/*#$-GUY CHANGE: Added robustness.-$#*/
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ pFW = POPPTR();
+ PUSHPTR(pFW->param + 1);
+ return;
+}
+
+
+/*
+** from-body ficl ( a-addr -- xt )
+** Reverse effect of >body
+*/
+static void fromBody(FICL_VM *pVM)
+{
+ char *ptr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 1);
+#endif
+
+ ptr = (char *)POPPTR() - sizeof (FICL_WORD);
+ PUSHPTR(ptr);
+ return;
+}
+
+
+/*
+** >name ficl ( xt -- c-addr u )
+** Push the address and length of a word's name given its address
+** xt.
+*/
+static void toName(FICL_VM *pVM)
+{
+ FICL_WORD *pFW;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 2);
+#endif
+
+ pFW = POPPTR();
+ PUSHPTR(pFW->name);
+ PUSHUNS(pFW->nName);
+ return;
+}
+
+
+static void getLastWord(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_WORD *wp = pDict->smudge;
+ assert(wp);
+ vmPush(pVM, LVALUEtoCELL(wp));
+ return;
+}
+
+
+/**************************************************************************
+ l b r a c k e t e t c
+**
+**************************************************************************/
+
+static void lbracketCoIm(FICL_VM *pVM)
+{
+ pVM->state = INTERPRET;
+ return;
+}
+
+
+static void rbracket(FICL_VM *pVM)
+{
+ pVM->state = COMPILE;
+ return;
+}
+
+
+/**************************************************************************
+ p i c t u r e d n u m e r i c w o r d s
+**
+** less-number-sign CORE ( -- )
+** Initialize the pictured numeric output conversion process.
+** (clear the pad)
+**************************************************************************/
+static void lessNumberSign(FICL_VM *pVM)
+{
+ FICL_STRING *sp = PTRtoSTRING pVM->pad;
+ sp->count = 0;
+ return;
+}
+
+/*
+** number-sign CORE ( ud1 -- ud2 )
+** Divide ud1 by the number in BASE giving the quotient ud2 and the remainder
+** n. (n is the least-significant digit of ud1.) Convert n to external form
+** and add the resulting character to the beginning of the pictured numeric
+** output string. An ambiguous condition exists if # executes outside of a
+** <# #> delimited number conversion.
+*/
+static void numberSign(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ DPUNS u;
+ UNS16 rem;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ u = u64Pop(pVM->pStack);
+ rem = m64UMod(&u, (UNS16)(pVM->base));
+ sp->text[sp->count++] = digit_to_char(rem);
+ u64Push(pVM->pStack, u);
+ return;
+}
+
+/*
+** number-sign-greater CORE ( xd -- c-addr u )
+** Drop xd. Make the pictured numeric output string available as a character
+** string. c-addr and u specify the resulting character string. A program
+** may replace characters within the string.
+*/
+static void numberSignGreater(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ sp->text[sp->count] = 0;
+ strrev(sp->text);
+ DROP(2);
+ PUSHPTR(sp->text);
+ PUSHUNS(sp->count);
+ return;
+}
+
+/*
+** number-sign-s CORE ( ud1 -- ud2 )
+** Convert one digit of ud1 according to the rule for #. Continue conversion
+** until the quotient is zero. ud2 is zero. An ambiguous condition exists if
+** #S executes outside of a <# #> delimited number conversion.
+** TO DO: presently does not use ud1 hi cell - use it!
+*/
+static void numberSignS(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ DPUNS u;
+ UNS16 rem;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 2);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ u = u64Pop(pVM->pStack);
+
+ do
+ {
+ rem = m64UMod(&u, (UNS16)(pVM->base));
+ sp->text[sp->count++] = digit_to_char(rem);
+ }
+ while (u.hi || u.lo);
+
+ u64Push(pVM->pStack, u);
+ return;
+}
+
+/*
+** HOLD CORE ( char -- )
+** Add char to the beginning of the pictured numeric output string. An ambiguous
+** condition exists if HOLD executes outside of a <# #> delimited number conversion.
+*/
+static void hold(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ int i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ i = POPINT();
+ sp->text[sp->count++] = (char) i;
+ return;
+}
+
+/*
+** SIGN CORE ( n -- )
+** If n is negative, add a minus sign to the beginning of the pictured
+** numeric output string. An ambiguous condition exists if SIGN
+** executes outside of a <# #> delimited number conversion.
+*/
+static void sign(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ int i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+
+ sp = PTRtoSTRING pVM->pad;
+ i = POPINT();
+ if (i < 0)
+ sp->text[sp->count++] = '-';
+ return;
+}
+
+
+/**************************************************************************
+ t o N u m b e r
+** to-number CORE ( ud1 c-addr1 u1 -- ud2 c-addr2 u2 )
+** ud2 is the unsigned result of converting the characters within the
+** string specified by c-addr1 u1 into digits, using the number in BASE,
+** and adding each into ud1 after multiplying ud1 by the number in BASE.
+** Conversion continues left-to-right until a character that is not
+** convertible, including any + or -, is encountered or the string is
+** entirely converted. c-addr2 is the location of the first unconverted
+** character or the first character past the end of the string if the string
+** was entirely converted. u2 is the number of unconverted characters in the
+** string. An ambiguous condition exists if ud2 overflows during the
+** conversion.
+**************************************************************************/
+static void toNumber(FICL_VM *pVM)
+{
+ FICL_UNS count;
+ char *cp;
+ DPUNS accum;
+ FICL_UNS base = pVM->base;
+ FICL_UNS ch;
+ FICL_UNS digit;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,4,4);
+#endif
+
+ count = POPUNS();
+ cp = (char *)POPPTR();
+ accum = u64Pop(pVM->pStack);
+
+ for (ch = *cp; count > 0; ch = *++cp, count--)
+ {
+ if (ch < '0')
+ break;
+
+ digit = ch - '0';
+
+ if (digit > 9)
+ digit = tolower(ch) - 'a' + 10;
+ /*
+ ** Note: following test also catches chars between 9 and a
+ ** because 'digit' is unsigned!
+ */
+ if (digit >= base)
+ break;
+
+ accum = m64Mac(accum, base, digit);
+ }
+
+ u64Push(pVM->pStack, accum);
+ PUSHPTR(cp);
+ PUSHUNS(count);
+
+ return;
+}
+
+
+
+/**************************************************************************
+ q u i t & a b o r t
+** quit CORE ( -- ) ( R: i*x -- )
+** Empty the return stack, store zero in SOURCE-ID if it is present, make
+** the user input device the input source, and enter interpretation state.
+** Do not display a message. Repeat the following:
+**
+** Accept a line from the input source into the input buffer, set >IN to
+** zero, and interpret.
+** Display the implementation-defined system prompt if in
+** interpretation state, all processing has been completed, and no
+** ambiguous condition exists.
+**************************************************************************/
+
+static void quit(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_QUIT);
+ return;
+}
+
+
+static void ficlAbort(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_ABORT);
+ return;
+}
+
+
+/**************************************************************************
+ a c c e p t
+** accept CORE ( c-addr +n1 -- +n2 )
+** Receive a string of at most +n1 characters. An ambiguous condition
+** exists if +n1 is zero or greater than 32,767. Display graphic characters
+** as they are received. A program that depends on the presence or absence
+** of non-graphic characters in the string has an environmental dependency.
+** The editing functions, if any, that the system performs in order to
+** construct the string are implementation-defined.
+**
+** (Although the standard text doesn't say so, I assume that the intent
+** of 'accept' is to store the string at the address specified on
+** the stack.)
+** Implementation: if there's more text in the TIB, use it. Otherwise
+** throw out for more text. Copy characters up to the max count into the
+** address given, and return the number of actual characters copied.
+**
+** Note (sobral) this may not be the behavior you'd expect if you're
+** trying to get user input at load time!
+**************************************************************************/
+static void accept(FICL_VM *pVM)
+{
+ FICL_UNS count, len;
+ char *cp;
+ char *pBuf, *pEnd;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ pBuf = vmGetInBuf(pVM);
+ pEnd = vmGetInBufEnd(pVM);
+ len = pEnd - pBuf;
+ if (len == 0)
+ vmThrow(pVM, VM_RESTART);
+
+ /*
+ ** Now we have something in the text buffer - use it
+ */
+ count = stackPopINT(pVM->pStack);
+ cp = stackPopPtr(pVM->pStack);
+
+ len = (count < len) ? count : len;
+ strncpy(cp, vmGetInBuf(pVM), len);
+ pBuf += len;
+ vmUpdateTib(pVM, pBuf);
+ PUSHINT(len);
+
+ return;
+}
+
+
+/**************************************************************************
+ a l i g n
+** 6.1.0705 ALIGN CORE ( -- )
+** If the data-space pointer is not aligned, reserve enough space to
+** align it.
+**************************************************************************/
+static void align(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ IGNORE(pVM);
+ dictAlign(dp);
+ return;
+}
+
+
+/**************************************************************************
+ a l i g n e d
+**
+**************************************************************************/
+static void aligned(FICL_VM *pVM)
+{
+ void *addr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+
+ addr = POPPTR();
+ PUSHPTR(alignPtr(addr));
+ return;
+}
+
+
+/**************************************************************************
+ b e g i n & f r i e n d s
+** Indefinite loop control structures
+** A.6.1.0760 BEGIN
+** Typical use:
+** : X ... BEGIN ... test UNTIL ;
+** or
+** : X ... BEGIN ... test WHILE ... REPEAT ;
+**************************************************************************/
+static void beginCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ markBranch(dp, pVM, destTag);
+ return;
+}
+
+static void untilCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranch0);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0));
+ resolveBackBranch(dp, pVM, destTag);
+ return;
+}
+
+static void whileCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranch0);
+
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranch0));
+ markBranch(dp, pVM, origTag);
+ twoSwap(pVM);
+ dictAppendUNS(dp, 1);
+ return;
+}
+
+static void repeatCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranchParen);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* expect "begin" branch marker */
+ resolveBackBranch(dp, pVM, destTag);
+ /* expect "while" branch marker */
+ resolveForwardBranch(dp, pVM, origTag);
+ return;
+}
+
+
+static void againCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ assert(pVM->pSys->pBranchParen);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pBranchParen));
+
+ /* expect "begin" branch marker */
+ resolveBackBranch(dp, pVM, destTag);
+ return;
+}
+
+
+/**************************************************************************
+ c h a r & f r i e n d s
+** 6.1.0895 CHAR CORE ( "<spaces>name" -- char )
+** Skip leading space delimiters. Parse name delimited by a space.
+** Put the value of its first character onto the stack.
+**
+** bracket-char CORE
+** Interpretation: Interpretation semantics for this word are undefined.
+** Compilation: ( "<spaces>name" -- )
+** Skip leading space delimiters. Parse name delimited by a space.
+** Append the run-time semantics given below to the current definition.
+** Run-time: ( -- char )
+** Place char, the value of the first character of name, on the stack.
+**************************************************************************/
+static void ficlChar(FICL_VM *pVM)
+{
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,1);
+#endif
+
+ si = vmGetWord(pVM);
+ PUSHUNS((FICL_UNS)(si.cp[0]));
+ return;
+}
+
+static void charCoIm(FICL_VM *pVM)
+{
+ ficlChar(pVM);
+ literalIm(pVM);
+ return;
+}
+
+/**************************************************************************
+ c h a r P l u s
+** char-plus CORE ( c-addr1 -- c-addr2 )
+** Add the size in address units of a character to c-addr1, giving c-addr2.
+**************************************************************************/
+static void charPlus(FICL_VM *pVM)
+{
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+
+ cp = POPPTR();
+ PUSHPTR(cp + 1);
+ return;
+}
+
+/**************************************************************************
+ c h a r s
+** chars CORE ( n1 -- n2 )
+** n2 is the size in address units of n1 characters.
+** For most processors, this function can be a no-op. To guarantee
+** portability, we'll multiply by sizeof (char).
+**************************************************************************/
+#if defined (_M_IX86)
+#pragma warning(disable: 4127)
+#endif
+static void ficlChars(FICL_VM *pVM)
+{
+ if (sizeof (char) > 1)
+ {
+ FICL_INT i;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+ i = POPINT();
+ PUSHINT(i * sizeof (char));
+ }
+ /* otherwise no-op! */
+ return;
+}
+#if defined (_M_IX86)
+#pragma warning(default: 4127)
+#endif
+
+
+/**************************************************************************
+ c o u n t
+** COUNT CORE ( c-addr1 -- c-addr2 u )
+** Return the character string specification for the counted string stored
+** at c-addr1. c-addr2 is the address of the first character after c-addr1.
+** u is the contents of the character at c-addr1, which is the length in
+** characters of the string at c-addr2.
+**************************************************************************/
+static void count(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+
+ sp = POPPTR();
+ PUSHPTR(sp->text);
+ PUSHUNS(sp->count);
+ return;
+}
+
+/**************************************************************************
+ e n v i r o n m e n t ?
+** environment-query CORE ( c-addr u -- false | i*x true )
+** c-addr is the address of a character string and u is the string's
+** character count. u may have a value in the range from zero to an
+** implementation-defined maximum which shall not be less than 31. The
+** character string should contain a keyword from 3.2.6 Environmental
+** queries or the optional word sets to be checked for correspondence
+** with an attribute of the present environment. If the system treats the
+** attribute as unknown, the returned flag is false; otherwise, the flag
+** is true and the i*x returned is of the type specified in the table for
+** the attribute queried.
+**************************************************************************/
+static void environmentQ(FICL_VM *pVM)
+{
+ FICL_DICT *envp;
+ FICL_WORD *pFW;
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ envp = pVM->pSys->envp;
+ si.count = (FICL_COUNT)stackPopUNS(pVM->pStack);
+ si.cp = stackPopPtr(pVM->pStack);
+
+ pFW = dictLookup(envp, si);
+
+ if (pFW != NULL)
+ {
+ vmExecute(pVM, pFW);
+ PUSHINT(FICL_TRUE);
+ }
+ else
+ {
+ PUSHINT(FICL_FALSE);
+ }
+ return;
+}
+
+/**************************************************************************
+ e v a l u a t e
+** EVALUATE CORE ( i*x c-addr u -- j*x )
+** Save the current input source specification. Store minus-one (-1) in
+** SOURCE-ID if it is present. Make the string described by c-addr and u
+** both the input source and input buffer, set >IN to zero, and interpret.
+** When the parse area is empty, restore the prior input source
+** specification. Other stack effects are due to the words EVALUATEd.
+**
+**************************************************************************/
+static void evaluate(FICL_VM *pVM)
+{
+ FICL_UNS count;
+ char *cp;
+ CELL id;
+ int result;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,0);
+#endif
+
+ count = POPUNS();
+ cp = POPPTR();
+
+ IGNORE(count);
+ id = pVM->sourceID;
+ pVM->sourceID.i = -1;
+ result = ficlExecC(pVM, cp, count);
+ pVM->sourceID = id;
+ if (result != VM_OUTOFTEXT)
+ vmThrow(pVM, result);
+
+ return;
+}
+
+
+/**************************************************************************
+ s t r i n g q u o t e
+** Interpreting: get string delimited by a quote from the input stream,
+** copy to a scratch area, and put its count and address on the stack.
+** Compiling: compile code to push the address and count of a string
+** literal, compile the string from the input stream, and align the dict
+** pointer.
+**************************************************************************/
+static void stringQuoteIm(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+
+ if (pVM->state == INTERPRET)
+ {
+ FICL_STRING *sp = (FICL_STRING *) dp->here;
+ vmGetString(pVM, sp, '\"');
+ PUSHPTR(sp->text);
+ PUSHUNS(sp->count);
+ }
+ else /* COMPILE state */
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStringLit));
+ dp->here = PTRtoCELL vmGetString(pVM, (FICL_STRING *)dp->here, '\"');
+ dictAlign(dp);
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ t y p e
+** Pop count and char address from stack and print the designated string.
+**************************************************************************/
+static void type(FICL_VM *pVM)
+{
+ FICL_UNS count;
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 2, 0);
+#endif
+
+ count = POPUNS();
+ cp = POPPTR();
+
+ /*
+ ** Since we don't have an output primitive for a counted string
+ ** (oops), make sure the string is null terminated. If not, copy
+ ** and terminate it.
+ */
+ if (cp[count] != 0)
+ {
+ char *pDest = (char *)vmGetDict(pVM)->here;
+ if (cp != pDest)
+ strncpy(pDest, cp, count);
+
+ pDest[count] = '\0';
+ cp = pDest;
+ }
+
+ vmTextOut(pVM, cp, 0);
+ return;
+}
+
+/**************************************************************************
+ w o r d
+** word CORE ( char "<chars>ccc<char>" -- c-addr )
+** Skip leading delimiters. Parse characters ccc delimited by char. An
+** ambiguous condition exists if the length of the parsed string is greater
+** than the implementation-defined length of a counted string.
+**
+** c-addr is the address of a transient region containing the parsed word
+** as a counted string. If the parse area was empty or contained no
+** characters other than the delimiter, the resulting string has a zero
+** length. A space, not included in the length, follows the string. A
+** program may replace characters within the string.
+** NOTE! Ficl also NULL-terminates the dest string.
+**************************************************************************/
+static void ficlWord(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ char delim;
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,1);
+#endif
+
+ sp = (FICL_STRING *)pVM->pad;
+ delim = (char)POPINT();
+ si = vmParseStringEx(pVM, delim, 1);
+
+ if (SI_COUNT(si) > nPAD-1)
+ SI_SETLEN(si, nPAD-1);
+
+ sp->count = (FICL_COUNT)SI_COUNT(si);
+ strncpy(sp->text, SI_PTR(si), SI_COUNT(si));
+ /*#$-GUY CHANGE: I added this.-$#*/
+ sp->text[sp->count] = 0;
+ strcat(sp->text, " ");
+
+ PUSHPTR(sp);
+ return;
+}
+
+
+/**************************************************************************
+ p a r s e - w o r d
+** ficl PARSE-WORD ( <spaces>name -- c-addr u )
+** Skip leading spaces and parse name delimited by a space. c-addr is the
+** address within the input buffer and u is the length of the selected
+** string. If the parse area is empty, the resulting string has a zero length.
+**************************************************************************/
+static void parseNoCopy(FICL_VM *pVM)
+{
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,2);
+#endif
+
+ si = vmGetWord0(pVM);
+ PUSHPTR(SI_PTR(si));
+ PUSHUNS(SI_COUNT(si));
+ return;
+}
+
+
+/**************************************************************************
+ p a r s e
+** CORE EXT ( char "ccc<char>" -- c-addr u )
+** Parse ccc delimited by the delimiter char.
+** c-addr is the address (within the input buffer) and u is the length of
+** the parsed string. If the parse area was empty, the resulting string has
+** a zero length.
+** NOTE! PARSE differs from WORD: it does not skip leading delimiters.
+**************************************************************************/
+static void parse(FICL_VM *pVM)
+{
+ STRINGINFO si;
+ char delim;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+
+ delim = (char)POPINT();
+
+ si = vmParseStringEx(pVM, delim, 0);
+ PUSHPTR(SI_PTR(si));
+ PUSHUNS(SI_COUNT(si));
+ return;
+}
+
+
+/**************************************************************************
+ f i l l
+** CORE ( c-addr u char -- )
+** If u is greater than zero, store char in each of u consecutive
+** characters of memory beginning at c-addr.
+**************************************************************************/
+static void fill(FICL_VM *pVM)
+{
+ char ch;
+ FICL_UNS u;
+ char *cp;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,0);
+#endif
+ ch = (char)POPINT();
+ u = POPUNS();
+ cp = (char *)POPPTR();
+
+ while (u > 0)
+ {
+ *cp++ = ch;
+ u--;
+ }
+ return;
+}
+
+
+/**************************************************************************
+ f i n d
+** FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 )
+** Find the definition named in the counted string at c-addr. If the
+** definition is not found, return c-addr and zero. If the definition is
+** found, return its execution token xt. If the definition is immediate,
+** also return one (1), otherwise also return minus-one (-1). For a given
+** string, the values returned by FIND while compiling may differ from
+** those returned while not compiling.
+**************************************************************************/
+static void do_find(FICL_VM *pVM, STRINGINFO si, void *returnForFailure)
+{
+ FICL_WORD *pFW;
+
+ pFW = dictLookup(vmGetDict(pVM), si);
+ if (pFW)
+ {
+ PUSHPTR(pFW);
+ PUSHINT((wordIsImmediate(pFW) ? 1 : -1));
+ }
+ else
+ {
+ PUSHPTR(returnForFailure);
+ PUSHUNS(0);
+ }
+ return;
+}
+
+
+
+/**************************************************************************
+ f i n d
+** FIND CORE ( c-addr -- c-addr 0 | xt 1 | xt -1 )
+** Find the definition named in the counted string at c-addr. If the
+** definition is not found, return c-addr and zero. If the definition is
+** found, return its execution token xt. If the definition is immediate,
+** also return one (1), otherwise also return minus-one (-1). For a given
+** string, the values returned by FIND while compiling may differ from
+** those returned while not compiling.
+**************************************************************************/
+static void cFind(FICL_VM *pVM)
+{
+ FICL_STRING *sp;
+ STRINGINFO si;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+ sp = POPPTR();
+ SI_PFS(si, sp);
+ do_find(pVM, si, sp);
+}
+
+
+
+/**************************************************************************
+ s f i n d
+** FICL ( c-addr u -- 0 0 | xt 1 | xt -1 )
+** Like FIND, but takes "c-addr u" for the string.
+**************************************************************************/
+static void sFind(FICL_VM *pVM)
+{
+ STRINGINFO si;
+
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,2);
+#endif
+
+ si.count = stackPopINT(pVM->pStack);
+ si.cp = stackPopPtr(pVM->pStack);
+
+ do_find(pVM, si, NULL);
+}
+
+
+
+/**************************************************************************
+ f m S l a s h M o d
+** f-m-slash-mod CORE ( d1 n1 -- n2 n3 )
+** Divide d1 by n1, giving the floored quotient n3 and the remainder n2.
+** Input and output stack arguments are signed. An ambiguous condition
+** exists if n1 is zero or if the quotient lies outside the range of a
+** single-cell signed integer.
+**************************************************************************/
+static void fmSlashMod(FICL_VM *pVM)
+{
+ DPINT d1;
+ FICL_INT n1;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,2);
+#endif
+
+ n1 = POPINT();
+ d1 = i64Pop(pVM->pStack);
+ qr = m64FlooredDivI(d1, n1);
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+
+/**************************************************************************
+ s m S l a s h R e m
+** s-m-slash-rem CORE ( d1 n1 -- n2 n3 )
+** Divide d1 by n1, giving the symmetric quotient n3 and the remainder n2.
+** Input and output stack arguments are signed. An ambiguous condition
+** exists if n1 is zero or if the quotient lies outside the range of a
+** single-cell signed integer.
+**************************************************************************/
+static void smSlashRem(FICL_VM *pVM)
+{
+ DPINT d1;
+ FICL_INT n1;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,2);
+#endif
+
+ n1 = POPINT();
+ d1 = i64Pop(pVM->pStack);
+ qr = m64SymmetricDivI(d1, n1);
+ PUSHINT(qr.rem);
+ PUSHINT(qr.quot);
+ return;
+}
+
+
+static void ficlMod(FICL_VM *pVM)
+{
+ DPINT d1;
+ FICL_INT n1;
+ INTQR qr;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ n1 = POPINT();
+ d1.lo = POPINT();
+ i64Extend(d1);
+ qr = m64SymmetricDivI(d1, n1);
+ PUSHINT(qr.rem);
+ return;
+}
+
+
+/**************************************************************************
+ u m S l a s h M o d
+** u-m-slash-mod CORE ( ud u1 -- u2 u3 )
+** Divide ud by u1, giving the quotient u3 and the remainder u2.
+** All values and arithmetic are unsigned. An ambiguous condition
+** exists if u1 is zero or if the quotient lies outside the range of a
+** single-cell unsigned integer.
+*************************************************************************/
+static void umSlashMod(FICL_VM *pVM)
+{
+ DPUNS ud;
+ FICL_UNS u1;
+ UNSQR qr;
+
+ u1 = stackPopUNS(pVM->pStack);
+ ud = u64Pop(pVM->pStack);
+ qr = ficlLongDiv(ud, u1);
+ PUSHUNS(qr.rem);
+ PUSHUNS(qr.quot);
+ return;
+}
+
+
+/**************************************************************************
+ l s h i f t
+** l-shift CORE ( x1 u -- x2 )
+** Perform a logical left shift of u bit-places on x1, giving x2.
+** Put zeroes into the least significant bits vacated by the shift.
+** An ambiguous condition exists if u is greater than or equal to the
+** number of bits in a cell.
+**
+** r-shift CORE ( x1 u -- x2 )
+** Perform a logical right shift of u bit-places on x1, giving x2.
+** Put zeroes into the most significant bits vacated by the shift. An
+** ambiguous condition exists if u is greater than or equal to the
+** number of bits in a cell.
+**************************************************************************/
+static void lshift(FICL_VM *pVM)
+{
+ FICL_UNS nBits;
+ FICL_UNS x1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ nBits = POPUNS();
+ x1 = POPUNS();
+ PUSHUNS(x1 << nBits);
+ return;
+}
+
+
+static void rshift(FICL_VM *pVM)
+{
+ FICL_UNS nBits;
+ FICL_UNS x1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ nBits = POPUNS();
+ x1 = POPUNS();
+
+ PUSHUNS(x1 >> nBits);
+ return;
+}
+
+
+/**************************************************************************
+ m S t a r
+** m-star CORE ( n1 n2 -- d )
+** d is the signed product of n1 times n2.
+**************************************************************************/
+static void mStar(FICL_VM *pVM)
+{
+ FICL_INT n2;
+ FICL_INT n1;
+ DPINT d;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,2);
+#endif
+
+ n2 = POPINT();
+ n1 = POPINT();
+
+ d = m64MulI(n1, n2);
+ i64Push(pVM->pStack, d);
+ return;
+}
+
+
+static void umStar(FICL_VM *pVM)
+{
+ FICL_UNS u2;
+ FICL_UNS u1;
+ DPUNS ud;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,2);
+#endif
+
+ u2 = POPUNS();
+ u1 = POPUNS();
+
+ ud = ficlLongMul(u1, u2);
+ u64Push(pVM->pStack, ud);
+ return;
+}
+
+
+/**************************************************************************
+ m a x & m i n
+**
+**************************************************************************/
+static void ficlMax(FICL_VM *pVM)
+{
+ FICL_INT n2;
+ FICL_INT n1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ n2 = POPINT();
+ n1 = POPINT();
+
+ PUSHINT((n1 > n2) ? n1 : n2);
+ return;
+}
+
+static void ficlMin(FICL_VM *pVM)
+{
+ FICL_INT n2;
+ FICL_INT n1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,1);
+#endif
+
+ n2 = POPINT();
+ n1 = POPINT();
+
+ PUSHINT((n1 < n2) ? n1 : n2);
+ return;
+}
+
+
+/**************************************************************************
+ m o v e
+** CORE ( addr1 addr2 u -- )
+** If u is greater than zero, copy the contents of u consecutive address
+** units at addr1 to the u consecutive address units at addr2. After MOVE
+** completes, the u consecutive address units at addr2 contain exactly
+** what the u consecutive address units at addr1 contained before the move.
+** NOTE! This implementation assumes that a char is the same size as
+** an address unit.
+**************************************************************************/
+static void move(FICL_VM *pVM)
+{
+ FICL_UNS u;
+ char *addr2;
+ char *addr1;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,3,0);
+#endif
+
+ u = POPUNS();
+ addr2 = POPPTR();
+ addr1 = POPPTR();
+
+ if (u == 0)
+ return;
+ /*
+ ** Do the copy carefully, so as to be
+ ** correct even if the two ranges overlap
+ */
+ if (addr1 >= addr2)
+ {
+ for (; u > 0; u--)
+ *addr2++ = *addr1++;
+ }
+ else
+ {
+ addr2 += u-1;
+ addr1 += u-1;
+ for (; u > 0; u--)
+ *addr2-- = *addr1--;
+ }
+
+ return;
+}
+
+
+/**************************************************************************
+ r e c u r s e
+**
+**************************************************************************/
+static void recurseCoIm(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+
+ IGNORE(pVM);
+ dictAppendCell(pDict, LVALUEtoCELL(pDict->smudge));
+ return;
+}
+
+
+/**************************************************************************
+ s t o d
+** s-to-d CORE ( n -- d )
+** Convert the number n to the double-cell number d with the same
+** numerical value.
+**************************************************************************/
+static void sToD(FICL_VM *pVM)
+{
+ FICL_INT s;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,1,2);
+#endif
+
+ s = POPINT();
+
+ /* sign extend to 64 bits.. */
+ PUSHINT(s);
+ PUSHINT((s < 0) ? -1 : 0);
+ return;
+}
+
+
+/**************************************************************************
+ s o u r c e
+** CORE ( -- c-addr u )
+** c-addr is the address of, and u is the number of characters in, the
+** input buffer.
+**************************************************************************/
+static void source(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,2);
+#endif
+ PUSHPTR(pVM->tib.cp);
+ PUSHINT(vmGetInBufLen(pVM));
+ return;
+}
+
+
+/**************************************************************************
+ v e r s i o n
+** non-standard...
+**************************************************************************/
+static void ficlVersion(FICL_VM *pVM)
+{
+ vmTextOut(pVM, "ficl Version " FICL_VER, 1);
+ return;
+}
+
+
+/**************************************************************************
+ t o I n
+** to-in CORE
+**************************************************************************/
+static void toIn(FICL_VM *pVM)
+{
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,0,1);
+#endif
+ PUSHPTR(&pVM->tib.index);
+ return;
+}
+
+
+/**************************************************************************
+ c o l o n N o N a m e
+** CORE EXT ( C: -- colon-sys ) ( S: -- xt )
+** Create an unnamed colon definition and push its address.
+** Change state to compile.
+**************************************************************************/
+static void colonNoName(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pFW;
+ STRINGINFO si;
+
+ SI_SETLEN(si, 0);
+ SI_SETPTR(si, NULL);
+
+ pVM->state = COMPILE;
+ pFW = dictAppendWord2(dp, si, colonParen, FW_DEFAULT | FW_SMUDGE);
+ PUSHPTR(pFW);
+ markControlTag(pVM, colonTag);
+ return;
+}
+
+
+/**************************************************************************
+ u s e r V a r i a b l e
+** user ( u -- ) "<spaces>name"
+** Get a name from the input stream and create a user variable
+** with the name and the index supplied. The run-time effect
+** of a user variable is to push the address of the indexed cell
+** in the running vm's user array.
+**
+** User variables are vm local cells. Each vm has an array of
+** FICL_USER_CELLS of them when FICL_WANT_USER is nonzero.
+** Ficl's user facility is implemented with two primitives,
+** "user" and "(user)", a variable ("nUser") (in softcore.c) that
+** holds the index of the next free user cell, and a redefinition
+** (also in softcore) of "user" that defines a user word and increments
+** nUser.
+**************************************************************************/
+#if FICL_WANT_USER
+static void userParen(FICL_VM *pVM)
+{
+ FICL_INT i = pVM->runningWord->param[0].i;
+ PUSHPTR(&pVM->user[i]);
+ return;
+}
+
+
+static void userVariable(FICL_VM *pVM)
+{
+ FICL_DICT *dp = vmGetDict(pVM);
+ STRINGINFO si = vmGetWord(pVM);
+ CELL c;
+
+ c = stackPop(pVM->pStack);
+ if (c.i >= FICL_USER_CELLS)
+ {
+ vmThrowErr(pVM, "Error - out of user space");
+ }
+
+ dictAppendWord2(dp, si, userParen, FW_DEFAULT);
+ dictAppendCell(dp, c);
+ return;
+}
+#endif
+
+
+/**************************************************************************
+ t o V a l u e
+** CORE EXT
+** Interpretation: ( x "<spaces>name" -- )
+** Skip leading spaces and parse name delimited by a space. Store x in
+** name. An ambiguous condition exists if name was not defined by VALUE.
+** NOTE: In ficl, VALUE is an alias of CONSTANT
+**************************************************************************/
+static void toValue(FICL_VM *pVM)
+{
+ STRINGINFO si = vmGetWord(pVM);
+ FICL_DICT *dp = vmGetDict(pVM);
+ FICL_WORD *pFW;
+
+#if FICL_WANT_LOCALS
+ if ((pVM->pSys->nLocals > 0) && (pVM->state == COMPILE))
+ {
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ pFW = dictLookup(pLoc, si);
+ if (pFW && (pFW->code == doLocalIm))
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pToLocalParen));
+ dictAppendCell(dp, LVALUEtoCELL(pFW->param[0]));
+ return;
+ }
+ else if (pFW && pFW->code == do2LocalIm)
+ {
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pTo2LocalParen));
+ dictAppendCell(dp, LVALUEtoCELL(pFW->param[0]));
+ return;
+ }
+ }
+#endif
+
+ assert(pVM->pSys->pStore);
+
+ pFW = dictLookup(dp, si);
+ if (!pFW)
+ {
+ int i = SI_COUNT(si);
+ vmThrowErr(pVM, "%.*s not found", i, SI_PTR(si));
+ }
+
+ if (pVM->state == INTERPRET)
+ pFW->param[0] = stackPop(pVM->pStack);
+ else /* compile code to store to word's param */
+ {
+ PUSHPTR(&pFW->param[0]);
+ literalIm(pVM);
+ dictAppendCell(dp, LVALUEtoCELL(pVM->pSys->pStore));
+ }
+ return;
+}
+
+
+#if FICL_WANT_LOCALS
+/**************************************************************************
+ l i n k P a r e n
+** ( -- )
+** Link a frame on the return stack, reserving nCells of space for
+** locals - the value of nCells is the next cell in the instruction
+** stream.
+**************************************************************************/
+static void linkParen(FICL_VM *pVM)
+{
+ FICL_INT nLink = *(FICL_INT *)(pVM->ip);
+ vmBranchRelative(pVM, 1);
+ stackLink(pVM->rStack, nLink);
+ return;
+}
+
+
+static void unlinkParen(FICL_VM *pVM)
+{
+ stackUnlink(pVM->rStack);
+ return;
+}
+
+
+/**************************************************************************
+ d o L o c a l I m
+** Immediate - cfa of a local while compiling - when executed, compiles
+** code to fetch the value of a local given the local's index in the
+** word's pfa
+**************************************************************************/
+static void getLocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ return;
+}
+
+
+static void toLocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ pVM->rStack->pFrame[nLocal] = stackPop(pVM->pStack);
+ return;
+}
+
+
+static void getLocal0(FICL_VM *pVM)
+{
+ stackPush(pVM->pStack, pVM->rStack->pFrame[0]);
+ return;
+}
+
+
+static void toLocal0(FICL_VM *pVM)
+{
+ pVM->rStack->pFrame[0] = stackPop(pVM->pStack);
+ return;
+}
+
+
+static void getLocal1(FICL_VM *pVM)
+{
+ stackPush(pVM->pStack, pVM->rStack->pFrame[1]);
+ return;
+}
+
+
+static void toLocal1(FICL_VM *pVM)
+{
+ pVM->rStack->pFrame[1] = stackPop(pVM->pStack);
+ return;
+}
+
+
+/*
+** Each local is recorded in a private locals dictionary as a
+** word that does doLocalIm at runtime. DoLocalIm compiles code
+** into the client definition to fetch the value of the
+** corresponding local variable from the return stack.
+** The private dictionary gets initialized at the end of each block
+** that uses locals (in ; and does> for example).
+*/
+static void doLocalIm(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_INT nLocal = pVM->runningWord->param[0].i;
+
+ if (pVM->state == INTERPRET)
+ {
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ }
+ else
+ {
+
+ if (nLocal == 0)
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocal0));
+ }
+ else if (nLocal == 1)
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocal1));
+ }
+ else
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGetLocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(nLocal));
+ }
+ }
+ return;
+}
+
+
+/**************************************************************************
+ l o c a l P a r e n
+** paren-local-paren LOCAL
+** Interpretation: Interpretation semantics for this word are undefined.
+** Execution: ( c-addr u -- )
+** When executed during compilation, (LOCAL) passes a message to the
+** system that has one of two meanings. If u is non-zero,
+** the message identifies a new local whose definition name is given by
+** the string of characters identified by c-addr u. If u is zero,
+** the message is last local and c-addr has no significance.
+**
+** The result of executing (LOCAL) during compilation of a definition is
+** to create a set of named local identifiers, each of which is
+** a definition name, that only have execution semantics within the scope
+** of that definition's source.
+**
+** local Execution: ( -- x )
+**
+** Push the local's value, x, onto the stack. The local's value is
+** initialized as described in 13.3.3 Processing locals and may be
+** changed by preceding the local's name with TO. An ambiguous condition
+** exists when local is executed while in interpretation state.
+**************************************************************************/
+static void localParen(FICL_VM *pVM)
+{
+ FICL_DICT *pDict;
+ STRINGINFO si;
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM,2,0);
+#endif
+
+ pDict = vmGetDict(pVM);
+ SI_SETLEN(si, POPUNS());
+ SI_SETPTR(si, (char *)POPPTR());
+
+ if (SI_COUNT(si) > 0)
+ { /* add a local to the **locals** dict and update nLocals */
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ if (pVM->pSys->nLocals >= FICL_MAX_LOCALS)
+ {
+ vmThrowErr(pVM, "Error: out of local space");
+ }
+
+ dictAppendWord2(pLoc, si, doLocalIm, FW_COMPIMMED);
+ dictAppendCell(pLoc, LVALUEtoCELL(pVM->pSys->nLocals));
+
+ if (pVM->pSys->nLocals == 0)
+ { /* compile code to create a local stack frame */
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pLinkParen));
+ /* save location in dictionary for #locals */
+ pVM->pSys->pMarkLocals = pDict->here;
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+ /* compile code to initialize first local */
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocal0));
+ }
+ else if (pVM->pSys->nLocals == 1)
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocal1));
+ }
+ else
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pToLocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+ }
+
+ (pVM->pSys->nLocals)++;
+ }
+ else if (pVM->pSys->nLocals > 0)
+ { /* write nLocals to (link) param area in dictionary */
+ *(FICL_INT *)(pVM->pSys->pMarkLocals) = pVM->pSys->nLocals;
+ }
+
+ return;
+}
+
+
+static void get2LocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal+1]);
+ return;
+}
+
+
+static void do2LocalIm(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ FICL_INT nLocal = pVM->runningWord->param[0].i;
+
+ if (pVM->state == INTERPRET)
+ {
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal]);
+ stackPush(pVM->pStack, pVM->rStack->pFrame[nLocal+1]);
+ }
+ else
+ {
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pGet2LocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(nLocal));
+ }
+ return;
+}
+
+
+static void to2LocalParen(FICL_VM *pVM)
+{
+ FICL_INT nLocal = *(FICL_INT *)(pVM->ip++);
+ pVM->rStack->pFrame[nLocal+1] = stackPop(pVM->pStack);
+ pVM->rStack->pFrame[nLocal] = stackPop(pVM->pStack);
+ return;
+}
+
+
+static void twoLocalParen(FICL_VM *pVM)
+{
+ FICL_DICT *pDict = vmGetDict(pVM);
+ STRINGINFO si;
+ SI_SETLEN(si, stackPopUNS(pVM->pStack));
+ SI_SETPTR(si, (char *)stackPopPtr(pVM->pStack));
+
+ if (SI_COUNT(si) > 0)
+ { /* add a local to the **locals** dict and update nLocals */
+ FICL_DICT *pLoc = ficlGetLoc(pVM->pSys);
+ if (pVM->pSys->nLocals >= FICL_MAX_LOCALS)
+ {
+ vmThrowErr(pVM, "Error: out of local space");
+ }
+
+ dictAppendWord2(pLoc, si, do2LocalIm, FW_COMPIMMED);
+ dictAppendCell(pLoc, LVALUEtoCELL(pVM->pSys->nLocals));
+
+ if (pVM->pSys->nLocals == 0)
+ { /* compile code to create a local stack frame */
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pLinkParen));
+ /* save location in dictionary for #locals */
+ pVM->pSys->pMarkLocals = pDict->here;
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+ }
+
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->pTo2LocalParen));
+ dictAppendCell(pDict, LVALUEtoCELL(pVM->pSys->nLocals));
+
+ pVM->pSys->nLocals += 2;
+ }
+ else if (pVM->pSys->nLocals > 0)
+ { /* write nLocals to (link) param area in dictionary */
+ *(FICL_INT *)(pVM->pSys->pMarkLocals) = pVM->pSys->nLocals;
+ }
+
+ return;
+}
+
+
+#endif
+/**************************************************************************
+ c o m p a r e
+** STRING ( c-addr1 u1 c-addr2 u2 -- n )
+** Compare the string specified by c-addr1 u1 to the string specified by
+** c-addr2 u2. The strings are compared, beginning at the given addresses,
+** character by character, up to the length of the shorter string or until a
+** difference is found. If the two strings are identical, n is zero. If the two
+** strings are identical up to the length of the shorter string, n is minus-one
+** (-1) if u1 is less than u2 and one (1) otherwise. If the two strings are not
+** identical up to the length of the shorter string, n is minus-one (-1) if the
+** first non-matching character in the string specified by c-addr1 u1 has a
+** lesser numeric value than the corresponding character in the string specified
+** by c-addr2 u2 and one (1) otherwise.
+**************************************************************************/
+static void compareInternal(FICL_VM *pVM, int caseInsensitive)
+{
+ char *cp1, *cp2;
+ FICL_UNS u1, u2, uMin;
+ int n = 0;
+
+ vmCheckStack(pVM, 4, 1);
+ u2 = stackPopUNS(pVM->pStack);
+ cp2 = (char *)stackPopPtr(pVM->pStack);
+ u1 = stackPopUNS(pVM->pStack);
+ cp1 = (char *)stackPopPtr(pVM->pStack);
+
+ uMin = (u1 < u2)? u1 : u2;
+ for ( ; (uMin > 0) && (n == 0); uMin--)
+ {
+ char c1 = *cp1++;
+ char c2 = *cp2++;
+ if (caseInsensitive)
+ {
+ c1 = (char)tolower(c1);
+ c2 = (char)tolower(c2);
+ }
+ n = (int)(c1 - c2);
+ }
+
+ if (n == 0)
+ n = (int)(u1 - u2);
+
+ if (n < 0)
+ n = -1;
+ else if (n > 0)
+ n = 1;
+
+ PUSHINT(n);
+ return;
+}
+
+
+static void compareString(FICL_VM *pVM)
+{
+ compareInternal(pVM, FALSE);
+}
+
+
+static void compareStringInsensitive(FICL_VM *pVM)
+{
+ compareInternal(pVM, TRUE);
+}
+
+
+/**************************************************************************
+ p a d
+** CORE EXT ( -- c-addr )
+** c-addr is the address of a transient region that can be used to hold
+** data for intermediate processing.
+**************************************************************************/
+static void pad(FICL_VM *pVM)
+{
+ stackPushPtr(pVM->pStack, pVM->pad);
+}
+
+
+/**************************************************************************
+ s o u r c e - i d
+** CORE EXT, FILE ( -- 0 | -1 | fileid )
+** Identifies the input source as follows:
+**
+** SOURCE-ID Input source
+** --------- ------------
+** fileid Text file fileid
+** -1 String (via EVALUATE)
+** 0 User input device
+**************************************************************************/
+static void sourceid(FICL_VM *pVM)
+{
+ PUSHINT(pVM->sourceID.i);
+ return;
+}
+
+
+/**************************************************************************
+ r e f i l l
+** CORE EXT ( -- flag )
+** Attempt to fill the input buffer from the input source, returning a true
+** flag if successful.
+** When the input source is the user input device, attempt to receive input
+** into the terminal input buffer. If successful, make the result the input
+** buffer, set >IN to zero, and return true. Receipt of a line containing no
+** characters is considered successful. If there is no input available from
+** the current input source, return false.
+** When the input source is a string from EVALUATE, return false and
+** perform no other action.
+**************************************************************************/
+static void refill(FICL_VM *pVM)
+{
+ FICL_INT ret = (pVM->sourceID.i == -1) ? FICL_FALSE : FICL_TRUE;
+ if (ret && (pVM->fRestart == 0))
+ vmThrow(pVM, VM_RESTART);
+
+ PUSHINT(ret);
+ return;
+}
+
+
+/**************************************************************************
+ freebsd exception handling words
+** Catch, from ANS Forth standard. Installs a safety net, then EXECUTE
+** the word in ToS. If an exception happens, restore the state to what
+** it was before, and pushes the exception value on the stack. If not,
+** push zero.
+**
+** Notice that Catch implements an inner interpreter. This is ugly,
+** but given how ficl works, it cannot be helped. The problem is that
+** colon definitions will be executed *after* the function returns,
+** while "code" definitions will be executed immediately. I considered
+** other solutions to this problem, but all of them shared the same
+** basic problem (with added disadvantages): if ficl ever changes it's
+** inner thread modus operandi, one would have to fix this word.
+**
+** More comments can be found throughout catch's code.
+**
+** Daniel C. Sobral Jan 09/1999
+** sadler may 2000 -- revised to follow ficl.c:ficlExecXT.
+**************************************************************************/
+
+static void ficlCatch(FICL_VM *pVM)
+{
+ int except;
+ jmp_buf vmState;
+ FICL_VM VM;
+ FICL_STACK pStack;
+ FICL_STACK rStack;
+ FICL_WORD *pFW;
+
+ assert(pVM);
+ assert(pVM->pSys->pExitInner);
+
+
+ /*
+ ** Get xt.
+ ** We need this *before* we save the stack pointer, or
+ ** we'll have to pop one element out of the stack after
+ ** an exception. I prefer to get done with it up front. :-)
+ */
+#if FICL_ROBUST > 1
+ vmCheckStack(pVM, 1, 0);
+#endif
+ pFW = stackPopPtr(pVM->pStack);
+
+ /*
+ ** Save vm's state -- a catch will not back out environmental
+ ** changes.
+ **
+ ** We are *not* saving dictionary state, since it is
+ ** global instead of per vm, and we are not saving
+ ** stack contents, since we are not required to (and,
+ ** thus, it would be useless). We save pVM, and pVM
+ ** "stacks" (a structure containing general information
+ ** about it, including the current stack pointer).
+ */
+ memcpy((void*)&VM, (void*)pVM, sizeof(FICL_VM));
+ memcpy((void*)&pStack, (void*)pVM->pStack, sizeof(FICL_STACK));
+ memcpy((void*)&rStack, (void*)pVM->rStack, sizeof(FICL_STACK));
+
+ /*
+ ** Give pVM a jmp_buf
+ */
+ pVM->pState = &vmState;
+
+ /*
+ ** Safety net
+ */
+ except = setjmp(vmState);
+
+ switch (except)
+ {
+ /*
+ ** Setup condition - push poison pill so that the VM throws
+ ** VM_INNEREXIT if the XT terminates normally, then execute
+ ** the XT
+ */
+ case 0:
+ vmPushIP(pVM, &(pVM->pSys->pExitInner)); /* Open mouth, insert emetic */
+ vmExecute(pVM, pFW);
+ vmInnerLoop(pVM);
+ break;
+
+ /*
+ ** Normal exit from XT - lose the poison pill,
+ ** restore old setjmp vector and push a zero.
+ */
+ case VM_INNEREXIT:
+ vmPopIP(pVM); /* Gack - hurl poison pill */
+ pVM->pState = VM.pState; /* Restore just the setjmp vector */
+ PUSHINT(0); /* Push 0 -- everything is ok */
+ break;
+
+ /*
+ ** Some other exception got thrown - restore pre-existing VM state
+ ** and push the exception code
+ */
+ default:
+ /* Restore vm's state */
+ memcpy((void*)pVM, (void*)&VM, sizeof(FICL_VM));
+ memcpy((void*)pVM->pStack, (void*)&pStack, sizeof(FICL_STACK));
+ memcpy((void*)pVM->rStack, (void*)&rStack, sizeof(FICL_STACK));
+
+ PUSHINT(except);/* Push error */
+ break;
+ }
+}
+
+/**************************************************************************
+** t h r o w
+** EXCEPTION
+** Throw -- From ANS Forth standard.
+**
+** Throw takes the ToS and, if that's different from zero,
+** returns to the last executed catch context. Further throws will
+** unstack previously executed "catches", in LIFO mode.
+**
+** Daniel C. Sobral Jan 09/1999
+**************************************************************************/
+static void ficlThrow(FICL_VM *pVM)
+{
+ int except;
+
+ except = stackPopINT(pVM->pStack);
+
+ if (except)
+ vmThrow(pVM, except);
+}
+
+
+/**************************************************************************
+** a l l o c a t e
+** MEMORY
+**************************************************************************/
+static void ansAllocate(FICL_VM *pVM)
+{
+ size_t size;
+ void *p;
+
+ size = stackPopINT(pVM->pStack);
+ p = ficlMalloc(size);
+ PUSHPTR(p);
+ if (p)
+ PUSHINT(0);
+ else
+ PUSHINT(1);
+}
+
+
+/**************************************************************************
+** f r e e
+** MEMORY
+**************************************************************************/
+static void ansFree(FICL_VM *pVM)
+{
+ void *p;
+
+ p = stackPopPtr(pVM->pStack);
+ ficlFree(p);
+ PUSHINT(0);
+}
+
+
+/**************************************************************************
+** r e s i z e
+** MEMORY
+**************************************************************************/
+static void ansResize(FICL_VM *pVM)
+{
+ size_t size;
+ void *new, *old;
+
+ size = stackPopINT(pVM->pStack);
+ old = stackPopPtr(pVM->pStack);
+ new = ficlRealloc(old, size);
+ if (new)
+ {
+ PUSHPTR(new);
+ PUSHINT(0);
+ }
+ else
+ {
+ PUSHPTR(old);
+ PUSHINT(1);
+ }
+}
+
+
+/**************************************************************************
+** e x i t - i n n e r
+** Signals execXT that an inner loop has completed
+**************************************************************************/
+static void ficlExitInner(FICL_VM *pVM)
+{
+ vmThrow(pVM, VM_INNEREXIT);
+}
+
+
+/**************************************************************************
+ d n e g a t e
+** DOUBLE ( d1 -- d2 )
+** d2 is the negation of d1.
+**************************************************************************/
+static void dnegate(FICL_VM *pVM)
+{
+ DPINT i = i64Pop(pVM->pStack);
+ i = m64Negate(i);
+ i64Push(pVM->pStack, i);
+
+ return;
+}
+
+
+#if 0
+/**************************************************************************
+
+**
+**************************************************************************/
+static void funcname(FICL_VM *pVM)
+{
+ IGNORE(pVM);
+ return;
+}
+
+
+#endif
+/**************************************************************************
+ f i c l W o r d C l a s s i f y
+** This public function helps to classify word types for SEE
+** and the deugger in tools.c. Given an pointer to a word, it returns
+** a member of WOR
+**************************************************************************/
+WORDKIND ficlWordClassify(FICL_WORD *pFW)
+{
+ typedef struct
+ {
+ WORDKIND kind;
+ FICL_CODE code;
+ } CODEtoKIND;
+
+ static CODEtoKIND codeMap[] =
+ {
+ {BRANCH, branchParen},
+ {COLON, colonParen},
+ {CONSTANT, constantParen},
+ {CREATE, createParen},
+ {DO, doParen},
+ {DOES, doDoes},
+ {IF, branch0},
+ {LITERAL, literalParen},
+ {LOOP, loopParen},
+ {OF, ofParen},
+ {PLOOP, plusLoopParen},
+ {QDO, qDoParen},
+ {CSTRINGLIT, cstringLit},
+ {STRINGLIT, stringLit},
+#if FICL_WANT_USER
+ {USER, userParen},
+#endif
+ {VARIABLE, variableParen},
+ };
+
+#define nMAP (sizeof(codeMap) / sizeof(CODEtoKIND))
+
+ FICL_CODE code = pFW->code;
+ int i;
+
+ for (i=0; i < nMAP; i++)
+ {
+ if (codeMap[i].code == code)
+ return codeMap[i].kind;
+ }
+
+ return PRIMITIVE;
+}
+
+
+/**************************************************************************
+** r a n d o m
+** FICL-specific
+**************************************************************************/
+static void ficlRandom(FICL_VM *pVM)
+{
+ PUSHINT(rand());
+}
+
+
+/**************************************************************************
+** s e e d - r a n d o m
+** FICL-specific
+**************************************************************************/
+static void ficlSeedRandom(FICL_VM *pVM)
+{
+ srand(POPINT());
+}
+
+
+/**************************************************************************
+ f i c l C o m p i l e C o r e
+** Builds the primitive wordset and the environment-query namespace.
+**************************************************************************/
+
+void ficlCompileCore(FICL_SYSTEM *pSys)
+{
+ FICL_DICT *dp = pSys->dp;
+ assert (dp);
+
+
+ /*
+ ** CORE word set
+ ** see softcore.c for definitions of: abs bl space spaces abort"
+ */
+ pSys->pStore =
+ dictAppendWord(dp, "!", store, FW_DEFAULT);
+ dictAppendWord(dp, "#", numberSign, FW_DEFAULT);
+ dictAppendWord(dp, "#>", numberSignGreater,FW_DEFAULT);
+ dictAppendWord(dp, "#s", numberSignS, FW_DEFAULT);
+ dictAppendWord(dp, "\'", ficlTick, FW_DEFAULT);
+ dictAppendWord(dp, "(", commentHang, FW_IMMEDIATE);
+ dictAppendWord(dp, "*", mul, FW_DEFAULT);
+ dictAppendWord(dp, "*/", mulDiv, FW_DEFAULT);
+ dictAppendWord(dp, "*/mod", mulDivRem, FW_DEFAULT);
+ dictAppendWord(dp, "+", add, FW_DEFAULT);
+ dictAppendWord(dp, "+!", plusStore, FW_DEFAULT);
+ dictAppendWord(dp, "+loop", plusLoopCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, ",", comma, FW_DEFAULT);
+ dictAppendWord(dp, "-", sub, FW_DEFAULT);
+ dictAppendWord(dp, ".", displayCell, FW_DEFAULT);
+ dictAppendWord(dp, ".\"", dotQuoteCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "/", ficlDiv, FW_DEFAULT);
+ dictAppendWord(dp, "/mod", slashMod, FW_DEFAULT);
+ dictAppendWord(dp, "0<", zeroLess, FW_DEFAULT);
+ dictAppendWord(dp, "0=", zeroEquals, FW_DEFAULT);
+ dictAppendWord(dp, "1+", onePlus, FW_DEFAULT);
+ dictAppendWord(dp, "1-", oneMinus, FW_DEFAULT);
+ dictAppendWord(dp, "2!", twoStore, FW_DEFAULT);
+ dictAppendWord(dp, "2*", twoMul, FW_DEFAULT);
+ dictAppendWord(dp, "2/", twoDiv, FW_DEFAULT);
+ dictAppendWord(dp, "2@", twoFetch, FW_DEFAULT);
+ dictAppendWord(dp, "2drop", twoDrop, FW_DEFAULT);
+ dictAppendWord(dp, "2dup", twoDup, FW_DEFAULT);
+ dictAppendWord(dp, "2over", twoOver, FW_DEFAULT);
+ dictAppendWord(dp, "2swap", twoSwap, FW_DEFAULT);
+ dictAppendWord(dp, ":", colon, FW_DEFAULT);
+ dictAppendWord(dp, ";", semicolonCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "<", isLess, FW_DEFAULT);
+ dictAppendWord(dp, "<#", lessNumberSign, FW_DEFAULT);
+ dictAppendWord(dp, "=", isEqual, FW_DEFAULT);
+ dictAppendWord(dp, ">", isGreater, FW_DEFAULT);
+ dictAppendWord(dp, ">body", toBody, FW_DEFAULT);
+ dictAppendWord(dp, ">in", toIn, FW_DEFAULT);
+ dictAppendWord(dp, ">number", toNumber, FW_DEFAULT);
+ dictAppendWord(dp, ">r", toRStack, FW_COMPILE);
+ dictAppendWord(dp, "?dup", questionDup, FW_DEFAULT);
+ dictAppendWord(dp, "@", fetch, FW_DEFAULT);
+ dictAppendWord(dp, "abort", ficlAbort, FW_DEFAULT);
+ dictAppendWord(dp, "accept", accept, FW_DEFAULT);
+ dictAppendWord(dp, "align", align, FW_DEFAULT);
+ dictAppendWord(dp, "aligned", aligned, FW_DEFAULT);
+ dictAppendWord(dp, "allot", allot, FW_DEFAULT);
+ dictAppendWord(dp, "and", bitwiseAnd, FW_DEFAULT);
+ dictAppendWord(dp, "base", base, FW_DEFAULT);
+ dictAppendWord(dp, "begin", beginCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "c!", cStore, FW_DEFAULT);
+ dictAppendWord(dp, "c,", cComma, FW_DEFAULT);
+ dictAppendWord(dp, "c@", cFetch, FW_DEFAULT);
+ dictAppendWord(dp, "case", caseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "cell+", cellPlus, FW_DEFAULT);
+ dictAppendWord(dp, "cells", cells, FW_DEFAULT);
+ dictAppendWord(dp, "char", ficlChar, FW_DEFAULT);
+ dictAppendWord(dp, "char+", charPlus, FW_DEFAULT);
+ dictAppendWord(dp, "chars", ficlChars, FW_DEFAULT);
+ dictAppendWord(dp, "constant", constant, FW_DEFAULT);
+ dictAppendWord(dp, "count", count, FW_DEFAULT);
+ dictAppendWord(dp, "cr", cr, FW_DEFAULT);
+ dictAppendWord(dp, "create", create, FW_DEFAULT);
+ dictAppendWord(dp, "decimal", decimal, FW_DEFAULT);
+ dictAppendWord(dp, "depth", depth, FW_DEFAULT);
+ dictAppendWord(dp, "do", doCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "does>", doesCoIm, FW_COMPIMMED);
+ pSys->pDrop =
+ dictAppendWord(dp, "drop", drop, FW_DEFAULT);
+ dictAppendWord(dp, "dup", dup, FW_DEFAULT);
+ dictAppendWord(dp, "else", elseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "emit", emit, FW_DEFAULT);
+ dictAppendWord(dp, "endcase", endcaseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "endof", endofCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "environment?", environmentQ,FW_DEFAULT);
+ dictAppendWord(dp, "evaluate", evaluate, FW_DEFAULT);
+ dictAppendWord(dp, "execute", execute, FW_DEFAULT);
+ dictAppendWord(dp, "exit", exitCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "fallthrough",fallthroughCoIm,FW_COMPIMMED);
+ dictAppendWord(dp, "fill", fill, FW_DEFAULT);
+ dictAppendWord(dp, "find", cFind, FW_DEFAULT);
+ dictAppendWord(dp, "fm/mod", fmSlashMod, FW_DEFAULT);
+ dictAppendWord(dp, "here", here, FW_DEFAULT);
+ dictAppendWord(dp, "hold", hold, FW_DEFAULT);
+ dictAppendWord(dp, "i", loopICo, FW_COMPILE);
+ dictAppendWord(dp, "if", ifCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "immediate", immediate, FW_DEFAULT);
+ dictAppendWord(dp, "invert", bitwiseNot, FW_DEFAULT);
+ dictAppendWord(dp, "j", loopJCo, FW_COMPILE);
+ dictAppendWord(dp, "k", loopKCo, FW_COMPILE);
+ dictAppendWord(dp, "leave", leaveCo, FW_COMPILE);
+ dictAppendWord(dp, "literal", literalIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "loop", loopCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "lshift", lshift, FW_DEFAULT);
+ dictAppendWord(dp, "m*", mStar, FW_DEFAULT);
+ dictAppendWord(dp, "max", ficlMax, FW_DEFAULT);
+ dictAppendWord(dp, "min", ficlMin, FW_DEFAULT);
+ dictAppendWord(dp, "mod", ficlMod, FW_DEFAULT);
+ dictAppendWord(dp, "move", move, FW_DEFAULT);
+ dictAppendWord(dp, "negate", negate, FW_DEFAULT);
+ dictAppendWord(dp, "of", ofCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "or", bitwiseOr, FW_DEFAULT);
+ dictAppendWord(dp, "over", over, FW_DEFAULT);
+ dictAppendWord(dp, "postpone", postponeCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "quit", quit, FW_DEFAULT);
+ dictAppendWord(dp, "r>", fromRStack, FW_COMPILE);
+ dictAppendWord(dp, "r@", fetchRStack, FW_COMPILE);
+ dictAppendWord(dp, "recurse", recurseCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "repeat", repeatCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "rot", rot, FW_DEFAULT);
+ dictAppendWord(dp, "rshift", rshift, FW_DEFAULT);
+ dictAppendWord(dp, "s\"", stringQuoteIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "s>d", sToD, FW_DEFAULT);
+ dictAppendWord(dp, "sign", sign, FW_DEFAULT);
+ dictAppendWord(dp, "sm/rem", smSlashRem, FW_DEFAULT);
+ dictAppendWord(dp, "source", source, FW_DEFAULT);
+ dictAppendWord(dp, "state", state, FW_DEFAULT);
+ dictAppendWord(dp, "swap", swap, FW_DEFAULT);
+ dictAppendWord(dp, "then", endifCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "type", type, FW_DEFAULT);
+ dictAppendWord(dp, "u.", uDot, FW_DEFAULT);
+ dictAppendWord(dp, "u<", uIsLess, FW_DEFAULT);
+ dictAppendWord(dp, "um*", umStar, FW_DEFAULT);
+ dictAppendWord(dp, "um/mod", umSlashMod, FW_DEFAULT);
+ dictAppendWord(dp, "unloop", unloopCo, FW_COMPILE);
+ dictAppendWord(dp, "until", untilCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "variable", variable, FW_DEFAULT);
+ dictAppendWord(dp, "while", whileCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "word", ficlWord, FW_DEFAULT);
+ dictAppendWord(dp, "xor", bitwiseXor, FW_DEFAULT);
+ dictAppendWord(dp, "[", lbracketCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "[\']", bracketTickCoIm,FW_COMPIMMED);
+ dictAppendWord(dp, "[char]", charCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "]", rbracket, FW_DEFAULT);
+ /*
+ ** CORE EXT word set...
+ ** see softcore.fr for other definitions
+ */
+ /* "#tib" */
+ dictAppendWord(dp, ".(", dotParen, FW_IMMEDIATE);
+ /* ".r" */
+ dictAppendWord(dp, "0>", zeroGreater, FW_DEFAULT);
+ dictAppendWord(dp, "2>r", twoToR, FW_COMPILE);
+ dictAppendWord(dp, "2r>", twoRFrom, FW_COMPILE);
+ dictAppendWord(dp, "2r@", twoRFetch, FW_COMPILE);
+ dictAppendWord(dp, ":noname", colonNoName, FW_DEFAULT);
+ dictAppendWord(dp, "?do", qDoCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "again", againCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "c\"", cstringQuoteIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "hex", hex, FW_DEFAULT);
+ dictAppendWord(dp, "pad", pad, FW_DEFAULT);
+ dictAppendWord(dp, "parse", parse, FW_DEFAULT);
+ dictAppendWord(dp, "pick", pick, FW_DEFAULT);
+ /* query restore-input save-input tib u.r u> unused [compile] */
+ dictAppendWord(dp, "roll", roll, FW_DEFAULT);
+ dictAppendWord(dp, "refill", refill, FW_DEFAULT);
+ dictAppendWord(dp, "source-id", sourceid, FW_DEFAULT);
+ dictAppendWord(dp, "to", toValue, FW_IMMEDIATE);
+ dictAppendWord(dp, "value", constant, FW_DEFAULT);
+ dictAppendWord(dp, "\\", commentLine, FW_IMMEDIATE);
+
+
+ /*
+ ** Set CORE environment query values
+ */
+ ficlSetEnv(pSys, "/counted-string", FICL_STRING_MAX);
+ ficlSetEnv(pSys, "/hold", nPAD);
+ ficlSetEnv(pSys, "/pad", nPAD);
+ ficlSetEnv(pSys, "address-unit-bits", 8);
+ ficlSetEnv(pSys, "core", FICL_TRUE);
+ ficlSetEnv(pSys, "core-ext", FICL_FALSE);
+ ficlSetEnv(pSys, "floored", FICL_FALSE);
+ ficlSetEnv(pSys, "max-char", UCHAR_MAX);
+ ficlSetEnvD(pSys,"max-d", 0x7fffffff, 0xffffffff);
+ ficlSetEnv(pSys, "max-n", 0x7fffffff);
+ ficlSetEnv(pSys, "max-u", 0xffffffff);
+ ficlSetEnvD(pSys,"max-ud", 0xffffffff, 0xffffffff);
+ ficlSetEnv(pSys, "return-stack-cells",FICL_DEFAULT_STACK);
+ ficlSetEnv(pSys, "stack-cells", FICL_DEFAULT_STACK);
+
+ /*
+ ** DOUBLE word set (partial)
+ */
+ dictAppendWord(dp, "2constant", twoConstant, FW_IMMEDIATE);
+ dictAppendWord(dp, "2literal", twoLiteralIm, FW_IMMEDIATE);
+ dictAppendWord(dp, "2variable", twoVariable, FW_IMMEDIATE);
+ dictAppendWord(dp, "dnegate", dnegate, FW_DEFAULT);
+
+
+ /*
+ ** EXCEPTION word set
+ */
+ dictAppendWord(dp, "catch", ficlCatch, FW_DEFAULT);
+ dictAppendWord(dp, "throw", ficlThrow, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "exception", FICL_TRUE);
+ ficlSetEnv(pSys, "exception-ext", FICL_TRUE);
+
+ /*
+ ** LOCAL and LOCAL EXT
+ ** see softcore.c for implementation of locals|
+ */
+#if FICL_WANT_LOCALS
+ pSys->pLinkParen =
+ dictAppendWord(dp, "(link)", linkParen, FW_COMPILE);
+ pSys->pUnLinkParen =
+ dictAppendWord(dp, "(unlink)", unlinkParen, FW_COMPILE);
+ dictAppendWord(dp, "doLocal", doLocalIm, FW_COMPIMMED);
+ pSys->pGetLocalParen =
+ dictAppendWord(dp, "(@local)", getLocalParen, FW_COMPILE);
+ pSys->pToLocalParen =
+ dictAppendWord(dp, "(toLocal)", toLocalParen, FW_COMPILE);
+ pSys->pGetLocal0 =
+ dictAppendWord(dp, "(@local0)", getLocal0, FW_COMPILE);
+ pSys->pToLocal0 =
+ dictAppendWord(dp, "(toLocal0)",toLocal0, FW_COMPILE);
+ pSys->pGetLocal1 =
+ dictAppendWord(dp, "(@local1)", getLocal1, FW_COMPILE);
+ pSys->pToLocal1 =
+ dictAppendWord(dp, "(toLocal1)",toLocal1, FW_COMPILE);
+ dictAppendWord(dp, "(local)", localParen, FW_COMPILE);
+
+ pSys->pGet2LocalParen =
+ dictAppendWord(dp, "(@2local)", get2LocalParen, FW_COMPILE);
+ pSys->pTo2LocalParen =
+ dictAppendWord(dp, "(to2Local)",to2LocalParen, FW_COMPILE);
+ dictAppendWord(dp, "(2local)", twoLocalParen, FW_COMPILE);
+
+ ficlSetEnv(pSys, "locals", FICL_TRUE);
+ ficlSetEnv(pSys, "locals-ext", FICL_TRUE);
+ ficlSetEnv(pSys, "#locals", FICL_MAX_LOCALS);
+#endif
+
+ /*
+ ** Optional MEMORY-ALLOC word set
+ */
+
+ dictAppendWord(dp, "allocate", ansAllocate, FW_DEFAULT);
+ dictAppendWord(dp, "free", ansFree, FW_DEFAULT);
+ dictAppendWord(dp, "resize", ansResize, FW_DEFAULT);
+
+ ficlSetEnv(pSys, "memory-alloc", FICL_TRUE);
+
+ /*
+ ** optional SEARCH-ORDER word set
+ */
+ ficlCompileSearch(pSys);
+
+ /*
+ ** TOOLS and TOOLS EXT
+ */
+ ficlCompileTools(pSys);
+
+ /*
+ ** FILE and FILE EXT
+ */
+#if FICL_WANT_FILE
+ ficlCompileFile(pSys);
+#endif
+
+ /*
+ ** Ficl extras
+ */
+#if FICL_WANT_FLOAT
+ dictAppendWord(dp, ".hash", dictHashSummary,FW_DEFAULT);
+#endif
+ dictAppendWord(dp, ".ver", ficlVersion, FW_DEFAULT);
+ dictAppendWord(dp, "-roll", minusRoll, FW_DEFAULT);
+ dictAppendWord(dp, ">name", toName, FW_DEFAULT);
+ dictAppendWord(dp, "add-parse-step",
+ addParseStep, FW_DEFAULT);
+ dictAppendWord(dp, "body>", fromBody, FW_DEFAULT);
+ dictAppendWord(dp, "compare", compareString, FW_DEFAULT); /* STRING */
+ dictAppendWord(dp, "compare-insensitive", compareStringInsensitive, FW_DEFAULT); /* STRING */
+ dictAppendWord(dp, "compile-only",
+ compileOnly, FW_DEFAULT);
+ dictAppendWord(dp, "endif", endifCoIm, FW_COMPIMMED);
+ dictAppendWord(dp, "last-word", getLastWord, FW_DEFAULT);
+ dictAppendWord(dp, "hash", hash, FW_DEFAULT);
+ dictAppendWord(dp, "objectify", setObjectFlag, FW_DEFAULT);
+ dictAppendWord(dp, "?object", isObject, FW_DEFAULT);
+ dictAppendWord(dp, "parse-word",parseNoCopy, FW_DEFAULT);
+ dictAppendWord(dp, "sfind", sFind, FW_DEFAULT);
+ dictAppendWord(dp, "sliteral", sLiteralCoIm, FW_COMPIMMED); /* STRING */
+ dictAppendWord(dp, "sprintf", ficlSprintf, FW_DEFAULT);
+ dictAppendWord(dp, "strlen", ficlStrlen, FW_DEFAULT);
+ dictAppendWord(dp, "q@", quadFetch, FW_DEFAULT);
+ dictAppendWord(dp, "q!", quadStore, FW_DEFAULT);
+ dictAppendWord(dp, "w@", wFetch, FW_DEFAULT);
+ dictAppendWord(dp, "w!", wStore, FW_DEFAULT);
+ dictAppendWord(dp, "x.", hexDot, FW_DEFAULT);
+#if FICL_WANT_USER
+ dictAppendWord(dp, "(user)", userParen, FW_DEFAULT);
+ dictAppendWord(dp, "user", userVariable, FW_DEFAULT);
+#endif
+ dictAppendWord(dp, "random", ficlRandom, FW_DEFAULT);
+ dictAppendWord(dp, "seed-random",ficlSeedRandom,FW_DEFAULT);
+
+ /*
+ ** internal support words
+ */
+ dictAppendWord(dp, "(create)", createParen, FW_COMPILE);
+ pSys->pExitParen =
+ dictAppendWord(dp, "(exit)", exitParen, FW_COMPILE);
+ pSys->pSemiParen =
+ dictAppendWord(dp, "(;)", semiParen, FW_COMPILE);
+ pSys->pLitParen =
+ dictAppendWord(dp, "(literal)", literalParen, FW_COMPILE);
+ pSys->pTwoLitParen =
+ dictAppendWord(dp, "(2literal)",twoLitParen, FW_COMPILE);
+ pSys->pStringLit =
+ dictAppendWord(dp, "(.\")", stringLit, FW_COMPILE);
+ pSys->pCStringLit =
+ dictAppendWord(dp, "(c\")", cstringLit, FW_COMPILE);
+ pSys->pBranch0 =
+ dictAppendWord(dp, "(branch0)", branch0, FW_COMPILE);
+ pSys->pBranchParen =
+ dictAppendWord(dp, "(branch)", branchParen, FW_COMPILE);
+ pSys->pDoParen =
+ dictAppendWord(dp, "(do)", doParen, FW_COMPILE);
+ pSys->pDoesParen =
+ dictAppendWord(dp, "(does>)", doesParen, FW_COMPILE);
+ pSys->pQDoParen =
+ dictAppendWord(dp, "(?do)", qDoParen, FW_COMPILE);
+ pSys->pLoopParen =
+ dictAppendWord(dp, "(loop)", loopParen, FW_COMPILE);
+ pSys->pPLoopParen =
+ dictAppendWord(dp, "(+loop)", plusLoopParen, FW_COMPILE);
+ pSys->pInterpret =
+ dictAppendWord(dp, "interpret", interpret, FW_DEFAULT);
+ dictAppendWord(dp, "lookup", lookup, FW_DEFAULT);
+ pSys->pOfParen =
+ dictAppendWord(dp, "(of)", ofParen, FW_DEFAULT);
+ dictAppendWord(dp, "(variable)",variableParen, FW_COMPILE);
+ dictAppendWord(dp, "(constant)",constantParen, FW_COMPILE);
+ dictAppendWord(dp, "(parse-step)",
+ parseStepParen, FW_DEFAULT);
+ pSys->pExitInner =
+ dictAppendWord(dp, "exit-inner",ficlExitInner, FW_DEFAULT);
+
+ /*
+ ** Set up system's outer interpreter loop - maybe this should be in initSystem?
+ */
+ pSys->pInterp[0] = pSys->pInterpret;
+ pSys->pInterp[1] = pSys->pBranchParen;
+ pSys->pInterp[2] = (FICL_WORD *)(void *)(-2);
+
+ assert(dictCellsAvail(dp) > 0);
+
+ return;
+}
+