/
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 <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include "emi.h"


/*
 * Globals
 */
char                                                          cNullChar;

int                                                    iInterpStackSize;
int                                                       iVarStackSize;

bool                                                   bReportFuncUsage;
char *                                                   pUsageFilename;

short int                                           siCurrentFileNumber;

char ***                                                 pppSymbolTable;
EM_FUNC **                                                      ppFuncs;
short int                                                siTopFuncIndex;
#if 0
extern EM_VALUE *                                           pStaticVars;
extern EM_VALUE *                                        pStaticVarsEnd;
extern EM_VALUE *                                        pStaticVarsPos;
#endif
int                                                        iCurrentFunc;


/*
 * Functions
 */
int main( int iArgC, char *pArgV[] )
{
    char *pBuf;
    int i                = 1;

    if ( --iArgC < 1 )
    {
usage:
        fprintf( stderr,
          "\nUsage: %s [-isize] [-vsize] <object files>\n",
          pArgV[0] );
        return ( 0 );
    }

    iInterpStackSize     = 128;
    iVarStackSize        = 64;

    for ( ; ; i++ )
    {
        if ( iArgC < i )
            goto usage;

        if ( pArgV[i][0] != '-' )
            break;

        if ( pArgV[i][1] == 'i' )
            iInterpStackSize = atoi( &pArgV[i][2] );
        else if ( pArgV[i][1] == 'v' )
            iVarStackSize    = atoi( &pArgV[i][2] );
        else
        {
            fprintf( stderr, "\nInvalid switch: `%s'.\n", pArgV[i] );
            return ( 1 );
        }
    }

    if ( ( pBuf = getenv( "EM_INTERP_STACK_SIZE" ) ) != NULL )
        iInterpStackSize = atoi( pBuf );

    if ( ( pBuf = getenv( "EM_VAR_STACK_SIZE" ) ) != NULL )
        iVarStackSize = atoi( pBuf );

    if ( iInterpStackSize < 1 )
    {
        strcpy( cErrorBuf, "Start: Invalid interpreter stack size." );
        print_error( );
        exit( 1 );
    }

    if ( iVarStackSize < 1 )
    {
        strcpy( cErrorBuf, "Start: Invalid variable stack size." );
        print_error( );
        exit( 1 );
    }

    for ( ; i <= iArgC; i++ )
    {
        if ( em_load_obj_file( pArgV[i], FALSE ) < 0 )
        {
            print_error( );
            exit( 1 );
        }
    }

    error_check( );
    em_translate( );

    if ( ( i = em_find_func( "main" ) ) < 0 )
    {
        strcpy( cErrorBuf, "Start: Cannot find user main()." );
        print_error( );
        exit( 1 );
    }

    stack_init( );
    em_call_func( i );
    stack_free( );

    return ( 0 );
}


/*
 * Allocates space for a function and returns it's index number.
 * Function space is never freed.
 */
int func_new( void )
{
    if ( ppFuncs == NULL )
        ppFuncs             = alloc_mem( ( sizeof( EM_FUNC * ) << 1 ) );
    else
        ppFuncs             = realloc_mem( ppFuncs, ( sizeof( EM_FUNC * )
                                * ( ++siTopFuncIndex + 2 ) ) );

    ppFuncs[siTopFuncIndex] = alloc_mem( sizeof( EM_FUNC ) );
    return ( siTopFuncIndex );
}


#define _E_FREAD1_O( f, p, s )                                         \
          if ( fread( (p), (s), 1, (f) ) == 0 )                        \
          {                                                            \
              if ( feof( (f) ) != 0 )                                  \
              {                                                        \
                  sprintf( cErrorBuf, "Loader: %s: Unexpected EOF.",   \
                    pFilename );                                       \
                  iError = -2;                                         \
              }                                                        \
              else                                                     \
              {                                                        \
                  sprintf( cErrorBuf, "Loader: %s: Unknown error.",    \
                    pFilename );                                       \
                  iError = -1;                                         \
              }                                                        \
                                                                       \
              goto end;                                                \
          }

/*
 * Loads in one emerald object (compiled) file.
 */
int em_load_obj_file( char *pFilename, bool bDynamic )
{
    FILE *pFile;
    char *pFileInfo;
    int iError               = 0;
    /* byte *pInstrStream; */
    i4 iI;
    i4 i                     = 0L;
    i2 iF                    = 0;
    i2 iFCount               = 0;
    i2 iSymTop               = -1;
    bool b                   = FALSE;

    if ( ( pFile = fopen( pFilename, "rb" ) ) == NULL )
    {
        sprintf( cErrorBuf, "Loader: %s: %s.", pFilename,
          strerror( errno ) );
        return ( -1 );
    }

    if ( pppSymbolTable == NULL )
        pppSymbolTable       = alloc_mem( sizeof( char ** ) );
    else
        pppSymbolTable       = realloc_mem( pppSymbolTable,
                                 ( sizeof( char ** )
                                 * ( siCurrentFileNumber + 1 ) ) );
#if 0
    if ( pStaticVars == NULL )
    {
        pStaticVars          = alloc_mem( ( sizeof( EM_VALUE )
                                 * iEmVarStackSize ) );
        pStaticVarsEnd       = ( pStaticVars + iEmVarStackSize - 1 );
        pStaticVarsPos       = pStaticVars;
    }
#endif
    /*
     * Magic number identifying this file as an Emerald object file.
     */
    _E_FREAD1_O( pFile, &i, sizeof( i4 ) );

    if ( (unsigned int) i != EM_MAGIC_NUMBER )
    {
        sprintf( cErrorBuf, "Loader: %s: Not an Emerald object file.",
          pFilename );
        iError               = -2;
        goto end;
    }

    /*
     * File format version number.
     */
    i                        = 0L;
    _E_FREAD1_O( pFile, &i, 1 );

    if ( i != EM_LOADER_VERSION )
    {
        sprintf( cErrorBuf, "Loader: %s: Wrong file format (%s format.)",
          pFilename, ( i > EM_LOADER_VERSION ? "Newer" : "Older" ) );
        iError               = -2;
        goto end;
    }

    /*
     * Information string.  Currently just gets discarded.
     */
    i                        = 0L;
    _E_FREAD1_O( pFile, &i, sizeof( i2 ) );

    pFileInfo                = alloc_mem( i + 1 );
    _E_FREAD1_O( pFile, pFileInfo, i );
    free_mem( (void **) &pFileInfo );

    /*
     * Symbol table.
     */
    i                        = 0L;
    _E_FREAD1_O( pFile, &i, sizeof( i2 ) );

    if ( i > 0 )
    {
        pppSymbolTable[siCurrentFileNumber] = alloc_mem( ( sizeof( char * )
                                                * ( i + 1 ) ) );
        iSymTop              = ( i - 1 );

        for ( iI = 0; iI < i; iI++ )
        {
            i2 iLen;

            _E_FREAD1_O( pFile, &iLen, sizeof( i2 ) );
            pppSymbolTable[siCurrentFileNumber][iI] = alloc_mem(
                                                        ( iLen + 1 ) );
            _E_FREAD1_O( pFile, pppSymbolTable[siCurrentFileNumber][iI],
              iLen );
            pppSymbolTable[siCurrentFileNumber][iI][iLen] = '\0';
        }
    }
#if 0
    i                        = 0L;
    _E_FREAD1_O( pFile, &i, sizeof( i4 ) );

    if ( i > 0 )
    {
        pInstrStream             = alloc_mem( i );
        _E_FREAD1_O( pFile, pInstrStream, i );
        stack_init( );

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

        while ( --pVarStackPos >= pVarStackBegin )
        {
            if ( pStaticVarsPos > pStaticVarsEnd )
            {
                strcpy( cErrorBuf,
                  "Loader: Static (Global) variable stack overflow." );
                print_error( );
                exit( 1 );
            }

            *pStaticVarsPos++    = *pVarStackPos;

            /*
             * After the pointer is copied set the original pointer
             * to NULL, thus it won't be freed by stack_free().
             */
            pVarStackPos->u.pInt = NULL;
        }

        stack_free( );
        free_mem( (void **) &pInstrStream );
    }
#endif
    /*
     * Functions.
     */
    _E_FREAD1_O( pFile, &iFCount, sizeof( i2 ) );

    if ( iFCount > 0 )
        b                         = TRUE;

    while ( iFCount > 0 )
    {
        iF                        = func_new( );
        ppFuncs[iF]->siFileNumber = siCurrentFileNumber;
        _E_FREAD1_O( pFile, &ppFuncs[iF]->fFuncFlags, sizeof( i4 ) );
        _E_FREAD1_O( pFile, &ppFuncs[iF]->iNumArgs, 1 );

        for ( i = 0L; i < ppFuncs[iF]->iNumArgs; i++ )
        {
            ppFuncs[iF]->pArgTypes      = realloc_mem(
                                            ppFuncs[iF]->pArgTypes,
                                            ( sizeof( intt )
                                            * ( i + 1 ) ) );
            ppFuncs[iF]->pArgArrayDim   = realloc_mem(
                                            ppFuncs[iF]->pArgArrayDim,
                                            ( sizeof( int )
                                            * ( i + 1 ) ) );
            ppFuncs[iF]->pArgArrayTypes = realloc_mem(
                                            ppFuncs[iF]->pArgArrayTypes,
                                            ( sizeof( intt )
                                            * ( i + 1 ) ) );

            ppFuncs[iF]->pArgArrayDim[i] = -1;

            do
            {
                _E_FREAD1_O( pFile, &ppFuncs[iF]->pArgTypes[i], 1 );
                ppFuncs[iF]->pArgArrayDim[i]++;
            }
            while ( ppFuncs[iF]->pArgTypes[i] == TYPE_ARRAY );

            switch ( ppFuncs[iF]->pArgTypes[i] )
            {
              case TYPE_INT   :
              case TYPE_FLOAT :
              case TYPE_STRING:
              case TYPE_OBJECT:
              case TYPE_ARRAY : break;
              default         :
                  sprintf( cErrorBuf,
                    "Loader: %s: User %s() has unknown argument type.",
                    pFilename, ppFuncs[iF]->pName );
                  iError          = -2;
                  goto end;
            }

            if ( ppFuncs[iF]->pArgArrayDim[i] > 0 )
            {
                ppFuncs[iF]->pArgArrayTypes[i] = ppFuncs[iF]->pArgTypes[i];
                ppFuncs[iF]->pArgTypes[i]      = TYPE_ARRAY;
            }
            else if ( ppFuncs[iF]->pArgTypes[i] == TYPE_STRING )
                ppFuncs[iF]->pArgArrayTypes[i] = TYPE_INT;
        }

        _E_FREAD1_O( pFile, &ppFuncs[iF]->iNumReturn, 1 );

        for ( i = 0L; i < ppFuncs[iF]->iNumReturn; i++ )
        {
            ppFuncs[iF]->pReturnTypes      = realloc_mem(
                                               ppFuncs[iF]->pReturnTypes,
                                               sizeof( intt )
                                               * ( i + 1 ) );
            ppFuncs[iF]->pReturnArrayDim   = realloc_mem( ppFuncs
                                               [iF]->pReturnArrayDim,
                                               ( sizeof( int )
                                               * ( i + 1 ) ) );
            ppFuncs[iF]->pReturnArrayTypes = realloc_mem( ppFuncs
                                               [iF]->pReturnArrayTypes,
                                               ( sizeof( intt )
                                               * ( i + 1 ) ) );

            ppFuncs[iF]->pReturnArrayDim[i] = -1;

            do
            {
                _E_FREAD1_O( pFile, &ppFuncs[iF]->pReturnTypes[i], 1 );
                ppFuncs[iF]->pReturnArrayDim[i]++;
            }
            while ( ppFuncs[iF]->pReturnTypes[i] == TYPE_ARRAY );

            switch ( ppFuncs[iF]->pReturnTypes[i] )
            {
              case TYPE_INT   :
              case TYPE_FLOAT :
              case TYPE_STRING:
              case TYPE_OBJECT:
              case TYPE_ARRAY : break;
              default         :
                  sprintf( cErrorBuf,
                    "Loader: %s: User %s() has unknown return type.",
                    pFilename, ppFuncs[iF]->pName );
                  iError          = -2;
                  goto end;
            }

            if ( ppFuncs[iF]->pReturnArrayDim[i] > 0 )
            {
                ppFuncs[iF]->pReturnArrayTypes[i] = ppFuncs
                                                    [iF]->pReturnTypes[i];
                ppFuncs[iF]->pReturnTypes[i]      = TYPE_ARRAY;
            }
            else if ( ppFuncs[iF]->pReturnTypes[i] == TYPE_STRING )
                ppFuncs[iF]->pReturnArrayTypes[i] = TYPE_INT;
        }

        i                         = 0L;
        _E_FREAD1_O( pFile, &i, sizeof( i2 ) );

        if ( pppSymbolTable[siCurrentFileNumber] == NULL || i > iSymTop )
        {
            sprintf( cErrorBuf,
              "Loader: %s: Invalid symbol table reference.", pFilename );
            iError                = -2;
            goto end;
        }

        ppFuncs[iF]->pName        = str_dup( pppSymbolTable
                                      [siCurrentFileNumber][i] );

        if ( em_find_func( ppFuncs[iF]->pName ) != iF )
        {
            sprintf( cErrorBuf,
              "Loader: %s: User %s() multiply defined.", pFilename,
              ppFuncs[iF]->pName );
            iError                = -2;
            goto end;
        }

        i                         = 0L;
        _E_FREAD1_O( pFile, &i, sizeof( i4 ) );

        if ( i <= 0 )
        {
            sprintf( cErrorBuf,
              "Loader: %s: Invalid code size for %s().", pFilename,
              ppFuncs[iF]->pName );
            iError                = -2;
            goto end;
        }

        ppFuncs[iF]->ulCodeSize   = (unsigned long) i;
        ppFuncs[iF]->pInstrStream = alloc_mem( i );
        _E_FREAD1_O( pFile, ppFuncs[iF]->pInstrStream, i );

        ppFuncs[iF]->bDynamic     = bDynamic;
        iFCount--;
    }

end:
    fclose( pFile );

    /*
     * If an error took place while loading a function remove the
     * function totally.
     */
    if ( b == TRUE && iError < 0 )
    {
        if ( ppFuncs[iF]->pName != NULL )
            str_free( ppFuncs[iF]->pName );

        if ( ppFuncs[iF]->pArgTypes != NULL )
            free_mem( (void **) &ppFuncs[iF]->pArgTypes );

        if ( ppFuncs[iF]->pArgArrayDim != NULL )
            free_mem( (void **) &ppFuncs[iF]->pArgArrayDim );

        if ( ppFuncs[iF]->pArgArrayTypes != NULL )
            free_mem( (void **) &ppFuncs[iF]->pArgArrayTypes );

        if ( ppFuncs[iF]->pReturnTypes != NULL )
            free_mem( (void **) &ppFuncs[iF]->pReturnTypes );

        if ( ppFuncs[iF]->pReturnArrayDim != NULL )
            free_mem( (void **) &ppFuncs[iF]->pReturnArrayDim );

        if ( ppFuncs[iF]->pReturnArrayTypes != NULL )
            free_mem( (void **) &ppFuncs[iF]->pReturnArrayTypes );

        if ( ppFuncs[iF]->pInstrStream != NULL )
            free_mem( (void **) &ppFuncs[iF]->pInstrStream );

        free_mem( (void **) &ppFuncs[iF] );

        if ( siTopFuncIndex == 0 )
            free_mem( (void **) &ppFuncs );
        else
            ppFuncs         = realloc_mem( ppFuncs, ( sizeof( EM_FUNC * )
                                * ( --siTopFuncIndex + 2 ) ) );
    }

    siCurrentFileNumber++;
    return ( iError );
}

#undef _E_FREAD1_O


/*
 * Called after all code modules are loaded.  Translates all TRANS_*
 * byte codes into actaul instructions executable by
 * interpret_instruction().
 */
void em_translate( void )
{
    byte *p;

    if ( ppFuncs == NULL )
        return;

    iEmError        = 0;

    for ( iCurrentFunc = 0; iCurrentFunc <= siTopFuncIndex;
      iCurrentFunc++ )
    {
        p           = ppFuncs[iCurrentFunc]->pInstrStream;

        while ( *p != INSTR_END )
        {
            p       = bct( p );

            if ( iEmError < 0 )
            {
                print_error( );
                exit( 1 );
            }
        }
    }
}


byte *bct( byte *p )
{
    i4 iTemp;

    switch ( *p++ )
    {
      case INSTR_PUSH_INT1          : p++; break;
      case INSTR_PUSH_INT2          :
      case INSTR_PUSH_LOCAL         :
      case INSTR_ASSIGN_LOCAL       :
      case INSTR_JUMP               :
          p    += sizeof( i2 );
          break;

      case INSTR_PUSH_INT4          :
          p    += sizeof( i4 );
          break;

      case INSTR_PUSH_FLOAT4        :
          p    += sizeof( f4 );
          break;

      case INSTR_PUSH_FLOAT8        :
          p    += sizeof( f8 );
          break;

      case INSTR_PUSH_STRING        :
          iTemp = (i4) *( (i2 *) p );
          p    += sizeof( i2 );
          p    += iTemp;
          break;

      case INSTR_COMPARE            :
          p    += sizeof( i2 );
          p++;

          while ( *p != INSTR_END )
          {
                p = bct( p );

                if ( iEmError < 0 )
                    return ( p );
          }

          while ( *p != INSTR_END )
          {
                p = bct( p );

                if ( iEmError < 0 )
                    return ( p );
          }


          break;

      case INSTR_CALL_BUILTIN_FUNC  :
      case INSTR_CALL_FUNC          :
          iTemp = (i4) *p++;
          p    += iTemp;
          iTemp = (i4) *p++;
          p    += iTemp;
          break;

      case TRANS_BUILTIN_FUNC       :
          {
              char *pStr;

              *( p - 1 )       = INSTR_PUSH_INT2;
              iTemp            = (i4) *( (i2 *) p );
              pStr = pppSymbolTable[ppFuncs[iCurrentFunc]->siFileNumber]
                       [iTemp];
              iTemp            = 0L;

              while ( emefCFuncTable[iTemp].pName != NULL )
              {
                  if ( strcmp( pStr,
                    emefCFuncTable[iTemp].pName ) == 0 )
                      break;

                  iTemp++;
              }

              if ( emefCFuncTable[iTemp].pName == NULL )
              {
                  sprintf( cErrorBuf, "Linker: Builtin %s() not found; "
                    "referenced from %s().", pStr,
                    ppFuncs[iCurrentFunc]->pName );
                  iEmError     = -1;
                  break;
              }

              ADD_INSTR_I2( p, iTemp );
          }

          break;

      case TRANS_FUNC               :
          {
              char *pStr;

              *( p - 1 )       = INSTR_PUSH_INT2;
              iTemp            = (i4) *( (i2 *) p );
              pStr = pppSymbolTable[ppFuncs[iCurrentFunc]->siFileNumber]
                       [iTemp];

              for ( iTemp = 0L; ppFuncs[iTemp] != NULL; iTemp++ )
              {
                  if ( !strcmp( pStr, ppFuncs[iTemp]->pName ) )
                      break;
              }

              if ( ppFuncs[iTemp] == NULL )
              {
                  sprintf( cErrorBuf, "Linker: User %s() not found; "
                    "referenced from %s().", pStr,
                    ppFuncs[iCurrentFunc]->pName );
                  iEmError     = -2;
                  break;
              }

              ADD_INSTR_I2( p, iTemp );
          }

          break;

      default                       :
          break;
    }

    return ( p );
}


/*
 * Returns a function's index number given it's name.  Will return -1
 * if no function with the given name exists.
 */
int em_find_func( char *pName )
{
    int iF;

    if ( ppFuncs != NULL )
    {
        for ( iF = 0; iF <= siTopFuncIndex; iF++ )
        {
            if ( strcmp( pName, ppFuncs[iF]->pName ) == 0 )
                break;
        }

        if ( ppFuncs[iF] != NULL )
            return ( iF );
    }

    return ( -1 );
}


/*
 * Calls an Emerald function given it's index in the function table.
 */
int em_call_func( int iF )
{
    byte *p;

    if ( em_setup_abort( ) != 0 )
        return ( -1 );

    if ( iF >= 0 && iF <= siTopFuncIndex )
    {
        p     = ppFuncs[iF]->pInstrStream;

        while ( *p != INSTR_END )
            p = interpret_instruction( p );
    }
    else
        return ( -2 );

    return ( 0 );
}


/*
 * End of main.c
 */