shithub: 9ficl

ref: 7c76dac1b268038f567939a70a18228e790a5cbc
dir: /vm.c/

View raw version
/*******************************************************************
** 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.22 2010/12/22 09:05:52 asau Exp $
*******************************************************************/
/*
** 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 interpreter.
*/
/*
** 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"

#if FICL_ROBUST >= 2
#define FICL_VM_CHECK(vm) FICL_VM_ASSERT(vm, (*(vm->ip - 1)) == vm->runningWord)
#else
#define FICL_VM_CHECK(vm) 
#endif

/**************************************************************************
                        v m B r a n c h R e l a t i v e 
** 
**************************************************************************/
void ficlVmBranchRelative(ficlVm *vm, int offset)
{
    vm->ip += offset;
    return;
}


/**************************************************************************
                        v m C r e a t e
** Creates a virtual machine either from scratch (if vm is NULL on entry)
** or by resizing and reinitializing an existing VM to the specified stack
** sizes.
**************************************************************************/
ficlVm *ficlVmCreate(ficlVm *vm, unsigned nPStack, unsigned nRStack)
{
    if (vm == NULL)
    {
        vm = (ficlVm *)ficlMalloc(sizeof (ficlVm));
        FICL_ASSERT(NULL, vm);
        memset(vm, 0, sizeof (ficlVm));
    }

    if (vm->dataStack)
        ficlStackDestroy(vm->dataStack);
    vm->dataStack = ficlStackCreate(vm, "data", nPStack);

    if (vm->returnStack)
        ficlStackDestroy(vm->returnStack);
    vm->returnStack = ficlStackCreate(vm, "return", nRStack);

#if FICL_WANT_FLOAT
    if (vm->floatStack)
        ficlStackDestroy(vm->floatStack);
    vm->floatStack = ficlStackCreate(vm, "float", nPStack);
#endif

    ficlVmReset(vm);
    return vm;
}


/**************************************************************************
                        v m D e l e t e
** Free all memory allocated to the specified VM and its subordinate 
** structures.
**************************************************************************/
void ficlVmDestroy(ficlVm *vm)
{
    if (vm)
    {
        ficlFree(vm->dataStack);
        ficlFree(vm->returnStack);
#if FICL_WANT_FLOAT
        ficlFree(vm->floatStack);
#endif
        ficlFree(vm);
    }

    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 interpreter
** to complete. This does not happen until control reaches ficlExec
**************************************************************************/
void ficlVmExecuteWord(ficlVm *vm, ficlWord *pWord)
{
	ficlVmInnerLoop(vm, pWord);
    return;
}



static void ficlVmOptimizeJumpToJump(ficlVm *vm, ficlIp ip)
	{
	ficlIp destination;
	switch ((ficlInstruction)(*ip))
		{
		case ficlInstructionBranchParenWithCheck:
			*ip = (ficlWord *)ficlInstructionBranchParen;
			goto RUNTIME_FIXUP;

		case ficlInstructionBranch0ParenWithCheck:
			*ip = (ficlWord *)ficlInstructionBranch0Paren;
RUNTIME_FIXUP:
			ip++;
			destination = ip + *(int *)ip;
			switch ((ficlInstruction)*destination)
			{
				case ficlInstructionBranchParenWithCheck:
					/* preoptimize where we're jumping to */
					ficlVmOptimizeJumpToJump(vm, destination);
				case ficlInstructionBranchParen:
				{
					destination++;
					destination += *(int *)destination;
					*ip = (ficlWord *)(destination - ip);
					break;
				}
			}
		}
	}

/**************************************************************************
                        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()
**************************************************************************/


#if FICL_ROBUST <= 1
	/* turn off stack checking for primitives */
	#define _CHECK_STACK(stack, top, pop, push)
#else

#define _CHECK_STACK(stack, top, pop, push)	\
	ficlStackCheckNospill(stack, top, pop, push)

FICL_PLATFORM_INLINE void ficlStackCheckNospill(ficlStack *stack, ficlCell *top, int popCells, int pushCells)
{
	/*
	** Why save and restore stack->top?
	** So the simple act of stack checking doesn't force a "register" spill,
	** which might mask bugs (places where we needed to spill but didn't).
	** --lch
	*/
	ficlCell *oldTop = stack->top;
	stack->top = top;
	ficlStackCheck(stack, popCells, pushCells);
	stack->top = oldTop;
}

#endif /* FICL_ROBUST <= 1 */

#define CHECK_STACK(pop, push)         _CHECK_STACK(vm->dataStack, dataTop, pop, push)
#define CHECK_FLOAT_STACK(pop, push)   _CHECK_STACK(vm->floatStack, floatTop, pop, push)
#define CHECK_RETURN_STACK(pop, push)  _CHECK_STACK(vm->returnStack, returnTop, pop, push)


#if FICL_WANT_FLOAT
	#define FLOAT_LOCAL_VARIABLE_SPILL	\
		vm->floatStack->top = floatTop;
	#define FLOAT_LOCAL_VARIABLE_REFILL	\
		floatTop = vm->floatStack->top;
#else
	#define FLOAT_LOCAL_VARIABLE_SPILL
	#define FLOAT_LOCAL_VARIABLE_REFILL
#endif  /* FICL_WANT_FLOAT */


#if FICL_WANT_LOCALS
	#define LOCALS_LOCAL_VARIABLE_SPILL	\
		vm->returnStack->frame = frame;
	#define LOCALS_LOCAL_VARIABLE_REFILL \
		frame = vm->returnStack->frame;
#else
	#define LOCALS_LOCAL_VARIABLE_SPILL
	#define LOCALS_LOCAL_VARIABLE_REFILL
#endif  /* FICL_WANT_FLOAT */


#define LOCAL_VARIABLE_SPILL	\
		vm->ip = (ficlIp)ip;	\
		vm->dataStack->top = dataTop;	\
		vm->returnStack->top = returnTop;	\
		FLOAT_LOCAL_VARIABLE_SPILL \
		LOCALS_LOCAL_VARIABLE_SPILL

#define LOCAL_VARIABLE_REFILL	\
		ip = (ficlInstruction *)vm->ip; \
		dataTop = vm->dataStack->top;	\
		returnTop = vm->returnStack->top;	\
		FLOAT_LOCAL_VARIABLE_REFILL	\
		LOCALS_LOCAL_VARIABLE_REFILL


void ficlVmInnerLoop(ficlVm *vm, ficlWord *fw)
{
	register ficlInstruction *ip;
	register ficlCell *dataTop;
	register ficlCell *returnTop;
#if FICL_WANT_FLOAT
	register ficlCell *floatTop;
#endif  /* FICL_WANT_FLOAT */
#if FICL_WANT_LOCALS
	register ficlCell *frame;
#endif  /* FICL_WANT_LOCALS */
    jmp_buf   *oldExceptionHandler;
    jmp_buf    exceptionHandler;
    int        except;
	int        once;
	int        count;
	ficlInstruction instruction;
	ficlInteger i;
	ficlUnsigned u;
	ficlCell c;
	ficlCountedString *s;
	ficlCell *cell;
	char *cp;

	once = (fw != NULL);
	if (once)
		count = 1;

	LOCAL_VARIABLE_REFILL;

    oldExceptionHandler = vm->exceptionHandler;
    vm->exceptionHandler = &exceptionHandler; /* This has to come before the setjmp! */
    except = setjmp(exceptionHandler);

	if (except)
		{
		LOCAL_VARIABLE_SPILL;
	    vm->exceptionHandler = oldExceptionHandler;
		ficlVmThrow(vm, except);
		}

	for (;;)
	{

		if (once)
		{
			if (!count--)
				break;
			instruction = (ficlInstruction)((void *)fw);
		}
		else
		{
			instruction = *ip++;
			fw = (ficlWord *)instruction;
		}

AGAIN:
		switch (instruction)
		{
			case ficlInstructionInvalid:
			{
		        ficlVmThrowError(vm, "Error: NULL instruction executed!");
				return;
			}

			case ficlInstruction1:
			case ficlInstruction2:
			case ficlInstruction3:
			case ficlInstruction4:
			case ficlInstruction5:
			case ficlInstruction6:
			case ficlInstruction7:
			case ficlInstruction8:
			case ficlInstruction9:
			case ficlInstruction10:
			case ficlInstruction11:
			case ficlInstruction12:
			case ficlInstruction13:
			case ficlInstruction14:
			case ficlInstruction15:
			case ficlInstruction16:
			{
				CHECK_STACK(0, 1);
				(++dataTop)->i = instruction;
				break;
			}

			case ficlInstruction0:
			case ficlInstructionNeg1:
			case ficlInstructionNeg2:
			case ficlInstructionNeg3:
			case ficlInstructionNeg4:
			case ficlInstructionNeg5:
			case ficlInstructionNeg6:
			case ficlInstructionNeg7:
			case ficlInstructionNeg8:
			case ficlInstructionNeg9:
			case ficlInstructionNeg10:
			case ficlInstructionNeg11:
			case ficlInstructionNeg12:
			case ficlInstructionNeg13:
			case ficlInstructionNeg14:
			case ficlInstructionNeg15:
			case ficlInstructionNeg16:
			{
				CHECK_STACK(0, 1);
				(++dataTop)->i = ficlInstruction0 - instruction;
				break;
			}

			/**************************************************************************
			** 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.
			**************************************************************************/
			case ficlInstructionStringLiteralParen:
			{
				ficlUnsigned8 length;
				CHECK_STACK(0, 2);

				s = (ficlCountedString *)(ip);
				length = s->length;
				cp = s->text;
				(++dataTop)->p = cp;
				(++dataTop)->i = length;

				cp += length + 1;
				cp = (char *)ficlAlignPointer(cp);
				ip = (ficlInstruction *)cp;
				break;
			}

			case ficlInstructionCStringLiteralParen:
			{
				CHECK_STACK(0, 1);

				s = (ficlCountedString *)(ip);
				cp = s->text + s->length + 1;
				cp = (char *)ficlAlignPointer(cp);
				ip = (ficlInstruction *)cp;
				(++dataTop)->p = s;
				break;
			}

			
#if FICL_WANT_OPTIMIZE == FICL_OPTIMIZE_FOR_SIZE
	#if FICL_WANT_FLOAT
		FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC:
			*++floatTop = cell[1];
			/* intentional fall-through */
		FLOAT_PUSH_CELL_POINTER_MINIPROC:
			*++floatTop = cell[0];
			break;

		FLOAT_POP_CELL_POINTER_MINIPROC:
			cell[0] = *floatTop--;
			break;
		FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC:
			cell[0] = *floatTop--;
			cell[1] = *floatTop--;
			break;

		#define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); goto FLOAT_PUSH_CELL_POINTER_DOUBLE_MINIPROC
		#define FLOAT_PUSH_CELL_POINTER(cp)        cell = (cp); goto FLOAT_PUSH_CELL_POINTER_MINIPROC
		#define FLOAT_POP_CELL_POINTER_DOUBLE(cp)  cell = (cp); goto FLOAT_POP_CELL_POINTER_DOUBLE_MINIPROC
		#define FLOAT_POP_CELL_POINTER(cp)         cell = (cp); goto FLOAT_POP_CELL_POINTER_MINIPROC
	#endif /* FICL_WANT_FLOAT */

		/*
		** Think of these as little mini-procedures.
		** --lch
		*/
		PUSH_CELL_POINTER_DOUBLE_MINIPROC:
			*++dataTop = cell[1];
			/* intentional fall-through */
		PUSH_CELL_POINTER_MINIPROC:
			*++dataTop = cell[0];
			break;

		POP_CELL_POINTER_MINIPROC:
			cell[0] = *dataTop--;
			break;
		POP_CELL_POINTER_DOUBLE_MINIPROC:
			cell[0] = *dataTop--;
			cell[1] = *dataTop--;
			break;

		#define PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); goto PUSH_CELL_POINTER_DOUBLE_MINIPROC
		#define PUSH_CELL_POINTER(cp)        cell = (cp); goto PUSH_CELL_POINTER_MINIPROC
		#define POP_CELL_POINTER_DOUBLE(cp)  cell = (cp); goto POP_CELL_POINTER_DOUBLE_MINIPROC
		#define POP_CELL_POINTER(cp)         cell = (cp); goto POP_CELL_POINTER_MINIPROC

		BRANCH_MINIPROC:
			ip += *(ficlInstruction *)ip;
			break;

		#define BRANCH()         goto BRANCH_MINIPROC

		EXIT_FUNCTION_MINIPROC:
		    ip = (ficlInstruction *)((returnTop--)->p);
			break;

		#define EXIT_FUNCTION    goto EXIT_FUNCTION_MINIPROC

#else /* FICL_WANT_SIZE */

	#if FICL_WANT_FLOAT
		#define FLOAT_PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); *++floatTop = cell[1]; *++floatTop = *cell; break
		#define FLOAT_PUSH_CELL_POINTER(cp)        cell = (cp); *++floatTop = *cell; break
		#define FLOAT_POP_CELL_POINTER_DOUBLE(cp)  cell = (cp); *cell = *floatTop--; cell[1] = *floatTop--; break
		#define FLOAT_POP_CELL_POINTER(cp)         cell = (cp); *cell = *floatTop--; break
	#endif /* FICL_WANT_FLOAT */

		#define PUSH_CELL_POINTER_DOUBLE(cp) cell = (cp); *++dataTop = cell[1]; *++dataTop = *cell; break
		#define PUSH_CELL_POINTER(cp)        cell = (cp); *++dataTop = *cell; break
		#define POP_CELL_POINTER_DOUBLE(cp)  cell = (cp); *cell = *dataTop--; cell[1] = *dataTop--; break
		#define POP_CELL_POINTER(cp)         cell = (cp); *cell = *dataTop--; break

		#define BRANCH()         ip += *(ficlInteger *)ip; break
		#define EXIT_FUNCTION()  ip = (ficlInstruction *)((returnTop--)->p); break

#endif /* FICL_WANT_SIZE */


			/**************************************************************************
			** This is the runtime for (literal). It assumes that it is part of a colon
			** definition, and that the next ficlCell contains a value to be pushed on the
			** parameter stack at runtime. This code is compiled by "literal".
			**************************************************************************/

			case ficlInstructionLiteralParen:
			{
				CHECK_STACK(0, 1);
				(++dataTop)->i = *ip++;
				break;
			}

			case ficlInstruction2LiteralParen:
			{
				CHECK_STACK(0, 2);
				(++dataTop)->i = ip[1];
				(++dataTop)->i = ip[0];
				ip += 2;
				break;
			}


#if FICL_WANT_LOCALS
			/**************************************************************************
			** Link a frame on the return stack, reserving nCells of space for
			** locals - the value of nCells is the next ficlCell in the instruction
			** stream.
			** 1) Push frame onto returnTop
			** 2) frame = returnTop
			** 3) returnTop += nCells
			**************************************************************************/
			case ficlInstructionLinkParen:
			{
				ficlInteger nCells = *ip++;
				(++returnTop)->p = frame;
				frame = returnTop + 1;
				returnTop += nCells;
				break;
			}


			/**************************************************************************
			** Unink a stack frame previously created by stackLink
			** 1) dataTop = frame
			** 2) frame = pop()
			*******************************************************************/
			case ficlInstructionUnlinkParen:
			{
			    returnTop = frame - 1;
				frame = (ficlCell *)(returnTop--)->p;
				break;
			}


			/**************************************************************************
			** 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
			**************************************************************************/
#if FICL_WANT_FLOAT
			case ficlInstructionGetF2LocalParen:
				FLOAT_PUSH_CELL_POINTER_DOUBLE(frame + *ip++);

			case ficlInstructionGetFLocalParen:
				FLOAT_PUSH_CELL_POINTER(frame + *ip++);

			case ficlInstructionToF2LocalParen:
				FLOAT_POP_CELL_POINTER_DOUBLE(frame + *ip++);

			case ficlInstructionToFLocalParen:
				FLOAT_POP_CELL_POINTER(frame + *ip++);
#endif /* FICL_WANT_FLOAT */

			case ficlInstructionGet2LocalParen:
				PUSH_CELL_POINTER_DOUBLE(frame + *ip++);

			case ficlInstructionGetLocalParen:
				PUSH_CELL_POINTER(frame + *ip++);

			/**************************************************************************
			** Immediate - cfa of a local while compiling - when executed, compiles
			** code to store the value of a local given the local's index in the
			** word's pfa
			**************************************************************************/

			case ficlInstructionTo2LocalParen:
				POP_CELL_POINTER_DOUBLE(frame + *ip++);

			case ficlInstructionToLocalParen:
				POP_CELL_POINTER(frame + *ip++);

			/*
			** Silly little minor optimizations.
			** --lch
			*/
			case ficlInstructionGetLocal0:
				PUSH_CELL_POINTER(frame);

			case ficlInstructionGetLocal1:
				PUSH_CELL_POINTER(frame + 1);

			case ficlInstructionGet2Local0:
				PUSH_CELL_POINTER_DOUBLE(frame);

			case ficlInstructionToLocal0:
				POP_CELL_POINTER(frame);

			case ficlInstructionToLocal1:
				POP_CELL_POINTER(frame + 1);

			case ficlInstructionTo2Local0:
				POP_CELL_POINTER_DOUBLE(frame);

#endif /* FICL_WANT_LOCALS */

			case ficlInstructionPlus:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i += i;
				break;
			}

			case ficlInstructionMinus:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i -= i;
				break;
			}

			case ficlInstruction1Plus:
			{
				CHECK_STACK(1, 1);
				dataTop->i++;
				break;
			}

			case ficlInstruction1Minus:
			{
				CHECK_STACK(1, 1);
				dataTop->i--;
				break;
			}

			case ficlInstruction2Plus:
			{
				CHECK_STACK(1, 1);
				dataTop->i += 2;
				break;
			}

			case ficlInstruction2Minus:
			{
				CHECK_STACK(1, 1);
				dataTop->i -= 2;
				break;
			}

			case ficlInstructionDup:
			{
				ficlInteger i = dataTop->i;
				CHECK_STACK(0, 1);
				(++dataTop)->i = i;
				break;
			}

			case ficlInstructionQuestionDup:
			{
				CHECK_STACK(1, 2);

				if (dataTop->i != 0)
					{
					dataTop[1] = dataTop[0];
					dataTop++;
					}

				break;
			}

			case ficlInstructionSwap:
			{
				ficlCell swap;
				CHECK_STACK(2, 2);
				swap = dataTop[0];
				dataTop[0] = dataTop[-1];
				dataTop[-1] = swap;
				break;
			}

			case ficlInstructionDrop:
			{
				CHECK_STACK(1, 0);
				dataTop--;
				break;
			}


			case ficlInstruction2Drop:
			{
				CHECK_STACK(2, 0);
				dataTop -= 2;
				break;
			}


			case ficlInstruction2Dup:
			{
				CHECK_STACK(2, 4);
				dataTop[1] = dataTop[-1];
				dataTop[2] = *dataTop;
				dataTop += 2;
				break;
			}


			case ficlInstructionOver:
			{
				CHECK_STACK(2, 3);
				dataTop[1] = dataTop[-1];
				dataTop++;
				break;
			}

			case ficlInstruction2Over:
			{
				CHECK_STACK(4, 6);
				dataTop[1] = dataTop[-3];
				dataTop[2] = dataTop[-2];
				dataTop += 2;
				break;
			}


			case ficlInstructionPick:
			{
				CHECK_STACK(1, 0);
				i = dataTop->i;
				if (i < 0)
					break;
				CHECK_STACK(i + 1, i + 2);
				*dataTop = dataTop[-i];
				break;
			}


			/*******************************************************************
			** Do stack rot.
			** rot ( 1 2 3  -- 2 3 1 )
			*******************************************************************/
			case ficlInstructionRot:
			{
				i = 2;
				goto ROLL;
			}

			/*******************************************************************
			** Do stack roll.
			** roll ( n -- )
			*******************************************************************/
			case ficlInstructionRoll:
			{
				CHECK_STACK(1, 0);
				i = (dataTop--)->i;

				if (i < 1)
					break;

ROLL:
				CHECK_STACK(i+1, i+2);
				c = dataTop[-i];
				memmove(dataTop - i, dataTop - (i - 1), i * sizeof(ficlCell));
				*dataTop = c;

				break;
			}

			/*******************************************************************
			** Do stack -rot.
			** -rot ( 1 2 3  -- 3 1 2 )
			*******************************************************************/
			case ficlInstructionMinusRot:
			{
				i = 2;
				goto MINUSROLL;
			}


			/*******************************************************************
			** Do stack -roll.
			** -roll ( n -- )
			*******************************************************************/
			case ficlInstructionMinusRoll:
			{
				CHECK_STACK(1, 0);
				i = (dataTop--)->i;

				if (i < 1)
					break;

MINUSROLL:
				CHECK_STACK(i+1, i+2);
				c = *dataTop;
				memmove(dataTop - (i - 1), dataTop - i, i * sizeof(ficlCell));
				dataTop[-i] = c;

				break;
			}



			/*******************************************************************
			** Do stack 2swap
			** 2swap ( 1 2 3 4  -- 3 4 1 2 )
			*******************************************************************/
			case ficlInstruction2Swap:
			{
				ficlCell c2;
				CHECK_STACK(4, 4);

				c = *dataTop;
				c2 = dataTop[-1];

				*dataTop = dataTop[-2];
				dataTop[-1] = dataTop[-3];

				dataTop[-2] = c;
				dataTop[-3] = c2;
				break;
			}


			case ficlInstructionPlusStore:
			{
				ficlCell *cell;
				CHECK_STACK(2, 0);
				cell = (ficlCell *)(dataTop--)->p;
				cell->i += (dataTop--)->i;
				break;
			}


			case ficlInstructionQuadFetch:
			{
				ficlUnsigned32 *integer32;
				CHECK_STACK(1, 1);
				integer32 = (ficlUnsigned32 *)dataTop->i;
				dataTop->u = (ficlUnsigned)*integer32;
				break;
			}

			case ficlInstructionQuadStore:
			{
				ficlUnsigned32 *integer32;
				CHECK_STACK(2, 0);
				integer32 = (ficlUnsigned32 *)(dataTop--)->p;
				*integer32 = (ficlUnsigned32)((dataTop--)->u);
				break;
			}

			case ficlInstructionWFetch:
			{
				ficlUnsigned16 *integer16;
				CHECK_STACK(1, 1);
				integer16 = (ficlUnsigned16 *)dataTop->p;
				dataTop->u = ((ficlUnsigned)*integer16);
				break;
			}

			case ficlInstructionWStore:
			{
				ficlUnsigned16 *integer16;
				CHECK_STACK(2, 0);
				integer16 = (ficlUnsigned16 *)(dataTop--)->p;
				*integer16 = (ficlUnsigned16)((dataTop--)->u);
				break;
			}

			case ficlInstructionCFetch:
			{
				ficlUnsigned8 *integer8;
				CHECK_STACK(1, 1);
				integer8 = (ficlUnsigned8 *)dataTop->p;
				dataTop->u = ((ficlUnsigned)*integer8);
				break;
			}

			case ficlInstructionCStore:
			{
				ficlUnsigned8 *integer8;
				CHECK_STACK(2, 0);
				integer8 = (ficlUnsigned8 *)(dataTop--)->p;
				*integer8 = (ficlUnsigned8)((dataTop--)->u);
				break;
			}


			/**************************************************************************
									l o g i c   a n d   c o m p a r i s o n s
			** 
			**************************************************************************/

			case ficlInstruction0Equals:
			{
				CHECK_STACK(1, 1);
				dataTop->i = FICL_BOOL(dataTop->i == 0);
				break;
			}

			case ficlInstruction0Less:
			{
				CHECK_STACK(1, 1);
				dataTop->i = FICL_BOOL(dataTop->i < 0);
				break;
			}

			case ficlInstruction0Greater:
			{
				CHECK_STACK(1, 1);
				dataTop->i = FICL_BOOL(dataTop->i > 0);
				break;
			}

			case ficlInstructionEquals:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i = FICL_BOOL(dataTop->i == i);
				break;
			}

			case ficlInstructionLess:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i = FICL_BOOL(dataTop->i < i);
				break;
			}

			case ficlInstructionULess:
			{
				CHECK_STACK(2, 1);
				u = (dataTop--)->u;
				dataTop->i = FICL_BOOL(dataTop->u < u);
				break;
			}

			case ficlInstructionAnd:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i = dataTop->i & i;
				break;
			}

			case ficlInstructionOr:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i = dataTop->i | i;
				break;
			}

			case ficlInstructionXor:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i = dataTop->i ^ i;
				break;
			}

			case ficlInstructionInvert:
			{
				CHECK_STACK(1, 1);
				dataTop->i = ~dataTop->i;
				break;
			}

			/**************************************************************************
									r e t u r n   s t a c k
			** 
			**************************************************************************/
			case ficlInstructionToRStack:
			{
				CHECK_STACK(1, 0);
				CHECK_RETURN_STACK(0, 1);
				*++returnTop = *dataTop--;
				break;
			}

			case ficlInstructionFromRStack:
			{
				CHECK_STACK(0, 1);
				CHECK_RETURN_STACK(1, 0);
				*++dataTop = *returnTop--;
				break;
			}

			case ficlInstructionFetchRStack:
			{
				CHECK_STACK(0, 1);
				CHECK_RETURN_STACK(1, 1);
				*++dataTop = *returnTop;
				break;
			}

			case ficlInstruction2ToR:
			{
				CHECK_STACK(2, 0);
				CHECK_RETURN_STACK(0, 2);
				*++returnTop = dataTop[-1];
				*++returnTop = dataTop[0];
				dataTop -= 2;
				break;
			}

			case ficlInstruction2RFrom:
			{
				CHECK_STACK(0, 2);
				CHECK_RETURN_STACK(2, 0);
				*++dataTop = returnTop[-1];
				*++dataTop = returnTop[0];
				returnTop -= 2;
				break;
			}

			case ficlInstruction2RFetch:
			{
				CHECK_STACK(0, 2);
				CHECK_RETURN_STACK(2, 2);
				*++dataTop = returnTop[-1];
				*++dataTop = returnTop[0];
				break;
			}


			/**************************************************************************
									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. 
			**************************************************************************/
			case ficlInstructionFill:
			{
				char c;
				char *memory;
				CHECK_STACK(3, 0);
				c = (char)(dataTop--)->i;
				u = (dataTop--)->u;
				memory = (char *)(dataTop--)->p;

				/* memset() is faster than the previous hand-rolled solution.  --lch */
				memset(memory, c, u);
				break;
			}


			/**************************************************************************
									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 ficlCell. 
			**
			** 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 ficlCell. 
			**************************************************************************/
			case ficlInstructionLShift:
			{
				ficlUnsigned nBits;
				ficlUnsigned x1;
				CHECK_STACK(2, 1);

				nBits = (dataTop--)->u;
				x1 = dataTop->u;
				dataTop->u = x1 << nBits;
				break;
			}


			case ficlInstructionRShift:
			{
				ficlUnsigned nBits;
				ficlUnsigned x1;
				CHECK_STACK(2, 1);

				nBits = (dataTop--)->u;
				x1 = dataTop->u;
				dataTop->u = x1 >> nBits;
				break;
			}


			/**************************************************************************
									m a x   &   m i n
			** 
			**************************************************************************/
			case ficlInstructionMax:
			{
				ficlInteger n2;
				ficlInteger n1;
				CHECK_STACK(2, 1);

				n2 = (dataTop--)->i;
				n1 = dataTop->i;

				dataTop->i = ((n1 > n2) ? n1 : n2);
				break;
			}

			case ficlInstructionMin:
			{
				ficlInteger n2;
				ficlInteger n1;
				CHECK_STACK(2, 1);

				n2 = (dataTop--)->i;
				n1 = dataTop->i;

				dataTop->i = ((n1 < n2) ? n1 : n2);
				break;
			}


			/**************************************************************************
									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.
			**************************************************************************/
			case ficlInstructionMove:
			{
				ficlUnsigned u;
				char *addr2;
				char *addr1;
				CHECK_STACK(3, 0);

				u = (dataTop--)->u;
				addr2 = (char *)(dataTop--)->p;
				addr1 = (char *)(dataTop--)->p;

				if (u == 0) 
					break;
				/*
				** Do the copy carefully, so as to be
				** correct even if the two ranges overlap
				*/
				/* Which ANSI C's memmove() does for you!  Yay!  --lch */
				memmove(addr2, addr1, u);
				break;
			}


			/**************************************************************************
									s t o d
			** s-to-d CORE ( n -- d )
			** Convert the number n to the double-ficlCell number d with the same
			** numerical value. 
			**************************************************************************/
			case ficlInstructionSToD:
			{
				ficlInteger s;
				CHECK_STACK(1, 2);

				s = dataTop->i;

				/* sign extend to 64 bits.. */
				(++dataTop)->i = (s < 0) ? -1 : 0;
				break;
			}


			/**************************************************************************
									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. 
			**************************************************************************/
			case ficlInstructionCompare:
			{
				i = FICL_FALSE;
				goto COMPARE;
			}


			case ficlInstructionCompareInsensitive:
			{
				i = FICL_TRUE;
				goto COMPARE;
			}

COMPARE:
			{
				char *cp1, *cp2;
				ficlUnsigned u1, u2, uMin;
				int n = 0;

				CHECK_STACK(4, 1);
				u2  = (dataTop--)->u;
				cp2 = (char *)(dataTop--)->p;
				u1  = (dataTop--)->u;
				cp1 = (char *)(dataTop--)->p;

				uMin = (u1 < u2)? u1 : u2;
				for ( ; (uMin > 0) && (n == 0); uMin--)
				{
					int c1 = (unsigned char)*cp1++;
					int c2 = (unsigned char)*cp2++;
					if (i)
					{
						c1 = tolower(c1);
						c2 = tolower(c2);
					}
					n = (c1 - c2);
				}

				if (n == 0)
					n = (int)(u1 - u2);

				if (n < 0) 
					n = -1;
				else if (n > 0)
					n = 1;

				(++dataTop)->i = n;
				break;
			}


			/**************************************************************************
			**                     r a n d o m
			** Ficl-specific
			**************************************************************************/
			case ficlInstructionRandom:
			{
				(++dataTop)->i = rand();
				break;
			}


			/**************************************************************************
			**                     s e e d - r a n d o m
			** Ficl-specific
			**************************************************************************/
			case ficlInstructionSeedRandom:
			{
				srand((dataTop--)->i);
				break;
			}



			case ficlInstructionGreaterThan:
			{
				ficlInteger x, y;
				CHECK_STACK(2, 1);
				y = (dataTop--)->i;
				x = dataTop->i;
				dataTop->i = FICL_BOOL(x > y);
				break;
			}

			/**************************************************************************
			** 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".
			**************************************************************************/
			case ficlInstructionExitParen:
			case ficlInstructionSemiParen:
				EXIT_FUNCTION();

			/**************************************************************************
			** The first time we run "(branch)", perform a "peephole optimization" to
			** see if we're jumping to another unconditional jump.  If so, just jump
			** directly there.
			**************************************************************************/
			case ficlInstructionBranchParenWithCheck:
			{
				LOCAL_VARIABLE_SPILL;
				ficlVmOptimizeJumpToJump(vm, vm->ip - 1);
				LOCAL_VARIABLE_REFILL;
				goto BRANCH_PAREN;
			}

			/**************************************************************************
			** Same deal with branch0.
			**************************************************************************/
			case ficlInstructionBranch0ParenWithCheck:
			{
				LOCAL_VARIABLE_SPILL;
				ficlVmOptimizeJumpToJump(vm, vm->ip - 1);
				LOCAL_VARIABLE_REFILL;
				/* intentional fall-through */
			}

			/**************************************************************************
			** Runtime code for "(branch0)"; pop a flag from the stack,
			** branch if 0. fall through otherwise.  The heart of "if" and "until".
			**************************************************************************/
			case ficlInstructionBranch0Paren:
			{
				CHECK_STACK(1, 0);

				if ((dataTop--)->i)
					{
					/* don't branch, but skip over branch relative address */
				    ip += 1;
					break;
					}
				/* otherwise, take branch (to else/endif/begin) */
				/* intentional fall-through! */
			}

			/**************************************************************************
			** Runtime for "(branch)" -- expects a literal offset in the next
			** compilation address, and branches to that location.
			**************************************************************************/
			case ficlInstructionBranchParen:
			{
BRANCH_PAREN:
				BRANCH();
			}

			case ficlInstructionOfParen:
			{
				ficlUnsigned a, b;

				CHECK_STACK(2, 1);

				a = (dataTop--)->u;
				b = dataTop->u;

				if (a == b)
				{
					/* fall through */
					ip++;
					/* remove CASE argument */
					dataTop--;
				}
				else 
				{
					/* take branch to next of or endcase */
					BRANCH();
				}

				break;
			}

			case ficlInstructionDoParen:
			{
				ficlCell index, limit;

				CHECK_STACK(2, 0);

				index = *dataTop--;
				limit = *dataTop--;

				/* copy "leave" target addr to stack */
				(++returnTop)->i = *(ip++);
				*++returnTop = limit;
				*++returnTop = index;

				break;
			}

			case ficlInstructionQDoParen:
			{
				ficlCell index, limit, leave;

				CHECK_STACK(2, 0);

				index = *dataTop--;
				limit = *dataTop--;

				leave.i = *ip;

				if (limit.u == index.u)
				{
				    ip = (ficlInstruction *)leave.p;
				}
				else
				{
					ip++;
					*++returnTop = leave;
					*++returnTop = limit;
					*++returnTop = index;
				}

				break;
			}

			case ficlInstructionLoopParen:
			case ficlInstructionPlusLoopParen:
			{
				ficlInteger index;
				ficlInteger limit;
				int direction = 0;

				index = returnTop->i;
				limit = returnTop[-1].i;

				if (instruction == ficlInstructionLoopParen)
					index++;
				else
				{
					ficlInteger increment;
					CHECK_STACK(1, 0);
					increment = (dataTop--)->i;
					index += increment;
					direction = (increment < 0);
				}

				if (direction ^ (index >= limit))
				{
					returnTop -= 3; /* nuke the loop indices & "leave" addr */
					ip++;  /* fall through the loop */
				}
				else 
				{                       /* update index, branch to loop head */
					returnTop->i = index;
					BRANCH();
				}

				break;
			}


			/*
			** 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.
			*/
			case ficlInstructionLeave:
			{
				/* almost unloop */
				returnTop -= 2;
				/* exit */
				EXIT_FUNCTION();
			}


			case ficlInstructionUnloop:
			{
				returnTop -= 3;
				break;
			}

			case ficlInstructionI:
			{
				*++dataTop = *returnTop;
				break;
			}


			case ficlInstructionJ:
			{
				*++dataTop = returnTop[-3];
				break;
			}


			case ficlInstructionK:
			{
				*++dataTop = returnTop[-6];
				break;
			}


			case ficlInstructionDoesParen:
			{
				ficlDictionary *dictionary = ficlVmGetDictionary(vm);
				dictionary->smudge->code = (ficlPrimitive)ficlInstructionDoDoes;
				dictionary->smudge->param[0].p = ip;
			    ip = (ficlInstruction *)((returnTop--)->p);
				break;
			}

			case ficlInstructionDoDoes:
			{
				ficlCell *cell;
				ficlIp tempIP;

				CHECK_STACK(0, 1);

				cell = fw->param;
				tempIP = (ficlIp)((*cell).p);
				(++dataTop)->p = (cell + 1);
				(++returnTop)->p = (void *)ip;
				ip = (ficlInstruction *)tempIP;
				break;
			}

#if FICL_WANT_FLOAT
			case ficlInstructionF2Fetch:
				CHECK_FLOAT_STACK(0, 2);
				CHECK_STACK(1, 0);
				FLOAT_PUSH_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);

			case ficlInstructionFFetch:
				CHECK_FLOAT_STACK(0, 1);
				CHECK_STACK(1, 0);
				FLOAT_PUSH_CELL_POINTER((ficlCell *)(dataTop--)->p);

			case ficlInstructionF2Store:
				CHECK_FLOAT_STACK(2, 0);
				CHECK_STACK(1, 0);
				FLOAT_POP_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);

			case ficlInstructionFStore:
				CHECK_FLOAT_STACK(1, 0);
				CHECK_STACK(1, 0);
				FLOAT_POP_CELL_POINTER((ficlCell *)(dataTop--)->p);
#endif /* FICL_WANT_FLOAT */

			/*
			** two-fetch CORE ( a-addr -- x1 x2 )
			**
			** Fetch the ficlCell pair x1 x2 stored at a-addr. x2 is stored at a-addr
			** and x1 at the next consecutive ficlCell. It is equivalent to the
			** sequence DUP ficlCell+ @ SWAP @ .
			*/
			case ficlInstruction2Fetch:
				CHECK_STACK(1, 2);
				PUSH_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);

			/*
			** fetch CORE ( a-addr -- x )
			**
			** x is the value stored at a-addr.
			*/
			case ficlInstructionFetch:
				CHECK_STACK(1, 1);
				PUSH_CELL_POINTER((ficlCell *)(dataTop--)->p);

			/*
			** two-store    CORE ( x1 x2 a-addr -- )
			** Store the ficlCell pair x1 x2 at a-addr, with x2 at a-addr and x1 at the
			** next consecutive ficlCell. It is equivalent to the sequence
			** SWAP OVER ! ficlCell+ ! . 
			*/
			case ficlInstruction2Store:
				CHECK_STACK(3, 0);
				POP_CELL_POINTER_DOUBLE((ficlCell *)(dataTop--)->p);

			/*
			** store        CORE ( x a-addr -- )
			** Store x at a-addr. 
			*/
			case ficlInstructionStore:
				CHECK_STACK(2, 0);
				POP_CELL_POINTER((ficlCell *)(dataTop--)->p);

			case ficlInstructionComma:
			{
				ficlDictionary *dictionary;
				CHECK_STACK(1, 0);

				dictionary = ficlVmGetDictionary(vm);
				ficlDictionaryAppendCell(dictionary, *dataTop--);
				break;
			}

			case ficlInstructionCComma:
			{
				ficlDictionary *dictionary;
				char c;
				CHECK_STACK(1, 0);

				dictionary = ficlVmGetDictionary(vm);
				c = (char)(dataTop--)->i;
				ficlDictionaryAppendCharacter(dictionary, c);
				break;
			}

			case ficlInstructionCells:
			{
				CHECK_STACK(1, 1);
				dataTop->i *= sizeof(ficlCell);
				break;
			}

			case ficlInstructionCellPlus:
			{
				CHECK_STACK(1, 1);
				dataTop->i += sizeof(ficlCell);
				break;
			}

			case ficlInstructionStar:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i *= i;
				break;
			}

			case ficlInstructionNegate:
			{
				CHECK_STACK(1, 1);
				dataTop->i = - dataTop->i;
				break;
			}

			case ficlInstructionSlash:
			{
				CHECK_STACK(2, 1);
				i = (dataTop--)->i;
				dataTop->i /= i;
				break;
			}

			/*
			** slash-mod        CORE ( n1 n2 -- n3 n4 )
			** Divide n1 by n2, giving the single-ficlCell remainder n3 and the single-ficlCell
			** 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)
			*/
			case ficlInstructionSlashMod:
			{
				ficl2Integer n1;
				ficlInteger n2;
				ficl2IntegerQR qr;

				CHECK_STACK(2, 2);
				n2    = dataTop[0].i;
				FICL_INTEGER_TO_2INTEGER(dataTop[-1].i, n1);

				qr = ficl2IntegerDivideSymmetric(n1, n2);
				dataTop[-1].i = qr.remainder;
				dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient);
				break;
			}


			case ficlInstruction2Star:
			{
				CHECK_STACK(1, 1);
				dataTop->i <<= 1;
				break;
			}

			case ficlInstruction2Slash:
			{
				CHECK_STACK(1, 1);
				dataTop->i >>= 1;
				break;
			}

			case ficlInstructionStarSlash:
			{
				ficlInteger x, y, z;
				ficl2Integer prod;
				CHECK_STACK(3, 1);

				z = (dataTop--)->i;
				y = (dataTop--)->i;
				x = dataTop->i;

				prod = ficl2IntegerMultiply(x,y);
				dataTop->i = FICL_2UNSIGNED_GET_LOW(ficl2IntegerDivideSymmetric(prod, z).quotient);
				break;
			}


			case ficlInstructionStarSlashMod:
			{
				ficlInteger x, y, z;
				ficl2Integer prod;
				ficl2IntegerQR qr;

				CHECK_STACK(3, 2);

				z = (dataTop--)->i;
				y = dataTop[0].i;
				x = dataTop[-1].i;

				prod = ficl2IntegerMultiply(x,y);
				qr   = ficl2IntegerDivideSymmetric(prod, z);

				dataTop[-1].i = qr.remainder;
				dataTop[0].i = FICL_2UNSIGNED_GET_LOW(qr.quotient);
				break;
			}


#if FICL_WANT_FLOAT

			case ficlInstructionF0:
			{
				CHECK_FLOAT_STACK(0, 1);
				(++floatTop)->f = 0.0f;
				break;
			}


			case ficlInstructionF1:
			{
				CHECK_FLOAT_STACK(0, 1);
				(++floatTop)->f = 1.0f;
				break;
			}


			case ficlInstructionFNeg1:
			{
				CHECK_FLOAT_STACK(0, 1);
				(++floatTop)->f = -1.0f;
				break;
			}


			/*******************************************************************
			** Floating point literal execution word.
			*******************************************************************/
			case ficlInstructionFLiteralParen:
			{
				CHECK_FLOAT_STACK(0, 1);

				/* Yes, I'm using ->i here, but it's really a float.  --lch */
				(++floatTop)->i = *ip++;
				break;
			}

			/*******************************************************************
			** Do float addition r1 + r2.
			** f+ ( r1 r2 -- r )
			*******************************************************************/
			case ficlInstructionFPlus:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(2, 1);

				f = (floatTop--)->f;
				floatTop->f += f;
				break;
			}

			/*******************************************************************
			** Do float subtraction r1 - r2.
			** f- ( r1 r2 -- r )
			*******************************************************************/
			case ficlInstructionFMinus:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(2, 1);

				f = (floatTop--)->f;
				floatTop->f -= f;
				break;
			}

			/*******************************************************************
			** Do float multiplication r1 * r2.
			** f* ( r1 r2 -- r )
			*******************************************************************/
			case ficlInstructionFStar:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(2, 1);

				f = (floatTop--)->f;
				floatTop->f *= f;
				break;
			}

			/*******************************************************************
			** Do float negation.
			** fnegate ( r -- r )
			*******************************************************************/
			case ficlInstructionFNegate:
			{
				CHECK_FLOAT_STACK(1, 1);

				floatTop->f = -(floatTop->f);
				break;
			}

			/*******************************************************************
			** Do float division r1 / r2.
			** f/ ( r1 r2 -- r )
			*******************************************************************/
			case ficlInstructionFSlash:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(2, 1);

				f = (floatTop--)->f;
				floatTop->f /= f;
				break;
			}

			/*******************************************************************
			** Do float + integer r + n.
			** f+i ( r n -- r )
			*******************************************************************/
			case ficlInstructionFPlusI:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(1, 1);
				CHECK_STACK(1, 0);

				f = (ficlFloat)(dataTop--)->f;
				floatTop->f += f;
				break;
			}

			/*******************************************************************
			** Do float - integer r - n.
			** f-i ( r n -- r )
			*******************************************************************/
			case ficlInstructionFMinusI:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(1, 1);
				CHECK_STACK(1, 0);

				f = (ficlFloat)(dataTop--)->f;
				floatTop->f -= f;
				break;
			}

			/*******************************************************************
			** Do float * integer r * n.
			** f*i ( r n -- r )
			*******************************************************************/
			case ficlInstructionFStarI:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(1, 1);
				CHECK_STACK(1, 0);

				f = (ficlFloat)(dataTop--)->f;
				floatTop->f *= f;
				break;
			}

			/*******************************************************************
			** Do float / integer r / n.
			** f/i ( r n -- r )
			*******************************************************************/
			case ficlInstructionFSlashI:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(1, 1);
				CHECK_STACK(1, 0);

				f = (ficlFloat)(dataTop--)->f;
				floatTop->f /= f;
				break;
			}

			/*******************************************************************
			** Do integer - float n - r.
			** i-f ( n r -- r )
			*******************************************************************/
			case ficlInstructionIMinusF:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(1, 1);
				CHECK_STACK(1, 0);

				f = (ficlFloat)(dataTop--)->f;
				floatTop->f = f - floatTop->f;
				break;
			}

			/*******************************************************************
			** Do integer / float n / r.
			** i/f ( n r -- r )
			*******************************************************************/
			case ficlInstructionISlashF:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(1,1);
				CHECK_STACK(1, 0);

				f = (ficlFloat)(dataTop--)->f;
				floatTop->f = f / floatTop->f;
				break;
			}

			/*******************************************************************
			** Do integer to float conversion.
			** int>float ( n -- r )
			*******************************************************************/
			case ficlInstructionIntToFloat:
			{
				CHECK_STACK(1, 0);
				CHECK_FLOAT_STACK(0, 1);

				(++floatTop)->f = (ficlFloat)((dataTop--)->i);
				break;
			}

			/*******************************************************************
			** Do float to integer conversion.
			** float>int ( r -- n )
			*******************************************************************/
			case ficlInstructionFloatToInt:
			{
				CHECK_STACK(0, 1);
				CHECK_FLOAT_STACK(1, 0);

				(++dataTop)->i = (ficlInteger)((floatTop--)->f);
				break;
			}

			/*******************************************************************
			** Add a floating point number to contents of a variable.
			** f+! ( r n -- )
			*******************************************************************/
			case ficlInstructionFPlusStore:
			{
				ficlCell *cell;

				CHECK_STACK(1, 0);
				CHECK_FLOAT_STACK(1, 0);

				cell = (ficlCell *)(dataTop--)->p;
				cell->f += (floatTop--)->f;
				break;
			}

			/*******************************************************************
			** Do float stack drop.
			** fdrop ( r -- )
			*******************************************************************/
			case ficlInstructionFDrop:
			{
				CHECK_FLOAT_STACK(1, 0);
				floatTop--;
				break;
			}

			/*******************************************************************
			** Do float stack ?dup.
			** f?dup ( r -- r )
			*******************************************************************/
			case ficlInstructionFQuestionDup:
			{
				CHECK_FLOAT_STACK(1, 2);

				if (floatTop->f != 0)
					goto FDUP;

				break;
			}

			/*******************************************************************
			** Do float stack dup.
			** fdup ( r -- r r )
			*******************************************************************/
			case ficlInstructionFDup:
			{
				CHECK_FLOAT_STACK(1, 2);

FDUP:
				floatTop[1] = floatTop[0];
				floatTop++;
				break;
			}

			/*******************************************************************
			** Do float stack swap.
			** fswap ( r1 r2 -- r2 r1 )
			*******************************************************************/
			case ficlInstructionFSwap:
			{
				CHECK_FLOAT_STACK(2, 2);

				c = floatTop[0];
				floatTop[0] = floatTop[-1];
				floatTop[-1] = c;
				break;
			}

			/*******************************************************************
			** Do float stack 2drop.
			** f2drop ( r r -- )
			*******************************************************************/
			case ficlInstructionF2Drop:
			{
				CHECK_FLOAT_STACK(2, 0);

				floatTop -= 2;
				break;
			}


			/*******************************************************************
			** Do float stack 2dup.
			** f2dup ( r1 r2 -- r1 r2 r1 r2 )
			*******************************************************************/
			case ficlInstructionF2Dup:
			{
				CHECK_FLOAT_STACK(2, 4);

				floatTop[1] = floatTop[-1];
				floatTop[2] = *floatTop;
				floatTop += 2;
				break;
			}

			/*******************************************************************
			** Do float stack over.
			** fover ( r1 r2 -- r1 r2 r1 )
			*******************************************************************/
			case ficlInstructionFOver:
			{
				CHECK_FLOAT_STACK(2, 3);

				floatTop[1] = floatTop[-1];
				floatTop++;
				break;
			}

			/*******************************************************************
			** Do float stack 2over.
			** f2over ( r1 r2 r3 -- r1 r2 r3 r1 r2 )
			*******************************************************************/
			case ficlInstructionF2Over:
			{
				CHECK_FLOAT_STACK(4, 6);

				floatTop[1] = floatTop[-2];
				floatTop[2] = floatTop[-1];
				floatTop += 2;
				break;
			}

			/*******************************************************************
			** Do float stack pick.
			** fpick ( n -- r )
			*******************************************************************/
			case ficlInstructionFPick:
			{
				CHECK_STACK(1, 0);
				c = *dataTop--;
				CHECK_FLOAT_STACK(c.i+1, c.i+2);

				floatTop[1] = floatTop[- c.i];
				break;
			}

			/*******************************************************************
			** Do float stack rot.
			** frot ( r1 r2 r3  -- r2 r3 r1 )
			*******************************************************************/
			case ficlInstructionFRot:
			{
				i = 2;
				goto FROLL;
			}

			/*******************************************************************
			** Do float stack roll.
			** froll ( n -- )
			*******************************************************************/
			case ficlInstructionFRoll:
			{
				CHECK_STACK(1, 0);
				i = (dataTop--)->i;

				if (i < 1)
					break;

FROLL:
				CHECK_FLOAT_STACK(i+1, i+2);
				c = floatTop[-i];
				memmove(floatTop - i, floatTop - (i - 1), i * sizeof(ficlCell));
				*floatTop = c;

				break;
			}

			/*******************************************************************
			** Do float stack -rot.
			** f-rot ( r1 r2 r3  -- r3 r1 r2 )
			*******************************************************************/
			case ficlInstructionFMinusRot:
			{
				i = 2;
				goto FMINUSROLL;
			}


			/*******************************************************************
			** Do float stack -roll.
			** f-roll ( n -- )
			*******************************************************************/
			case ficlInstructionFMinusRoll:
			{
				CHECK_STACK(1, 0);
				i = (dataTop--)->i;

				if (i < 1)
					break;

FMINUSROLL:
				CHECK_FLOAT_STACK(i+1, i+2);
				c = *floatTop;
				memmove(floatTop - (i - 1), floatTop - i, i * sizeof(ficlCell));
				floatTop[-i] = c;

				break;
			}

			/*******************************************************************
			** Do float stack 2swap
			** f2swap ( r1 r2 r3 r4  -- r3 r4 r1 r2 )
			*******************************************************************/
			case ficlInstructionF2Swap:
			{
				ficlCell c2;
				CHECK_FLOAT_STACK(4, 4);

				c = *floatTop;
				c2 = floatTop[-1];

				*floatTop = floatTop[-2];
				floatTop[-1] = floatTop[-3];

				floatTop[-2] = c;
				floatTop[-3] = c2;
				break;
			}

			/*******************************************************************
			** Do float 0= comparison r = 0.0.
			** f0= ( r -- T/F )
			*******************************************************************/
			case ficlInstructionF0Equals:
			{
				CHECK_FLOAT_STACK(1, 0);
				CHECK_STACK(0, 1);

				(++dataTop)->i = FICL_BOOL((floatTop--)->f != 0.0f);
				break;
			}

			/*******************************************************************
			** Do float 0< comparison r < 0.0.
			** f0< ( r -- T/F )
			*******************************************************************/
			case ficlInstructionF0Less:
			{
				CHECK_FLOAT_STACK(1, 0);
				CHECK_STACK(0, 1);

				(++dataTop)->i = FICL_BOOL((floatTop--)->f < 0.0f);
				break;
			}

			/*******************************************************************
			** Do float 0> comparison r > 0.0.
			** f0> ( r -- T/F )
			*******************************************************************/
			case ficlInstructionF0Greater:
			{
				CHECK_FLOAT_STACK(1, 0);
				CHECK_STACK(0, 1);

				(++dataTop)->i = FICL_BOOL((floatTop--)->f > 0.0f);
				break;
			}

			/*******************************************************************
			** Do float = comparison r1 = r2.
			** f= ( r1 r2 -- T/F )
			*******************************************************************/
			case ficlInstructionFEquals:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(2, 0);
				CHECK_STACK(0, 1);

				f = (floatTop--)->f;
				(++dataTop)->i = FICL_BOOL((floatTop--)->f == f);
				break;
			}

			/*******************************************************************
			** Do float < comparison r1 < r2.
			** f< ( r1 r2 -- T/F )
			*******************************************************************/
			case ficlInstructionFLess:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(2, 0);
				CHECK_STACK(0, 1);

				f = (floatTop--)->f;
				(++dataTop)->i = FICL_BOOL((floatTop--)->f < f);
				break;
			}

			/*******************************************************************
			** Do float > comparison r1 > r2.
			** f> ( r1 r2 -- T/F )
			*******************************************************************/
			case ficlInstructionFGreater:
			{
				ficlFloat f;
				CHECK_FLOAT_STACK(2, 0);
				CHECK_STACK(0, 1);

				f = (floatTop--)->f;
				(++dataTop)->i = FICL_BOOL((floatTop--)->f > f);
				break;
			}


			/*******************************************************************
			** Move float to param stack (assumes they both fit in a single ficlCell)
			** f>s 
			*******************************************************************/
			case ficlInstructionFFrom:
			{
				CHECK_FLOAT_STACK(1, 0);
				CHECK_STACK(0, 1);

				*++dataTop = *floatTop--;
				break;
			}

			case ficlInstructionToF:
			{
				CHECK_FLOAT_STACK(0, 1);
				CHECK_STACK(1, 0);

				*++floatTop = *dataTop--;
				break;
			}

#endif /* FICL_WANT_FLOAT */


			/**************************************************************************
									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.
			**
			**************************************************************************/
			case ficlInstructionColonParen:
			{
				(++returnTop)->p = (void *)ip;
				ip = (ficlInstruction *)(fw->param);
				break;
			}

			case ficlInstructionCreateParen:
			{
				CHECK_STACK(0, 1);
				(++dataTop)->p = (fw->param + 1);
				break;
			}

			case ficlInstructionVariableParen:
			{
				CHECK_STACK(0, 1);
				(++dataTop)->p = fw->param;
				break;
			}

			/**************************************************************************
									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 ficlCell.
			**
			**************************************************************************/


#if FICL_WANT_FLOAT
			case ficlInstructionF2ConstantParen:
				CHECK_FLOAT_STACK(0, 2);
				FLOAT_PUSH_CELL_POINTER_DOUBLE(fw->param);

			case ficlInstructionFConstantParen:
				CHECK_FLOAT_STACK(0, 1);
				FLOAT_PUSH_CELL_POINTER(fw->param);
#endif /* FICL_WANT_FLOAT */

			case ficlInstruction2ConstantParen:
				CHECK_STACK(0, 2);
				PUSH_CELL_POINTER_DOUBLE(fw->param);

			case ficlInstructionConstantParen:
				CHECK_STACK(0, 1);
				PUSH_CELL_POINTER(fw->param);


#if FICL_WANT_USER
			case ficlInstructionUserParen:
			{
				ficlInteger i = fw->param[0].i;
				(++dataTop)->p = &vm->user[i];
				break;
			}
#endif

			default:
			{
				/*
				** Clever hack, or evil coding?  You be the judge.
				**
				** If the word we've been asked to execute is in fact
				** an *instruction*, we grab the instruction, stow it
				** in "i" (our local cache of *ip), and *jump* to the
				** top of the switch statement.  --lch
				*/
				if ((ficlInstruction)fw->code < ficlInstructionLast)
				{
					instruction = (ficlInstruction)fw->code;
					goto AGAIN;
				}

				LOCAL_VARIABLE_SPILL;
				(vm)->runningWord = fw;
				fw->code(vm);
				LOCAL_VARIABLE_REFILL;
				break;
			}
		}
	}

	LOCAL_VARIABLE_SPILL;
	vm->exceptionHandler = oldExceptionHandler;
}


/**************************************************************************
                        v m G e t D i c t
** Returns the address dictionary for this VM's system
**************************************************************************/
ficlDictionary  *ficlVmGetDictionary(ficlVm *vm)
{
	FICL_VM_ASSERT(vm, vm);
	return vm->callback.system->dictionary;
}


/**************************************************************************
                        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_COUNTED_STRING_MAX characters to the supplied destination buffer, a
** ficlCountedString. The destination string is NULL terminated.
** 
** Returns the address of the first unused character in the dest buffer.
**************************************************************************/
char *ficlVmGetString(ficlVm *vm, ficlCountedString *counted, char delimiter)
{
    ficlString s = ficlVmParseStringEx(vm, delimiter, 0);

    if (FICL_STRING_GET_LENGTH(s) > FICL_COUNTED_STRING_MAX)
    {
        FICL_STRING_SET_LENGTH(s, FICL_COUNTED_STRING_MAX);
    }

    strncpy(counted->text, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s));
    counted->text[FICL_STRING_GET_LENGTH(s)] = '\0';
    counted->length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s);

    return counted->text + FICL_STRING_GET_LENGTH(s) + 1;
}


/**************************************************************************
                        v m G e t W o r d
** vmGetWord calls vmGetWord0 repeatedly until it gets a string with 
** non-zero length.
**************************************************************************/
ficlString ficlVmGetWord(ficlVm *vm)
{
    ficlString s = ficlVmGetWord0(vm);

    if (FICL_STRING_GET_LENGTH(s) == 0)
    {
        ficlVmThrow(vm, FICL_VM_STATUS_RESTART);
    }

    return s;
}


/**************************************************************************
                        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.
**************************************************************************/
ficlString ficlVmGetWord0(ficlVm *vm)
{
    char *trace      = ficlVmGetInBuf(vm);
    char *stop      = ficlVmGetInBufEnd(vm);
    ficlString s;
    ficlUnsigned length = 0;
    char c = 0;

    trace = ficlStringSkipSpace(trace, stop);
    FICL_STRING_SET_POINTER(s, trace);


	/* Please leave this loop this way; it makes Purify happier.  --lch */
    for (;;)
    {
		if (trace == stop)
			break;
		c = *trace;
		if (isspace((unsigned char)c))
			break;
        length++;
		trace++;
    }

    FICL_STRING_SET_LENGTH(s, length);

    if ((trace != stop) && isspace((unsigned char)c))    /* skip one trailing delimiter */
        trace++;

    ficlVmUpdateTib(vm, trace);

    return s;
}


/**************************************************************************
                        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 ficlVmGetWordToPad(ficlVm *vm)
{
    ficlString s;
    char *pad = (char *)vm->pad;
    s = ficlVmGetWord(vm);

    if (FICL_STRING_GET_LENGTH(s) > FICL_PAD_SIZE)
        FICL_STRING_SET_LENGTH(s, FICL_PAD_SIZE);

    strncpy(pad, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s));
    pad[FICL_STRING_GET_LENGTH(s)] = '\0';
    return (int)(FICL_STRING_GET_LENGTH(s));
}


/**************************************************************************
                        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.
**************************************************************************/
ficlString ficlVmParseString(ficlVm *vm, char delimiter)
{ 
    return ficlVmParseStringEx(vm, delimiter, 1);
}

ficlString ficlVmParseStringEx(ficlVm *vm, char delimiter, char skipLeadingDelimiters)
{
    ficlString s;
    char *trace      = ficlVmGetInBuf(vm);
    char *stop      = ficlVmGetInBufEnd(vm);
    char c;

    if (skipLeadingDelimiters)
    {
        while ((trace != stop) && (*trace == delimiter))
            trace++;
    }

    FICL_STRING_SET_POINTER(s, trace);    /* mark start of text */

    for (c = *trace;
		(trace != stop) && (c != delimiter)
			&& (c != '\r') && (c != '\n');
		c = *++trace)
    {
        ;                   /* find next delimiter or end of line */
    }

                            /* set length of result */
    FICL_STRING_SET_LENGTH(s, trace - FICL_STRING_GET_POINTER(s));

    if ((trace != stop) && (*trace == delimiter))     /* gobble trailing delimiter */
        trace++;

    ficlVmUpdateTib(vm, trace);
    return s;
}


/**************************************************************************
                        v m P o p
** 
**************************************************************************/
ficlCell ficlVmPop(ficlVm *vm)
{
    return ficlStackPop(vm->dataStack);
}


/**************************************************************************
                        v m P u s h
** 
**************************************************************************/
void ficlVmPush(ficlVm *vm, ficlCell c)
{
    ficlStackPush(vm->dataStack, c);
    return;
}


/**************************************************************************
                        v m P o p I P
** 
**************************************************************************/
void ficlVmPopIP(ficlVm *vm)
{
    vm->ip = (ficlIp)(ficlStackPopPointer(vm->returnStack));
    return;
}


/**************************************************************************
                        v m P u s h I P
** 
**************************************************************************/
void ficlVmPushIP(ficlVm *vm, ficlIp newIP)
{
    ficlStackPushPointer(vm->returnStack, (void *)vm->ip);
    vm->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 ficlVmPushTib(ficlVm *vm, char *text, ficlInteger nChars, ficlTIB *pSaveTib)
{
    if (pSaveTib)
    {
        *pSaveTib = vm->tib;
    }

    vm->tib.text = text;
    vm->tib.end = text + nChars;
    vm->tib.index = 0;
}


void ficlVmPopTib(ficlVm *vm, ficlTIB *pTib)
{
    if (pTib)
    {
        vm->tib = *pTib;
    }
    return;
}


/**************************************************************************
                        v m Q u i t
** 
**************************************************************************/
void ficlVmQuit(ficlVm *vm)
{
    ficlStackReset(vm->returnStack);
    vm->restart    = 0;
    vm->ip          = NULL;
    vm->runningWord = NULL;
    vm->state       = FICL_VM_STATE_INTERPRET;
    vm->tib.text    = NULL;
    vm->tib.end     = NULL;
    vm->tib.index   = 0;
    vm->pad[0]      = '\0';
    vm->sourceId.i  = 0;
    return;
}


/**************************************************************************
                        v m R e s e t 
** 
**************************************************************************/
void ficlVmReset(ficlVm *vm)
{
    ficlVmQuit(vm);
    ficlStackReset(vm->dataStack);
#if FICL_WANT_FLOAT
    ficlStackReset(vm->floatStack);
#endif
    vm->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 ficlVmSetTextOut(ficlVm *vm, ficlOutputFunction textOut)
{
    vm->callback.textOut = textOut;
    return;
}


void ficlVmTextOut(ficlVm *vm, char *text)
	{
	ficlCallbackTextOut((ficlCallback *)vm, text);
	}


void ficlVmErrorOut(ficlVm *vm, char *text)
	{
	ficlCallbackErrorOut((ficlCallback *)vm, text);
	}


	/**************************************************************************
                        v m T h r o w
** 
**************************************************************************/
void ficlVmThrow(ficlVm *vm, int except)
{
    if (vm->exceptionHandler)
        longjmp(*(vm->exceptionHandler), except);
}


void ficlVmThrowError(ficlVm *vm, char *fmt, ...)
{
    va_list list;

    va_start(list, fmt);
    vsprintf(vm->pad, fmt, list);
    va_end(list);
	strcat(vm->pad, "\n");

    longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT);
}


void ficlVmThrowErrorVararg(ficlVm *vm, char *fmt, va_list list)
{
    vsprintf(vm->pad, fmt, list);
	/* well, we can try anyway, we're certainly not returning to our caller! */
    va_end(list);
	strcat(vm->pad, "\n");
	
    longjmp(*(vm->exceptionHandler), FICL_VM_STATUS_ERROR_EXIT);
}


/**************************************************************************
                    f i c l E v a l u a t e
** Wrapper for ficlExec() which sets SOURCE-ID to -1.
**************************************************************************/
int ficlVmEvaluate(ficlVm *vm, char *s)
{
    int returnValue;
    ficlCell id = vm->sourceId;
	ficlString string;
    vm->sourceId.i = -1;
	FICL_STRING_SET_FROM_CSTRING(string, s);
    returnValue = ficlVmExecuteString(vm, string);
    vm->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 interpreter 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 ficlVmExecuteString(ficlVm *vm, ficlString s)
{
    ficlSystem *system = vm->callback.system;
    ficlDictionary   *dictionary   = system->dictionary;

    int        except;
    jmp_buf    vmState;
    jmp_buf   *oldState;
    ficlTIB        saveficlTIB;

    FICL_VM_ASSERT(vm, vm);
    FICL_VM_ASSERT(vm, system->interpreterLoop[0]);

    ficlVmPushTib(vm, FICL_STRING_GET_POINTER(s), FICL_STRING_GET_LENGTH(s), &saveficlTIB);

    /*
    ** Save and restore VM's jmp_buf to enable nested calls to ficlExec 
    */
    oldState = vm->exceptionHandler;
    vm->exceptionHandler = &vmState; /* This has to come before the setjmp! */
    except = setjmp(vmState);

    switch (except)
    {
    case 0:
        if (vm->restart)
        {
            vm->runningWord->code(vm);
            vm->restart = 0;
        }
        else
        {   /* set VM up to interpret text */
            ficlVmPushIP(vm, (ficlWord**)&(system->interpreterLoop[0]));
        }

        ficlVmInnerLoop(vm, 0);
        break;

    case FICL_VM_STATUS_RESTART:
        vm->restart = 1;
        except = FICL_VM_STATUS_OUT_OF_TEXT;
        break;

    case FICL_VM_STATUS_OUT_OF_TEXT:
        ficlVmPopIP(vm);
        if ((vm->state != FICL_VM_STATE_COMPILE) && (vm->sourceId.i == 0))
            ficlVmTextOut(vm, FICL_PROMPT);
        break;

    case FICL_VM_STATUS_USER_EXIT:
    case FICL_VM_STATUS_INNER_EXIT:
    case FICL_VM_STATUS_BREAK:
        break;

    case FICL_VM_STATUS_QUIT:
        if (vm->state == FICL_VM_STATE_COMPILE)
        {
            ficlDictionaryAbortDefinition(dictionary);
#if FICL_WANT_LOCALS
            ficlDictionaryEmpty(system->locals, system->locals->forthWordlist->size);
#endif
        }
        ficlVmQuit(vm);
        break;

    default: /* unhandled exception */
    case FICL_VM_STATUS_ERROR_EXIT:
	ficlVmErrorOut(vm, vm->pad); /* print saved message */
    case FICL_VM_STATUS_ABORT:
    case FICL_VM_STATUS_ABORTQ:
        if (vm->state == FICL_VM_STATE_COMPILE)
        {
            ficlDictionaryAbortDefinition(dictionary);
#if FICL_WANT_LOCALS
            ficlDictionaryEmpty(system->locals, system->locals->forthWordlist->size);
#endif
        }
        ficlDictionaryResetSearchOrder(dictionary);
        ficlVmReset(vm);
        break;
   }

    vm->exceptionHandler    = oldState;
    ficlVmPopTib(vm, &saveficlTIB);
    return (except);
}


/**************************************************************************
                        f i c l E x e c X T
** Given a pointer to a ficlWord, 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 ficlVmExecuteXT(ficlVm *vm, ficlWord *pWord)
{
    int        except;
    jmp_buf    vmState;
    jmp_buf   *oldState;
    ficlWord *oldRunningWord;

    FICL_VM_ASSERT(vm, vm);
    FICL_VM_ASSERT(vm, vm->callback.system->exitInnerWord);
    
    /* 
    ** Save the runningword so that RESTART behaves correctly
    ** over nested calls.
    */
    oldRunningWord = vm->runningWord;
    /*
    ** Save and restore VM's jmp_buf to enable nested calls
    */
    oldState = vm->exceptionHandler;
    vm->exceptionHandler = &vmState; /* This has to come before the setjmp! */
    except = setjmp(vmState);

    if (except)
        ficlVmPopIP(vm);
    else
        ficlVmPushIP(vm, &(vm->callback.system->exitInnerWord));

    switch (except)
    {
    case 0:
        ficlVmExecuteWord(vm, pWord);
        ficlVmInnerLoop(vm, 0);
        break;

    case FICL_VM_STATUS_INNER_EXIT:
    case FICL_VM_STATUS_BREAK:
        break;

    case FICL_VM_STATUS_RESTART:
    case FICL_VM_STATUS_OUT_OF_TEXT:
    case FICL_VM_STATUS_USER_EXIT:
    case FICL_VM_STATUS_QUIT:
    case FICL_VM_STATUS_ERROR_EXIT:
    case FICL_VM_STATUS_ABORT:
    case FICL_VM_STATUS_ABORTQ:
    default:    /* user defined exit code?? */
        if (oldState)
        {
            vm->exceptionHandler = oldState;
            ficlVmThrow(vm, except);
        }
        break;
    }

    vm->exceptionHandler    = oldState;
    vm->runningWord = oldRunningWord;
    return (except);
}


/**************************************************************************
                        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 FICL_TRUE. Otherwise, returns FICL_FALSE.
** (jws 8/01) Trailing decimal point causes a zero ficlCell to be pushed. (See
** the standard for DOUBLE wordset.
**************************************************************************/

int ficlVmParseNumber(ficlVm *vm, ficlString s)
{
    ficlInteger accumulator  = 0;
    char isNegative      = 0;
	char isDouble      = 0;
    unsigned base   = vm->base;
    char *trace        = FICL_STRING_GET_POINTER(s);
    ficlUnsigned8 length = (ficlUnsigned8)FICL_STRING_GET_LENGTH(s);
    unsigned c;
    unsigned digit;

    if (length > 1)
    {
        switch (*trace)
        {
        case '-':
            trace++;
            length--;
            isNegative = 1;
            break;
        case '+':
            trace++;
            length--;
            isNegative = 0;
            break;
        default:
            break;
        }
    }

    if ((length > 0) && (trace[length - 1] == '.')) /* detect & remove trailing decimal */
    {
        isDouble = 1;
        length--;
    }

    if (length == 0)        /* detect "+", "-", ".", "+." etc */
        return 0; /* false */

    while ((length--) && ((c = *trace++) != '\0'))
    {
        if (!isalnum(c))
            return 0; /* false */

        digit = c - '0';

        if (digit > 9)
            digit = tolower(c) - 'a' + 10;

        if (digit >= base)
            return 0; /* false */

        accumulator = accumulator * base + digit;
    }

	if (isDouble)		/* simple (required) DOUBLE support */
		ficlStackPushInteger(vm->dataStack, 0);

    if (isNegative)
        accumulator = -accumulator;

    ficlStackPushInteger(vm->dataStack, accumulator);
    if (vm->state == FICL_VM_STATE_COMPILE)
        ficlPrimitiveLiteralIm(vm);

    return 1; /* true */
}





/**************************************************************************
                        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 ficlCells) proposed to allot
**        -n number of ADDRESS UNITS proposed to de-allot
**         0 just do a consistency check
**************************************************************************/
void ficlVmDictionarySimpleCheck(ficlVm *vm, ficlDictionary *dictionary, int cells)
#if FICL_ROBUST >= 1
{
    if ((cells >= 0) && (ficlDictionaryCellsAvailable(dictionary) * (int)sizeof(ficlCell) < cells))
    {
        ficlVmThrowError(vm, "Error: dictionary full");
    }

    if ((cells <= 0) && (ficlDictionaryCellsUsed(dictionary) * (int)sizeof(ficlCell) < -cells))
    {
        ficlVmThrowError(vm, "Error: dictionary underflow");
    }

    return;
}
#else /* FICL_ROBUST >= 1 */
{
	FICL_IGNORE(vm);
	FICL_IGNORE(dictionary);
	FICL_IGNORE(cells);
}
#endif /* FICL_ROBUST >= 1 */


void ficlVmDictionaryCheck(ficlVm *vm, ficlDictionary *dictionary, int cells)
#if FICL_ROBUST >= 1
{
    ficlVmDictionarySimpleCheck(vm, dictionary, cells);

    if (dictionary->wordlistCount > FICL_MAX_WORDLISTS)
    {
        ficlDictionaryResetSearchOrder(dictionary);
        ficlVmThrowError(vm, "Error: search order overflow");
    }
    else if (dictionary->wordlistCount < 0)
    {
        ficlDictionaryResetSearchOrder(dictionary);
        ficlVmThrowError(vm, "Error: search order underflow");
    }

    return;
}
#else /* FICL_ROBUST >= 1 */
{
	FICL_IGNORE(vm);
	FICL_IGNORE(dictionary);
	FICL_IGNORE(cells);
}
#endif /* FICL_ROBUST >= 1 */



void ficlVmDictionaryAllot(ficlVm *vm, ficlDictionary *dictionary, int n)
{
	FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, n);
	FICL_IGNORE(vm);
	ficlDictionaryAllot(dictionary, n);
}


void ficlVmDictionaryAllotCells(ficlVm *vm, ficlDictionary *dictionary, int cells)
{
	FICL_VM_DICTIONARY_SIMPLE_CHECK(vm, dictionary, cells);
	FICL_IGNORE(vm);
	ficlDictionaryAllotCells(dictionary, cells);
}


/**************************************************************************
                        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, FICL_VM_STATE_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 ficlParseStep
**************************************************************************/
int ficlVmParseWord(ficlVm *vm, ficlString name)
{
    ficlDictionary *dictionary = ficlVmGetDictionary(vm);
    ficlWord *tempFW;

    FICL_VM_DICTIONARY_CHECK(vm, dictionary, 0);
    FICL_STACK_CHECK(vm->dataStack, 0, 0);

#if FICL_WANT_LOCALS
    if (vm->callback.system->localsCount > 0)
    {
        tempFW = ficlSystemLookupLocal(vm->callback.system, name);
    }
    else
#endif
    tempFW = ficlDictionaryLookup(dictionary, name);

    if (vm->state == FICL_VM_STATE_INTERPRET)
    {
        if (tempFW != NULL)
        {
            if (ficlWordIsCompileOnly(tempFW))
            {
                ficlVmThrowError(vm, "Error: FICL_VM_STATE_COMPILE only!");
            }

            ficlVmExecuteWord(vm, tempFW);
            return 1; /* true */
        }
    }

    else /* (vm->state == FICL_VM_STATE_COMPILE) */
    {
        if (tempFW != NULL)
        {
            if (ficlWordIsImmediate(tempFW))
            {
                ficlVmExecuteWord(vm, tempFW);
            }
            else
            {
				if (tempFW->flags & FICL_WORD_INSTRUCTION)
	                ficlDictionaryAppendUnsigned(dictionary, (ficlInteger)tempFW->code);
				else
	                ficlDictionaryAppendCell(dictionary, FICL_LVALUE_TO_CELL(tempFW));
            }
            return 1; /* true */
        }
    }

    return 0; /* false */
}