/*  Command-line shell for the "Small" Abstract Machine using JIT compiler.
 *
 *  Copyright (c) ITB CompuPhase, 1997-2002
 *  Copyright (c) Mark Peter, 1998-1999
 *
 *  This file may be freely used. No warranties of any kind.
 */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>     /* for memset() (on some compilers) */
#include <time.h>
#include "amx.h"


#define CONSOLE
/*#define FIXEDPOINT*/

void core_Init(void);   /* two functions from AMX_CORE.C */
void core_Exit(void);

static int abortflagged = 0;
void sigabort(int sig)
{
  abortflagged=1;
  signal(sig,sigabort); /* re-install the signal handler */
}

int AMXAPI amx_AbortProc(AMX *amx)
{
  switch (amx->dbgcode) {
  case DBG_INIT:
    return AMX_ERR_NONE;
  case DBG_LINE:
    /* check whether an "abort" was requested */
    return abortflagged ? AMX_ERR_EXIT : AMX_ERR_NONE;
  default:
    return AMX_ERR_DEBUG;
  } /* switch */
}

void *LoadProgram(AMX *amx, char *filename)
{
  FILE *fp;
  AMX_HEADER hdr;
  void *program;
  #if defined JIT
    void *np, *rt;       // np = new machine; rt = relocation table
  #endif

  if ( (fp = fopen( filename, "rb" )) != NULL )
  {
    fread(&hdr, sizeof hdr, 1, fp);
    amx_Align32((uint32_t *)&hdr.stp);
	amx_Align32((uint32_t *)&hdr.size);
    if ( (program = malloc( (int)hdr.stp )) != NULL )
    {
      rewind( fp );
      fread( program, 1, (int)hdr.size, fp );
      fclose( fp );
      memset(amx, 0, sizeof *amx);
      amx_SetDebugHook(amx, amx_AbortProc);
      if ( amx_Init( amx, program ) != AMX_ERR_NONE )
      {
         free( program );
         return NULL;
      }

      #if defined JIT
        /* Now try to run the JIT compiler... */
        np = malloc( amx->code_size );
        rt = malloc( amx->reloc_size );

        if ( !np || (!rt && amx->reloc_size>0) )
        {
           free( program );
           free( np );
           free( rt );
           return program;
        }

        /* JIT rulz! (TM) */
        if (amx_InitJIT(amx, rt, np) == AMX_ERR_NONE) {
          /* The compiled code is relocatable, since only relative jumps are
           * used for destinations within the generated code and absoulute
           * addresses for jumps into the runtime, which is fixed in memory.
           */
          amx->base = realloc( np, amx->code_size );
          free( program );
          free( rt );
          return np;
        }
        else    /* MP: added case where JIT failed */
        {
          free( np );
          free( program );
          free( rt );
          printf("JIT compiler failed\n");
          return NULL;
        } /* if */
      #else
        /* standard virtual machine (no JIT) */
        return program;
      #endif
    } /* if */
  } /* if */
  return NULL;
}


static int register_natives(AMX *amx)
{
  #if defined CONSOLE
    extern AMX_NATIVE_INFO console_Natives[];
  #endif
  #if defined DATETIME
    extern AMX_NATIVE_INFO time_Natives[];
  #endif
  #if defined FIXEDPOINT
    extern AMX_NATIVE_INFO fixed_Natives[];
  #endif
  #if defined FLOATPOINT
    extern AMX_NATIVE_INFO float_Natives[];
  #endif
  extern AMX_NATIVE_INFO core_Natives[];

  #if defined CONSOLE
    amx_Register(amx, console_Natives, -1);
  #endif
  #if defined DATETIME
    amx_Register(amx, time_Natives, -1);
  #endif
  #if defined FIXEDPOINT
    amx_Register(amx, fixed_Natives, -1);
  #endif
  #if defined FLOATPOINT
    amx_Register(amx, float_Natives, -1);
  #endif
  return amx_Register(amx, core_Natives, -1);
}


int main(int argc,char *argv[])
{
  AMX amx;
  cell ret = 0;
  int err;
  void *program;
  clock_t start,end;

  if (argc != 2 || (program = LoadProgram(&amx,argv[1])) == NULL) {
    printf("Usage: SRUN <filename>\n\n"
           "The filename must include the extension\n");
    return 1;
  } /* if */
  signal(SIGINT,sigabort);

  err = register_natives(&amx);
  core_Init();

  start=clock();
  if (err == AMX_ERR_NONE) {
    err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN, 0);
    while (err == AMX_ERR_SLEEP)
      err = amx_Exec(&amx, &ret, AMX_EXEC_CONT, 0);
  } /* if */
  end=clock();

  if (err != AMX_ERR_NONE)
    printf("Run time error %d on line %ld\n", err, amx.curline);
  else if (ret != 0)
    printf("%s returns %ld\n", argv[1], (long)ret);

  free(program);
  core_Exit();

  printf("\nRun time: %.2f seconds",(double)(end-start)/CLK_TCK);
  return 0;
}

