/
Sapphire/bin/
Sapphire/db/
Sapphire/db/OLC_rooms/
Sapphire/db/abi/
Sapphire/db/em_src/
Sapphire/db/helps/
Sapphire/db/helps/emman/ifunc/
Sapphire/db/npcs/Tatt/
Sapphire/db/objects/Tatt/
Sapphire/db/q_data/
Sapphire/db/rooms/Tatt/
Sapphire/doc/
Sapphire/doc/em/
Sapphire/etc/
Sapphire/src/abic/
Sapphire/src/areacon/
Sapphire/src/client/
Sapphire/src/embc/
Sapphire/src/emi/
Sapphire/src/emi/test/
Sapphire/src/include/
Sapphire/src/sapphire/em/
Sapphire/src/tcon/
/*
 * Copyright (C) 1995-1997 Christopher D. Granz
 *
 * This header may not be removed.
 *
 * Refer to the file "License" included in this package for further
 * information and before using any of the following.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "emi.h"


/*
 * Prototypes
 */
static bool      compare_instr                     ( register byte ** );


/*
 * Globals
 */
EM_VALUE *                                                       pStack;
EM_VALUE *                                                    pStackEnd;
EM_VALUE *                                              pInterpStackPos;
EM_VALUE *                                               pVarStackBegin;
EM_VALUE *                                                 pVarStackPos;
#if 0
EM_VALUE *                                                  pStaticVars;
EM_VALUE *                                               pStaticVarsEnd;
EM_VALUE *                                               pStaticVarsPos;
#endif

/*
 * Functions
 */
void stack_init( void )
{
    if ( iInterpStackSize > 0 || iVarStackSize > 0 )
    {
        pStack          = alloc_mem( sizeof( EM_VALUE )
                            * ( iInterpStackSize + iVarStackSize ) );
        pStackEnd       = ( pStack + iInterpStackSize
                          + iVarStackSize - 1 );
        pInterpStackPos = pStack;
        pVarStackBegin  = ( pStack + iInterpStackSize );
        pVarStackPos    = pVarStackBegin;
    }
}


void stack_free( void )
{
    interp_stack_pop( ( pInterpStackPos - pStack ) );
    var_stack_pop( ( pVarStackPos - pVarStackBegin ) );

    if ( pStack != NULL )
    {
        free_mem( (void **) &pStack );
        pStackEnd       = NULL;
        pInterpStackPos = NULL;
        pVarStackBegin  = NULL;
        pVarStackPos    = NULL;
    }
}

#ifdef DEBUG
static char *_get_valtype( int i )
{
    switch ( i )
    {
      case TYPE_INT   : return ( "integer" );
      case TYPE_FLOAT : return ( "floating point" );
      case TYPE_STRING: return ( "string" );
      case TYPE_OBJECT: return ( "object" );
      case TYPE_ARRAY : return ( "array" );
      default         : break;
    }

    return ( "unknown" );
}
#endif

/*
 * Push a new variable onto the variable stack.
 */
void var_stack_push( register EM_VALUE *p )
{
    if ( var_stack_overflow( ) == TRUE )
    {
        strcpy( cErrorBuf, "Interpreter: Variable stack overflow." );
        /* print_error( ); */
        em_abort( );
    }

    *pVarStackPos++ = *p;
}


/*
 * Pop (delete) a number of variables off the variable stack.
 */
void var_stack_pop( register short int rsiNumber )
{
    while ( rsiNumber-- > 0 )
        delete_value( --pVarStackPos );
}


/*
 * Reset the variable stack.
 */
void var_stack_reset( void )
{
    while ( pVarStackPos >= pVarStackBegin )
        delete_value( pVarStackPos-- );

    pVarStackPos = pVarStackBegin;
}


/*
 * Check for overflow in the variable stack.
 */
bool var_stack_overflow( void )
{
    if ( pVarStackPos > pStackEnd )
        return ( TRUE );
    else
        return ( FALSE );
}


/*
 * Push a new value onto the interpreter stack.
 */
void interp_stack_push( register EM_VALUE *p )
{
    if ( interp_stack_overflow( ) == TRUE )
    {
        strcpy( cErrorBuf, "Interpreter: Interpreter stack overflow." );
        /* print_error( ); */
        em_abort( );
    }

    *pInterpStackPos++ = *p;
}


/*
 * Pop a number of values off the interpreter stack.
 */
void interp_stack_pop( register short int rsiNumber )
{
    while ( rsiNumber-- > 0 )
        delete_value( --pInterpStackPos );
}


/*
 * Check for overflow in the interpreter stack.
 */
bool interp_stack_overflow( void )
{
    if ( pInterpStackPos > pVarStackBegin )
        return ( TRUE );
    else
        return ( FALSE );
}


void init_value( register EM_VALUE *p )
{
    switch ( p->iType )
    {
      case TYPE_STRING  :
          p->u.pString              = alloc_mem( sizeof( EM_STRING ) );
          p->u.pString->pString     = EMPTY_STRING;
          break;

      case TYPE_OBJECT  :
          p->u.pObject              = alloc_mem( sizeof( EM_OBJECT ) );
          p->u.pObject->pRealObject = NULL;
          break;

      default           : break;
    }
}


void delete_value( register EM_VALUE *p )
{
    switch ( p->iType )
    {
      case TYPE_STRING  :
          str_free( p->u.pString->pString );
          free_mem( (void **) &p->u.pString );
          break;

      case TYPE_ARRAY   : array_free( p->u.pArray );           break;
      case TYPE_OBJECT  : free_mem( (void **) &p->u.pObject ); break;
      default           : break;
    }
}


/*
 * Macros for getting values (constants) off an instruction byte stream.
 */
#define GET_INT1( p )                              ( *( (byte *) (p) ) )
#define GET_INT2( p )                                ( *( (i2 *) (p) ) )
#define GET_INT4( p )                                ( *( (i4 *) (p) ) )
#define GET_FLOAT4( p )                              ( *( (f4 *) (p) ) )
#define GET_FLOAT8( p )                              ( *( (f8 *) (p) ) )

/*
 * Macros for moving instruction byte stream pointers.
 */
#define MOVEP_1( p )                                           ( (p)++ )
#define MOVEP_2( p )                             ( (p) += sizeof( i2 ) )
#define MOVEP_4( p )                             ( (p) += sizeof( i4 ) )
#define MOVEP_8( p )                             ( (p) += sizeof( f8 ) )


/*
 * Main interpreter function; executes one instruction byte code.
 */
#define p2                                       ( pInterpStackPos - 1 )

byte *interpret_instruction( byte *pInstrStream )
{
    EM_VALUE v         = { 0, { NULL } };
    register EM_VALUE *p;
    /* register EM_ARRAY *pArray; */
    register long rl    = 0L;
    register long rl2   = 0L;

    switch ( *pInstrStream++ )
    {
      case INSTR_END                : break;
      case INSTR_NEW_INT            :
          v.iType      = TYPE_INT;
          var_stack_push( &v );
          break;

      case INSTR_NEW_FLOAT          :
          v.iType      = TYPE_FLOAT;
          var_stack_push( &v );
          break;

      case INSTR_NEW_STRING         :
          v.iType                  = TYPE_STRING;
          init_value( &v );
          var_stack_push( &v );
          break;

      case INSTR_NEW_OBJECT         :
          v.iType      = TYPE_OBJECT;
          init_value( &v );
          var_stack_push( &v );
          break;

      case INSTR_NEW_ARRAY          :
          v.iType      = TYPE_ARRAY;
          v.u.pArray   = NULL;
          var_stack_push( &v );
          break;

      case INSTR_PUSH_ZERO          :
          v.iType      = TYPE_INT;
          v.u.lInt     = 0L;
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_ONE           :
          v.iType      = TYPE_INT;
          v.u.lInt     = 1L;
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_INT1          :
          v.iType      = TYPE_INT;
          v.u.lInt     = GET_INT1( pInstrStream );
          MOVEP_1( pInstrStream );
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_INT2          :
          v.iType      = TYPE_INT;
          v.u.lInt     = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_INT4          :
          v.iType      = TYPE_INT;
          v.u.lInt     = GET_INT4( pInstrStream );
          MOVEP_4( pInstrStream );
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_FLOAT4        :
          v.iType      = TYPE_FLOAT;
          v.u.dFloat   = GET_FLOAT4( pInstrStream );
          MOVEP_4( pInstrStream );
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_FLOAT8        :
          v.iType      = TYPE_FLOAT;
          v.u.dFloat   = GET_FLOAT8( pInstrStream );
          MOVEP_8( pInstrStream );
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_STRING        :
          /* First we need to get the string length. */
          rl                       = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );
          v.iType                  = TYPE_STRING;
          init_value( &v );
          v.u.pString->sLength     = rl;
          v.u.pString->pString     = alloc_mem( rl + 1 );

          for ( rl2 = 0L; rl2 < rl; rl2++, MOVEP_1( pInstrStream ) )
              (v.u.pString->pString)[rl2] = GET_INT1( pInstrStream );

          (v.u.pString->pString)[rl2]     = '\0';
          interp_stack_push( &v );
          break;

      case INSTR_PUSH_LOCAL         :
          rl                   = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );

          switch ( ( p = ( pVarStackBegin + rl ) )->iType )
          {
            case TYPE_INT    :
                v.iType        = TYPE_INT;
                v.u.lInt       = p->u.lInt;
                break;

            case TYPE_FLOAT  :
                v.iType        = TYPE_FLOAT;
                v.u.dFloat     = p->u.dFloat;
                break;

            case TYPE_STRING :
                v.iType              = TYPE_STRING;
                init_value( &v );
                v.u.pString->sLength = p->u.pString->sLength;
                v.u.pString->pString = str_dup( p->u.pString->pString );
                break;

            case TYPE_OBJECT :
                v.iType        = TYPE_OBJECT;
                init_value( &v );
                *v.u.pObject   = *p->u.pObject;
                break;

            case TYPE_ARRAY  :
                v.iType        = TYPE_ARRAY;
                v.u.pArray     = array_copy( p->u.pArray );
                break;
          }

          interp_stack_push( &v );
          break;

      case INSTR_PUSH_INDEX         :
          p = ( pInterpStackPos - 2 );

          if ( p->u.pArray == NULL )
          {
              strcpy( cErrorBuf, "Interpreter: "
                "Attemp to access non-existent array." );
              /* print_error( ); */
              em_abort( );
          }

          if ( ( rl = p2->u.lInt ) < 0 || rl >= p->u.pArray->sSize )
          {
              strcpy( cErrorBuf, "Interpreter: "
                "Attemp to access non-existent array element." );
              /* print_error( ); */
              em_abort( );
          }

          switch ( ( p->u.pArray->pElements + rl )->iType )
          {
            case TYPE_INT    :
                v.iType    = TYPE_INT;
                v.u.lInt   = ( p->u.pArray->pElements + rl )->u.lInt;
                break;

            case TYPE_FLOAT  :
                v.iType    = TYPE_FLOAT;
                v.u.dFloat = ( p->u.pArray->pElements + rl )->u.dFloat;
                break;

            case TYPE_STRING :
                v.iType              = TYPE_STRING;
                init_value( &v );
                v.u.pString->sLength = ( p->u.pArray->pElements
                                       + rl )->u.pString->sLength;
                v.u.pString->pString = str_dup( ( p->u.pArray->pElements
                                         + rl )->u.pString->pString );
                break;

            case TYPE_OBJECT :
                v.iType      = TYPE_OBJECT;
                init_value( &v );
                *v.u.pObject = *( p->u.pArray->pElements
                               + rl )->u.pObject;
                break;

            case TYPE_ARRAY  :
                v.iType        = TYPE_ARRAY;
                v.u.pArray     = array_copy( ( p->u.pArray->pElements
                                   + rl )->u.pArray );
                break;
          }

          interp_stack_pop( 2 );
          interp_stack_push( &v );
          break;

      case INSTR_POP                : interp_stack_pop( 1 ); break;
      case INSTR_ASSIGN_LOCAL       :
          rl                   = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );

          switch ( ( p = ( pVarStackBegin + rl ) )->iType )
          {
            case TYPE_INT    :
                p->u.lInt      = p2->u.lInt;
                break;

            case TYPE_FLOAT  :
                p->u.dFloat    = p2->u.dFloat;
                break;

            case TYPE_STRING :
                str_free( p->u.pString->pString );
                p->u.pString->sLength = p2->u.pString->sLength;
                p->u.pString->pString = str_dup( p2->u.pString->pString );
                break;

            case TYPE_OBJECT :
                *p->u.pObject  = *p2->u.pObject;
                break;

            case TYPE_ARRAY  :
                delete_value( p );
                p->u.pArray    = array_copy( p2->u.pArray );
                break;
          }

          interp_stack_pop( 1 );
          break;

      case INSTR_ASSIGN_INDEX       :
          p = ( pInterpStackPos - 3 );

          if ( p->u.pArray == NULL )
          {
              strcpy( cErrorBuf, "Interpreter: "
                "Attemp to access non-existent array." );
              /* print_error( ); */
              em_abort( );
          }

          if ( ( rl = ( pInterpStackPos - 2 )->u.lInt ) < 0
            || rl >= p->u.pArray->sSize )
          {
              strcpy( cErrorBuf, "Interpreter: "
                "Attemp to access non-existent array element." );
              /* print_error( ); */
              em_abort( );
          }

          switch ( ( p->u.pArray->pElements + rl )->iType )
          {
            case TYPE_INT    :
                ( p->u.pArray->pElements + rl )->u.lInt   = p2->u.lInt;
                break;

            case TYPE_FLOAT  :
                ( p->u.pArray->pElements + rl )->u.dFloat = p2->u.dFloat;
                break;

            case TYPE_STRING :
                str_free( ( p->u.pArray->pElements
                  + rl )->u.pString->pString );
                ( p->u.pArray->pElements + rl )->u.pString->sLength
                  = p2->u.pString->sLength;
                ( p->u.pArray->pElements + rl )->u.pString->pString
                  = str_dup( p2->u.pString->pString );
                break;

            case TYPE_OBJECT :
                *( p->u.pArray->pElements + rl )->u.pObject
                  = *p2->u.pObject;
                break;

            case TYPE_ARRAY  :
                delete_value( ( p->u.pArray->pElements + rl ) );
                ( p->u.pArray->pElements + rl )->u.pArray
                  = array_copy( p2->u.pArray );
                break;
          }

          interp_stack_pop( 2 );
          break;

      case INSTR_MULTIPLY           :
          switch ( ( p = ( pInterpStackPos - 2 ) )->iType )
          {
            case TYPE_INT    :
                p->u.lInt   *= p2->u.lInt;
                break;

            case TYPE_FLOAT  :
                p->u.dFloat *= p2->u.dFloat;
                break;
          }

          interp_stack_pop( 1 );
          break;

      case INSTR_DIVIDE             :
          switch ( ( p = ( pInterpStackPos - 2 ) )->iType )
          {
            case TYPE_INT    :
                p->u.lInt   /= p2->u.lInt;
                break;

            case TYPE_FLOAT  :
                p->u.dFloat /= p2->u.dFloat;
                break;
          }

          interp_stack_pop( 1 );
          break;

      case INSTR_MODULUS            :
          ( pInterpStackPos - 2 )->u.lInt %= p2->u.lInt;
          interp_stack_pop( 1 );
          break;

      case INSTR_ADD                :
          switch ( ( p = ( pInterpStackPos - 2 ) )->iType )
          {
            case TYPE_INT    :
                p->u.lInt    += p2->u.lInt;
                break;

            case TYPE_FLOAT  :
                p->u.dFloat  += p2->u.dFloat;
                break;

            case TYPE_STRING :
                {
                    char *pStr  = p->u.pString->pString;
                    char *pStr2 = p2->u.pString->pString;

                    if ( pStr == EMPTY_STRING )
                    {
                        p->u.pString->sLength = p2->u.pString->sLength;
                        p->u.pString->pString = str_dup( pStr2 );
                    }
                    else
                    {
                        p->u.pString->pString = realloc_mem( pStr,
                                                  strlen( pStr )
                                                  + strlen( pStr2 ) + 1 );
                        strcat( pStr, pStr2 );
                        p->u.pString->sLength = strlen( pStr );
                    }
                }

                break;
          }

          interp_stack_pop( 1 );
          break;

      case INSTR_SUBTRACT           :
          switch ( ( p = ( pInterpStackPos - 2 ) )->iType )
          {
            case TYPE_INT    :
                p->u.lInt   -= p2->u.lInt;
                break;

            case TYPE_FLOAT  :
                p->u.dFloat -= p2->u.dFloat;
                break;
          }

          interp_stack_pop( 1 );
          break;

      case INSTR_LSHIFT             :
          ( pInterpStackPos - 2 )->u.lInt <<= p2->u.lInt;
          interp_stack_pop( 1 );
          break;

      case INSTR_RSHIFT             :
          ( pInterpStackPos - 2 )->u.lInt >>= p2->u.lInt;
          interp_stack_pop( 1 );
          break;

      case INSTR_AND                :
          ( pInterpStackPos - 2 )->u.lInt  &= p2->u.lInt;
          interp_stack_pop( 1 );
          break;

      case INSTR_XOR                :
          ( pInterpStackPos - 2 )->u.lInt  ^= p2->u.lInt;
          interp_stack_pop( 1 );
          break;

      case INSTR_OR                 :
          ( pInterpStackPos - 2 )->u.lInt  |= p2->u.lInt;
          interp_stack_pop( 1 );
          break;

      case INSTR_ONES_COMPLEMENT    :
          p2->u.lInt                        = ~( p2->u.lInt );
          break;

      case INSTR_JUMP               :
          pInstrStream += GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );
          break;

      case INSTR_COMPARE            :
          rl                   = GET_INT2( pInstrStream );
          MOVEP_2( pInstrStream );

          if ( compare_instr( &pInstrStream ) == TRUE )
              pInstrStream    += rl;

          {
              EM_VALUE *pInterpSav = pInterpStackPos;
              EM_VALUE *pVarSav    = pVarStackPos;

              while ( *pInstrStream != INSTR_END )
                  pInstrStream = interpret_instruction( pInstrStream );

              pInstrStream++; /* Skip past END instruction. */

              interp_stack_pop( ( pInterpStackPos - pInterpSav ) );
              var_stack_pop( ( pVarStackPos - pVarSav ) );
          }

          break;

      case INSTR_CALL_BUILTIN_FUNC  :
          rl                   = p2->u.lInt;
          interp_stack_pop( 1 );

          /*
           * Would be nice to do all this stuff in the error checker but
           * we can't because the function index is taken off the stack
           * and not off the instruction stream (thus it may be different
           * each time.)
           */
          for ( rl2 = 0L; emefCFuncTable[rl2].pName != NULL; rl2++ );

          if ( rl < 0 || rl >= rl2 )
          {
              strcpy( cErrorBuf, "Interpreter: "
                "Attemp to call non-existent builtin function." );
              /* print_error( ); */
              em_abort( );
          }

          rl2                  = 0L;
          rl2                  = GET_INT1( pInstrStream );
          MOVEP_1( pInstrStream );

          /*
           * If the number of arguments is less then 0, the builtin
           * function will deal with the type checking (this is so a
           * variable number of arguments is possible.)
           */
          if ( emefCFuncTable[rl].iNumArgs < 0 )
          {
              for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumArgs; rl2++ )
                  MOVEP_1( pInstrStream );
          }
          else
          {
              if ( rl2 != emefCFuncTable[rl].iNumArgs )
              {
                  strcpy( cErrorBuf, "Interpreter: Builtin "
                    "function does not conform to predefined prototype." );
                  /* print_error( ); */
                  em_abort( );
              }

              for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumArgs; rl2++ )
              {
                  if ( emefCFuncTable[rl].iArgTypes[rl2]
                    != (signed int) GET_INT1( pInstrStream ) )
                  {
                      strcpy( cErrorBuf, "Interpreter: Builtin function "
                        "does not conform to predefined prototype." );
                      /* print_error( ); */
                      em_abort( );
                  }

                  MOVEP_1( pInstrStream );
              }
          }

          rl2                  = 0L;
          rl2                  = GET_INT1( pInstrStream );
          MOVEP_1( pInstrStream );

          /*
           * If the number of return values is less then 0, the
           * builtin function will deal with the type checking (this
           * is so a variable number of return values is possible.)
           */
          if ( emefCFuncTable[rl].iNumReturn < 0 )
          {
              for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumReturn; rl2++ )
                  MOVEP_1( pInstrStream );
          }
          else
          {
              if ( rl2 != emefCFuncTable[rl].iNumReturn )
              {
                  strcpy( cErrorBuf, "Interpreter: Builtin "
                    "function does not conform to predefined prototype." );
                  /* print_error( ); */
                  em_abort( );
              }

              for ( rl2 = 0L; rl2 < emefCFuncTable[rl].iNumReturn; rl2++ )
              {
                  if ( emefCFuncTable[rl].iReturnTypes[rl2]
                    != (signed int) GET_INT1( pInstrStream ) )
                  {
                      strcpy( cErrorBuf, "Interpreter: Builtin function "
                        "does not conform to predefined prototype." );
                      /* print_error( ); */
                      em_abort( );
                  }

                  MOVEP_1( pInstrStream );
              }
          }

          ( *emefCFuncTable[rl].pFunc ) ( );
          break;

      case INSTR_CALL_FUNC          :
          {
              EM_VALUE *pOldVarStackBegin;
              EM_VALUE *pOldVarStackPos;
              byte *pNewStream;

              /*
               * Make sure the function trying to be called actaully
               * exists.
               */
              rl             = p2->u.lInt;
              interp_stack_pop( 1 );

              if ( rl < 0 || rl > siTopFuncIndex )
              {
                  strcpy( cErrorBuf, "Interpreter: "
                    "Attemp to call non-existent user function." );
                  /* print_error( ); */
                  em_abort( );
              }

              /*
               * Calling of dynamicly loaded functions from code is not
               * allowed.
               */
              if ( ppFuncs[rl]->bDynamic == TRUE )
              {
                  strcpy( cErrorBuf, "Interpreter: "
                    "Attemp to call dynamicly loaded user function." );
                  /* print_error( ); */
                  em_abort( );
              }

              /*
               * Compare call to real function.  We abort if they don't
               * match.
               */
              rl2            = GET_INT1( pInstrStream );
              MOVEP_1( pInstrStream );

              if ( rl2 != ppFuncs[rl]->iNumArgs )
              {
                  strcpy( cErrorBuf, "Interpreter: User function "
                    "does not conform to predefined prototype." );
                  /* print_error( ); */
                  em_abort( );
              }

              for ( rl2 = 0L; rl2 < ppFuncs[rl]->iNumArgs; rl2++ )
              {
                  if ( ppFuncs[rl]->pArgTypes[rl2]
                    != (signed int) GET_INT1( pInstrStream ) )
                  {
                      strcpy( cErrorBuf, "Interpreter: User function "
                        "does not conform to predefined prototype." );
                      /* print_error( ); */
                      em_abort( );
                  }

                  MOVEP_1( pInstrStream );
              }

              rl2            = 0L;
              rl2            = GET_INT1( pInstrStream );
              MOVEP_1( pInstrStream );

              if ( rl2 != ppFuncs[rl]->iNumReturn )
              {
                  strcpy( cErrorBuf, "Interpreter: User function "
                    "does not conform to predefined prototype." );
                  /* print_error( ); */
                  em_abort( );
              }

              for ( rl2 = 0L; rl2 < ppFuncs[rl]->iNumReturn; rl2++ )
              {
                  if ( ppFuncs[rl]->pReturnTypes[rl2]
                    != (signed int) GET_INT1( pInstrStream ) )
                  {
                      strcpy( cErrorBuf, "Interpreter: User function "
                        "does not conform to predefined prototype." );
                      /* print_error( ); */
                      em_abort( );
                  }

                  MOVEP_1( pInstrStream );
              }

              /*
               * We shift the variable stack so variable references
               * in the called function's code end up referencing the
               * right place.
               */
              pOldVarStackBegin = pVarStackBegin;
              pOldVarStackPos   = pVarStackPos;
              pVarStackBegin = pVarStackPos;

              pNewStream     = ppFuncs[rl]->pInstrStream;

              while ( *pNewStream != INSTR_END )
                  pNewStream = interpret_instruction( pNewStream );

              var_stack_pop( ( pVarStackPos - pOldVarStackPos ) );
              pVarStackBegin = pOldVarStackBegin;
              pVarStackPos   = pOldVarStackPos;
          }

          break;

      /*
       * If the error checker worked right and a bad pointer never
       * currupted the byte stream, this should NEVER happen.
       */
      default                       :
          sprintf( cErrorBuf,
            "Interpreter: Illegal instruction (0x%.2X).",
            (int) *--pInstrStream );
          print_error( );
          *pInstrStream = INSTR_END; /* Don't let this happen again. */
          /* em_abort( ); */
          break;
    }

    return ( pInstrStream );
}

#undef p2


/*
 * Compares two values.
 */
#define p                                        ( pInterpStackPos - 1 )
#define p2                                       ( pInterpStackPos - 2 )

static bool compare_instr( register byte **ppInstrStream )
{
    register bool rbTrue        = FALSE;

    switch ( p->iType )
    {
      case TYPE_INT   :
          switch ( *( *ppInstrStream++ ) )
          {
            case COND_EQUAL           :
                if ( p2->u.lInt == p->u.lInt )
                    rbTrue = TRUE;

                break;

            case COND_NOT_EQUAL       :
                if ( p2->u.lInt != p->u.lInt )
                    rbTrue = TRUE;

                break;

            case COND_GREATER         :
                if ( p2->u.lInt > p->u.lInt )
                    rbTrue = TRUE;

                break;

            case COND_LESS            :
                if ( p2->u.lInt < p->u.lInt )
                    rbTrue = TRUE;

                break;

            case COND_GREATER_OR_EQUAL:
                if ( p2->u.lInt >= p->u.lInt )
                    rbTrue = TRUE;

                break;

            case COND_LESS_OR_EQUAL   :
                if ( p2->u.lInt <= p->u.lInt )
                    rbTrue = TRUE;

                break;
          }

          break;

      case TYPE_FLOAT :
          switch ( *( *ppInstrStream++ ) )
          {
            case COND_EQUAL           :
                if ( p2->u.dFloat == p->u.dFloat )
                    rbTrue = TRUE;

                break;

            case COND_NOT_EQUAL       :
                if ( p2->u.dFloat != p->u.dFloat )
                    rbTrue = TRUE;

                break;

            case COND_GREATER         :
                if ( p2->u.dFloat > p->u.dFloat )
                    rbTrue = TRUE;

                break;

            case COND_LESS            :
                if ( p2->u.dFloat < p->u.dFloat )
                    rbTrue = TRUE;

                break;

            case COND_GREATER_OR_EQUAL:
                if ( p2->u.dFloat >= p->u.dFloat )
                    rbTrue = TRUE;

                break;

            case COND_LESS_OR_EQUAL   :
                if ( p2->u.dFloat <= p->u.dFloat )
                    rbTrue = TRUE;

                break;
          }

          break;

      case TYPE_STRING:
          switch ( *( *ppInstrStream++ ) )
          {
            case COND_EQUAL           :
                if ( strcmp( p2->u.pString->pString,
                  p->u.pString->pString ) == 0 )
                    rbTrue = TRUE;

                break;

            case COND_NOT_EQUAL       :
                if ( strcmp( p2->u.pString->pString,
                  p->u.pString->pString ) != 0 )
                    rbTrue = TRUE;

                break;
          }

          break;

      case TYPE_OBJECT:
          switch ( *( *ppInstrStream++ ) )
          {
            case COND_EQUAL           :
                if ( p2->u.pObject->pRealObject
                  == p->u.pObject->pRealObject )
                    rbTrue = TRUE;

                break;

            case COND_NOT_EQUAL       :
                if ( p2->u.pObject->pRealObject
                  != p->u.pObject->pRealObject )
                    rbTrue = TRUE;

                break;
          }

          break;
    }

    interp_stack_pop( 2 );
    return ( rbTrue );
}

#undef p
#undef p2

#undef GET_INT1
#undef GET_INT2
#undef GET_INT4
#undef GET_FLOAT4
#undef GET_FLOAT8
#undef MOVEP_1
#undef MOVEP_2
#undef MOVEP_4
#undef MOVEP_8


/*
 * End of interp.c
 */