//Copyright, 2001-2003, Astra Network Inc.  All Rights Reserved

//This source code has been published by Astra Network Inc. However, any
//use, reproduction, modification, distribution or transfer of this
//software, or any software which includes or is based upon any of this
//code, is only permitted if expressly authorized by a written license
//agreement from Astra. Contact your Astra representative directly for
//more information.



#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <string.h>

#include "sixfourbit.h"
#include "loop.h"
#include "pcode.h"
#include "global_bits.h"
#include "module_load.h"
#include "olmod.h"

/* datatypes and file globals */

/* Stack stuff */
enum
{
  kStacktype_barrier = 0,
  kStacktype_any = 0,
  kStacktype_num,
  kStacktype_string,
  kStacktype_ptr
};

typedef struct
{
  int type;
  union
  {
    char *string;
    int64_t num;
    void *ptr;
  }
  v;
}
pcode_stackelement_t;

pcode_stackelement_t *stack;
static size_t stack_size = 0;
static size_t stack_alloc = 0;

/* forstack stuff */
static char *forstack[64];
static unsigned int fordepth = 0;

/* Alarm stuff */
typedef struct
{
  size_t segment;
  struct timespec when;
  void *next;
}
alarm_listelement_t;

static /*@null@ */ alarm_listelement_t *alarm_lh = NULL;

typedef struct
{
  size_t segment;
  void *next;
}
signal_listelement_t;

static /*@null@ */ signal_listelement_t *suo_lh  = NULL; /* SIGUSR1 */
static /*@null@ */ signal_listelement_t *tsuo_lh = NULL; /* temp */
static /*@null@ */ signal_listelement_t *sut_lh  = NULL; /* SIGUSR2 */
static /*@null@ */ signal_listelement_t *tsut_lh = NULL; /* temp */

static volatile int sigusrone = 0;
static volatile int sigusrtwo = 0;
static volatile int sigchild = 0;
static volatile pid_t sigchilds[64];

/* Nebuloid stuff */
typedef struct
{
  char *name;
  olmod_module_t *module;
  olmod_blackbox_t *blackbox;
  void **args;  /* just a place to store the allocation used to pass params */
  void *next;
}
nebuloid_t;

/* Context stuff */
typedef struct
{
  pid_t pid;
  char *sigchild;
  nebuloid_t *nebuloids;
}
context_t;

static /*@null@ */ nebuloid_t *global_nebuloids = NULL;
static /*@null@ */ context_t *contexts = NULL;

/* prototypes */

static void p_dup (void);
static int64_t *p_popnum (void);
/* WARNING! you must use/copy the value returned before calling p_push */
static char *p_popstr (void);
static void *
p_pop (int type) /*@globals stack_size, stack;@ */ ;
     static void p_push (int type, /*@null@ */ void *value);
/* integers are copied upon push */
     static int p_trimstack (void);
/* *** */
     static void insert_alarm (long int segment, int64_t delay);
     static size_t wait_alarm (void);
/* *** */
     static void signal_handler (int signo);
     static void add_signal (int64_t segment, int64_t signo);
/* *** */
     static nebuloid_t *find_neb (char *name);
/* *** */
     static void run_pcode (char *pc);
     static void op_get (void);
     static void op_set (void);
     static void op_inst (void);
     static void op_dinst (void);
     static void op_call (char **pca);
     static void op_alarm (void);
     static void op_signal (void);
     static void op_sigchild (void);
     static int op_return (char **pca);
     static void op_context (void);
     static void op_getpid (void);
     static void op_setpid (void);
     static void op_drop (void);
     static void op_dup (void);
     static void op_if (char **pca);
     static void op_fi (void);
     static void op_for (char *pc);
     static char *op_rof (char *pc);
     static void op_lt (void);
     static void op_gt (void);
     static void op_not (void);
     static void op_eq (void);
     static void op_and (void);
     static void op_or (void);
     static void op_plus (void);
     static void op_minus (void);
     static void op_mult (void);
     static void op_div (void);
     static void op_neg (void);
     static char *op_num (char *pc);
     static char *op_paren (char *pc);

/* code */
     static long int cur_ctx = -1;

// This is the main loop of the program.  At this point we only exit if
// something fatal() has happened.

     void do_pcode (void)
{
  char *pc = pcode;

  // clear sigchild array
  memset ((char *) sigchilds, 0, sizeof (pid_t) * 64);
  // allocate contexts
  contexts = calloc ((size_t) ncontexts, sizeof (context_t));
  if (contexts == NULL)
    {
      fatal ("Could not allocate initial context");
    }
  // allocate stack
  stack = malloc (stack_initial_alloc * sizeof (pcode_stackelement_t));
  if (stack == NULL)
    {
      fatal ("Could not allocate initial stack");
    }
  stack_alloc = (size_t) stack_initial_alloc;

  // set up signal handler
  (void) signal (SIGUSR1, signal_handler);
  (void) signal (SIGUSR2, signal_handler);
  (void) signal (SIGCHLD, signal_handler);
  (void) signal (SIGALRM, signal_handler);

  // go
  for (;;)
    {
      run_pcode (pc);
      cur_ctx = -1;

      // At this point the runtime sleeps until the next alarm
      // occurs and sets the program counter (pc) to the next code
      // segment to run.
      pc = segment_table[wait_alarm ()].pcode;	/* pauses here */
    }
}

void
p_dup (void)
{
  pcode_stackelement_t *ptr = &stack[stack_size - 1];
  switch (ptr->type)
    {
    case kStacktype_string:
      p_push (ptr->type, ptr->v.string);
      break;
    case kStacktype_num:
      p_push (ptr->type, &(ptr->v.num));
      break;
    case kStacktype_ptr:
    case kStacktype_barrier:
      fatal ("Attempted to dup ptr or barrier");
    default:
      fatal ("Unknown type in dup");
    }
}

int64_t *
p_popnum (void)
{
  return (int64_t *) p_pop (kStacktype_num);
}

char *
p_popstr ()
{
  return (char *) p_pop (kStacktype_string);
}

/*@null@*/ void *
p_pop (int type)
     /*@globals stack_size, stack;@ */
{
  if (stack_size == 0)
    {
      fatal ("Tried to pop off empty stack");
    }
  --stack_size;
  if (type != kStacktype_any && type != stack[stack_size].type)
    {
      printf ("Mismatched types: wanted ");
      if (type == kStacktype_string)
	printf ("string, ");
      else if (type == kStacktype_num)
	printf ("num, ");
      else if (type == kStacktype_ptr)
	printf ("ptr, ");

      if (stack[stack_size].type == kStacktype_string)
	printf ("got string \"%s\")\n", stack[stack_size].v.string);
      else if (stack[stack_size].type == kStacktype_num)
	printf ("got num %s)\n", QTOSTR (stack[stack_size].v.num));
      else if (stack[stack_size].type == kStacktype_ptr)
	printf ("got ptr %p)\n", stack[stack_size].v.ptr);
      else
	printf ("got barrier)\n");
      exit (EXIT_FAILURE);
    }
  switch (stack[stack_size].type)
    {
    case kStacktype_num:
      return &stack[stack_size].v.num;
    case kStacktype_string:
      return stack[stack_size].v.string;
    case kStacktype_ptr:
      return stack[stack_size].v.ptr;
    case kStacktype_barrier:
      fatal ("Popped barrier");
    default:
      fatal ("Unknown type in pop");
    }
  /*@-unreachable@ */
  return NULL;
  /*@+unreachable@ */
}

void
p_push (int type, void *value)
{
  if (stack == NULL)
    {
      stack = malloc (256 * sizeof (pcode_stackelement_t));
      stack_alloc = 256;
    }
  if ((stack_size + 1) > stack_alloc)
    {
      if (stack_policy == 0)
	{
	  stack_alloc *= 2;
	  stack = realloc (stack, stack_alloc);
	}
      else
	{
	  fatal ("Overflow on die policy");
	}
    }
  if (stack == NULL)
    {
      fatal ("out of memory growing stack in push");
    }

  stack[stack_size].type = type;
  if (type == kStacktype_string)
    stack[stack_size].v.string = value;
  else if (type == kStacktype_num)
    stack[stack_size].v.num = *(int64_t *) value;
  else if (type == kStacktype_ptr)
    stack[stack_size].v.ptr = value;
  else if (type != kStacktype_barrier)
    {
      fatal ("Unknown type in push");
    }
  stack_size++;
}

int
p_trimstack (void)
{
  if (stack_size == 0)
    return 1;
  while (stack[stack_size].type != kStacktype_barrier)
    {
      if (stack_size == 0)
	return 1;
      stack_size--;
    }
  return 0;
}

#define tvtscmp(tva, tsb, cmp)\
	((tva)->tv_sec cmp (tsb)->tv_sec ||\
	 ((tva)->tv_sec == (tsb)->tv_sec &&\
	  ((tva)->tv_usec * 1000) cmp (tsb)->tv_nsec))

#define tstscmp(tsa, tsb, cmp)\
	((tsa)->tv_sec cmp (tsb)->tv_sec ||\
	 ((tsa)->tv_sec == (tsb)->tv_sec &&\
	  (tsa)->tv_nsec cmp (tsb)->tv_nsec))

void
insert_alarm (long int segment, int64_t delay)
     /*@globals alarm_lh;@ */
{
  alarm_listelement_t *alpp = NULL, *alp = alarm_lh;
  struct timespec when;
  struct timeval now;

  if (QLT (delay, LTOQ (0)))
    {
      fatal ("Negative alarm delay");
    }

  (void) gettimeofday (&now, NULL);

  when.tv_nsec =
    (QTOL (QMOD (delay, LTOQ (1000))) * 1000 + now.tv_usec) * 1000;
  when.tv_sec =
    QTOL (QDIV (delay, LTOQ (1000))) + now.tv_sec +
    (when.tv_nsec / 1000000000);
  when.tv_nsec %= 1000000000;

  while (alp != NULL && tstscmp (&alp->when, &when, <))
    {
      alpp = alp;
      alp = (alarm_listelement_t *) alp->next;
    }

  if (alp == NULL)
    {				/* append */
      alp = malloc (sizeof (alarm_listelement_t));
      if (alp == NULL)
	{
	  fatal ("Could not allocate alarm entry in append");
	}
      alp->next = NULL;

      if (alpp == NULL)		/* at the head */
	alarm_lh = alp;
      else			/* just at tail */
	alpp->next = alp;
    }
  else
    {				/* insert */
      if (alpp == NULL)
	{			/* before first */
	  alpp = malloc (sizeof (alarm_listelement_t));
	  if (alpp == NULL)
	    {
	      fatal ("Could not allocate alarm entry in insert");
	    }
	  alpp->next = alp;
	  alarm_lh = alpp;
	  alp = alpp;
	}
      else
	{			/* after first */
	  alpp->next = malloc (sizeof (alarm_listelement_t));
	  if (alpp->next == NULL)
	    {
	      fatal ("Could not allocate alarm entry in after first");
	    }
	  ((alarm_listelement_t *) (alpp->next))->next = alp;
	  alp = (alarm_listelement_t *) alpp->next;
	}
    }

  alp->when.tv_sec = when.tv_sec;
  alp->when.tv_nsec = when.tv_nsec;
  alp->segment = (size_t) segment;
}

size_t
wait_alarm (void)
{
  struct timeval now;
  struct itimerspec nit;
  timer_t timer_id;
  alarm_listelement_t *alp = alarm_lh;
  size_t rv;
  sigset_t mysigset, unsigset;
  signal_listelement_t *ptr;

  (void) sigemptyset (&unsigset);
  (void) sigemptyset (&mysigset);
  (void) sigaddset (&mysigset, SIGTERM);
  (void) sigaddset (&mysigset, SIGCHLD);
  (void) sigaddset (&mysigset, SIGUSR1);
  (void) sigaddset (&mysigset, SIGUSR2);
  (void) sigaddset (&mysigset, SIGALRM);

  if (alarm_lh == NULL)
    {
      fatal ("Waiting for alarm with no scheduled alarms");
    }
  if (contexts == NULL)
    {
      fatal ("Internal error: NULL contexts");
    }
  for (;;)
    {
      (void) gettimeofday (&now, NULL);

      /* SIGTERM, SIGUSR1, SIGUSR2, SIGCHLD, SIGALRM */
      (void) sigprocmask (SIG_BLOCK, &mysigset, NULL);

      exit_if_quitting ();

      if (sigchild != 0)
	{
	  uint32_t i = 0, j = 0;

	  while (i < 64 && sigchilds[i] == 0)
	    i++;
	  if (sigchilds[i] == 0)
	    {
	      /* all done */
	      sigchild = 0;
	      continue;
	    }
	  while (j < ncontexts && contexts[j].pid != sigchilds[i])
	    j++;
	  if (contexts[j].pid == sigchilds[i] && contexts[j].sigchild != NULL)
	    {
	      /* found one! */
	      rv = (size_t) contexts[j].sigchild;
	      contexts[j].sigchild = NULL;
	      sigchilds[i] = 0;
	      return rv;
	    }
	  sigchilds[i] = 0;
	}
      if (sigusrone != 0)
	{
	  if (suo_lh == NULL)
	    {
	      sigusrone = 0;
	      suo_lh = tsuo_lh;
	    }
	  else
	    {
	      rv = suo_lh->segment;
	      ptr = suo_lh;
	      suo_lh = ptr->next;
	      free (ptr);
	      return rv;
	    }
	}
      if (sigusrtwo != 0)
	{
	  if (sut_lh == NULL)
	    {
	      sigusrtwo = 0;
	      sut_lh = tsut_lh;
	    }
	  else
	    {
	      rv = sut_lh->segment;
	      ptr = sut_lh;
	      sut_lh = ptr->next;
	      free (ptr);
	      return rv;
	    }
	}

      if (tvtscmp (&now, &alarm_lh->when, >))
	{
	  rv = alarm_lh->segment;
	  alarm_lh = (alarm_listelement_t *) alarm_lh->next;
	  free (alp);
	  /* SIGTERM, SIGUSR1, SIGUSR2, SIGCHLD, SIGALRM */
	  (void) sigprocmask (SIG_UNBLOCK, &mysigset, NULL);
	  return rv;
	}

#if defined(__QNX__) && !defined(__QNXNTO__)
      timer_id = timer_create (CLOCK_REALTIME, NULL);
#else
      (void) timer_create (CLOCK_REALTIME, NULL, &timer_id);
#endif

      nit.it_value.tv_sec = alarm_lh->when.tv_sec;
      nit.it_value.tv_nsec = alarm_lh->when.tv_nsec;
      nit.it_interval.tv_sec = 0;
      nit.it_interval.tv_nsec = 0;

      (void) timer_settime (timer_id, TIMER_ABSTIME, &nit, NULL);

      /* all signals */
      (void) sigsuspend (&unsigset);

      (void) timer_delete (timer_id);

      /* SIGTERM, SIGUSR1, SIGUSR2, SIGCHLD, SIGALRM */
      (void) sigprocmask (SIG_UNBLOCK, &mysigset, NULL);
    }
  return 0;
}

// Add pcode defined signal handlers
void
add_signal (int64_t segment, int64_t signo)
{
  signal_listelement_t **bptr, *ptr;
  long int sig = QTOL (signo);

  // set the list pointer to the correct list of signals based on signo
  switch (sig)
    {
    case 1:
      bptr = sigusrone != 0 ? &tsuo_lh : &suo_lh;
      break;
    case 2:
      bptr = sigusrtwo != 0 ? &tsut_lh : &sut_lh;
      break;
    default:
      fatal ("Signal number not '1' or '2'");
    }
  // allocate space for the new signal list element
  ptr = malloc (sizeof (signal_listelement_t));
  if (ptr == NULL)
    {
      fatal ("Could not allocate signal entry");
    }
  // initilize element
  ptr->segment = (size_t) QTOL (segment);
  ptr->next = NULL;

  // attach new element to list
  if (*bptr != NULL)
    {
      while ((*bptr)->next != NULL)	/* find end of list */
	bptr = (signal_listelement_t **) (&((*bptr)->next));
    }
  *bptr = ptr;
}

void
signal_handler (int signo)
{
  switch (signo)
    {
    case SIGUSR1:
      sigusrone = 1;
      (void) signal (SIGUSR1, signal_handler);
      break;
    case SIGUSR2:
      sigusrtwo = 1;
      (void) signal (SIGUSR2, signal_handler);
      break;
    case SIGCHLD:
      {
	int status;
	int i = 0;
	while (i < 64 && sigchilds[i] != 0)
	  i++;
	if (i != 64)
	  {
	    sigchilds[i] = waitpid (-1, &status, WNOHANG);
	    if (sigchilds[i] != 0)
	      sigchild = 1;
	  }
	else
	  {
	    (void) waitpid (-1, &status, WNOHANG);
	  }
	(void) signal (SIGCHLD, signal_handler);
      }
      break;
    case SIGALRM:
      (void) signal (SIGALRM, signal_handler);
      break;
    default:
      break;
    }
}

// look up a nebuloid by name and return it's pointer
nebuloid_t *
find_neb (char *name)
{
  nebuloid_t *ptr;

  if (contexts == NULL)
    {
      fatal ("Internal error: NULL contexts in find neb");
    }

  // look in the local context first (if we have have one)
  if (cur_ctx != -1)
    {
      ptr = contexts[cur_ctx].nebuloids;
      while (ptr != NULL)
	{
	  if (strcmp (ptr->name, name) == 0)
	    return ptr;
	  ptr = (nebuloid_t *) ptr->next;
	}
    }
  // look through list of global nebuloids
  ptr = global_nebuloids;
  while (ptr != NULL)
    {
      if (strcmp (ptr->name, name) == 0)
	return ptr;
      ptr = (nebuloid_t *) ptr->next;
    }
  return NULL;
}

static void
run_pcode (char *pc)
{
  int bk = 0;

  do
    {
      // main switch
      switch (*pc)
	{
	case pCALL:
	  op_call (&pc);
	  break;
	case pALARM:
	  op_alarm ();
	  break;
	case pSIGNAL:
	  op_signal ();
	  break;
	case pSIGCHILD:
	  op_sigchild ();
	  break;
	case pRETURN:
	  bk = op_return (&pc);
	  break;

	  /* *** */
	case pGET:
	  op_get ();
	  break;
	case pSET:
	  op_set ();
	  break;
	case pINST:
	  op_inst ();
	  break;
	case pDINST:
	  op_dinst ();
	  break;

	  /* *** */
	case pCONTEXT:
	  op_context ();
	  break;
	case pGETPID:
	  op_getpid ();
	  break;
	case pSETPID:
	  op_setpid ();
	  break;

	  /* *** */
	case pDROP:
	  op_drop ();
	  break;
	case pDUP:
	  op_dup ();
	  break;

	  /* *** */
	case pIF:
	  op_if (&pc);
	  break;
	case pFI:
	  op_fi ();
	  break;

	case pFOR:
	  op_for (pc);
	  break;

	case pROF:
	  pc = op_rof (pc);
	  break;

	  /* *** */
	case pLT:
	  op_lt ();
	  break;
	case pGT:
	  op_gt ();
	  break;
	case pNOT:
	  op_not ();
	  break;
	case pEQ:
	  op_eq ();
	  break;
	case pAND:
	  op_and ();
	  break;
	case pOR:
	  op_or ();
	  break;

	  /* *** */
	case pPLUS:
	  op_plus ();
	  break;
	case pMINUS:
	  op_minus ();
	  break;
	case pMULT:
	  op_mult ();
	  break;
	case pDIV:
	  op_div ();
	  break;
	case pNEG:
	  op_neg ();
	  break;

	  /* *** */
	case pNUM:
	  pc = op_num (pc);
	  break;
	case '(':
	  pc = op_paren (pc);
	  break;

	  /* *** */
	default:
	  fatal ("Bad P-Code (unknown operand, pc=%p, op=%d)", pc, (int) *pc);
	}
      pc++;
    }
  while (bk == 0);
  if (verbose != 0)
    printf (".\n");
}

// get the value from the get function of a module
static void
op_get (void)
{
  int i = 0;
  char *name = p_popstr ();
  nebuloid_t *neb = find_neb (name);
  int64_t p;

  if (verbose != 0)
    printf ("get ");

  if (neb == NULL)
    {
      fatal ("Attempted to get uninstantiated nebuloid \"%s\"", name);
    }

  while (neb->module->dynamic_argtypes[i] != kEndList)
    i++;
  while (i-- != 0)
    neb->args[i] =
      p_pop (neb->module->dynamic_argtypes[i] == kString ?
	     kStacktype_string : kStacktype_num);

  p = (*neb->module->module_get) (neb->blackbox, neb->args);
  p_push (kStacktype_num, &p);
}

static void
op_set (void)
{
  char *name = p_popstr ();
  nebuloid_t *neb = find_neb (name);

  if (verbose != 0)
    printf ("set ");

  if (neb == NULL)
    {
      fatal ("Attempted to access uninstantiated nebuloid\"%s\"", name);
    }

  (void) (*neb->module->module_set) (neb->blackbox, *p_popnum ());
}

static void
op_inst (void)
{
  int i = 0, j = 0;
  nebuloid_t *neb = malloc (sizeof (nebuloid_t));
  char *modname;
  pid_t pid;

  if (verbose != 0)
    printf ("inst ");

  if (neb == NULL)
    {
      fatal ("Could not allocate nebuloid entry");
    }

  if (modtable == NULL)
    {
      fatal ("Internal error: NULL modtable in op_inst");
    }

  if (contexts == NULL)
    {
      fatal ("Internal error: NULL contexts in op_inst");
    }

  neb->name = p_popstr ();
  modname = pcode_module_table[QTOL (*p_popnum ())];

  for (i = 0; modtable[i].module != NULL; i++)
    {
      if (modtable[i].module->keyword == NULL)
	{
	  fatal ("Internal error: modtable[].module->keyword == NULL");
	}

      if (strcmp (modtable[i].module->keyword, modname) == 0)
	break;
    }
  neb->module = modtable[i].module;

  /* local/global */
  if (QTOBOOL (*p_popnum ()) != 0)
    {
      neb->next = contexts[cur_ctx].nebuloids;
      contexts[cur_ctx].nebuloids = neb;
      pid = contexts[cur_ctx].pid;
    }
  else
    {
      neb->next = global_nebuloids;
      global_nebuloids = neb;
      pid = 0;
    }

  i = 0;
  while (neb->module->static_argtypes[i] != kEndList)
    i++;
  while (neb->module->dynamic_argtypes[j] != kEndList)
    j++;
  neb->args = malloc ((i > j ? i : j) * sizeof (void *));
  if (((neb->args) == NULL) && ((i > j ? i : j) != 0))
    {
      fatal ("Could not allocate nebuloid arguments");
    }

  while (i-- != 0)
    neb->args[i] = p_pop (neb->module->static_argtypes[i] == kString ?
			  kStacktype_string : kStacktype_num);

  neb->blackbox = (*neb->module->module_inst) (pid, neb->args);
  if ((int) neb->blackbox == -1)
    {
      fatal ("Module %s returned -1 on instantiation", modname);
    }
}

static void
op_dinst (void)
{
  nebuloid_t *neb, *pneb = NULL;
  char *name = p_popstr ();
  int glob = 0;

  if (verbose != 0)
    printf ("dinst ");

  neb = contexts[cur_ctx].nebuloids;
  while (neb != NULL)
    {
      if (strcmp (neb->name, name) == 0)
	break;
      pneb = neb;
      neb = neb->next;
    }
  if (neb == NULL)
    {
      neb = global_nebuloids;
      pneb = NULL;
      glob = 1;
      while (neb != NULL)
	{
	  if (strcmp (neb->name, name) == 0)
	    break;
	  pneb = neb;
	  neb = neb->next;
	}
    }

  if (neb == NULL)
    {
      fatal ("Attempted to dinst uninstantiated nebuloid \"%s\"", name);
    }

  (*neb->module->module_deinst) (neb->blackbox);

  if (pneb == NULL)
    {				/* front of list */
      if (glob != 0)
	global_nebuloids = neb->next;
      else
	contexts[cur_ctx].nebuloids = neb->next;
    }
  else				/* middle and/or end */
    pneb->next = neb->next;
  free (neb->args);
  free (neb);
}

static void
op_call (char **pca)
{
  int64_t a;
  void *tpc = *pca;

  a = *p_popnum ();
  *pca = segment_table[QTOL (a)].pcode - 1;	// incremented later
  if (verbose != 0)
    printf ("call/%s ", segment_table[QTOL (a)].name);
  p_push (kStacktype_ptr, tpc);
  p_push (kStacktype_barrier, NULL);
}

static void
op_alarm (void)
{
  int64_t p;
  if (verbose != 0)
    printf ("alarm ");
  p = *p_popnum ();
  insert_alarm (QTOL (p), *p_popnum ());
}

static void
op_signal (void)
{
  int64_t p;
  if (verbose != 0)
    printf ("signal ");
  p = *p_popnum ();
  add_signal (p, *p_popnum ());
}

static void
op_sigchild (void)
{
  if (verbose != 0)
    printf ("sigchild ");
  if (cur_ctx == -1)
    {
      fatal ("Attempted sigchild before setting context!");
    }

  contexts[cur_ctx].sigchild = segment_table[QTOL (*p_popnum ())].pcode;
}

static int
op_return (char **pca)
{
  int64_t p;

  if (verbose != 0)
    printf ("return ");
  p = *p_popnum ();
  if (p_trimstack () == 1)
    {
      return 1;
    }
  *pca = p_pop (kStacktype_ptr);
  p_push (kStacktype_num, &p);
  return 0;
}

static void
op_context (void)
{
  if (verbose != 0)
    printf ("context ");
  cur_ctx = QTOL (*p_popnum ());
}


static void
op_getpid (void)
{
  int64_t p;

  if (verbose != 0)
    printf ("getpid ");

  if (cur_ctx == -1)
    {
      fatal ("Attempted getpid before setting context");
    }
  p = LTOQ (contexts[cur_ctx].pid);
  p_push (kStacktype_num, &p);
}

static void
op_setpid (void)
{
  if (verbose != 0)
    printf ("setpid ");
  if (cur_ctx == -1)
    {
      fatal ("Attempted setpid before setting context");
    }
  contexts[cur_ctx].pid = (pid_t) QTOL (*p_popnum ());
}

static void
op_drop (void)
{
  if (verbose != 0)
    printf ("drop ");
  (void) p_pop (kStacktype_any);
}

static void
op_dup (void)
{
  if (verbose != 0)
    printf ("dup ");
  p_dup ();
}

// handle the if op code and move the pc to point to the next pcode
static void
op_if (char **pca)
{
  int64_t a;

  if (verbose != 0)
    printf ("if");
  a = *p_popnum ();
  if (verbose != 0)
    printf ("#%s", QTOSTR (a));
  if (QEQ (a, LTOQ (0)))
    {
      int ifdepth = 1;
      if (verbose != 0)
	printf ("-false ");
      while (ifdepth != 0)
	{
	  ++(*pca);
	  switch (**pca)
	    {
	    case pIF:
	      ifdepth++;
	      break;
	    case pFI:
	      ifdepth--;
	      break;
	    case '(':
	      while (**pca != '\0')
		(*pca)++;
	      break;
	    case pNUM:
	      (*pca) += sizeof (int64_t);
	      break;
	    default:
	      break;
	    }
	}
      if (verbose != 0)
	printf ("fi^1 ");
      a = LTOQ (1);
      p_push (kStacktype_num, &a);
    }
  else
    {
      if (verbose != 0)
	printf ("-true ");
    }
}

static void
op_fi (void)
{
  int64_t p;

  if (verbose != 0)
    printf ("fi^0 ");
  p = LTOQ (0);
  p_push (kStacktype_num, &p);
}

static void
op_for (char *pc)
{
  if (verbose != 0)
    printf ("for ");
  if (fordepth > 63)
    {
      fatal ("Loop nest depth (64) exceeded");
    }
  forstack[fordepth++] = pc;
}

static char *
op_rof (char *pc)
{
  char *ret = pc;

  if (verbose != 0)
    printf ("rof");
  if (QTOBOOL (*p_popnum ()) != 0)
    {
      if (verbose != 0)
	printf ("-loop ");
      ret = forstack[--fordepth] - 1;	/* incremented later */
    }
  else
    {
      if (verbose != 0)
	printf ("-pass ");
      --fordepth;
    }
  return ret;
}

static void
op_lt (void)
{
  int64_t a, p;

  a = *p_popnum ();
  p = LTOQ (QLT (a, *p_popnum ()));
  p_push (kStacktype_num, &p);
}

static void
op_gt (void)
{
  int64_t a, p;

  a = *p_popnum ();
  p = LTOQ (QGT (a, *p_popnum ()));
  p_push (kStacktype_num, &p);
}

static void
op_not (void)
{
  int64_t p;

  p = QNOT (*p_popnum ());
  p_push (kStacktype_num, &p);
}

static void
op_eq (void)
{
  int64_t p;

  p = LTOQ (QEQ (*p_popnum (), *p_popnum ()));
  p_push (kStacktype_num, &p);
}

static void
op_and (void)
{
  int64_t a, b, p;

  a = *p_popnum ();
  b = *p_popnum ();
  p = QAND (a, b);
  p_push (kStacktype_num, &p);
}

static void
op_or (void)
{
  int64_t a, b, p;

  a = *p_popnum ();
  b = *p_popnum ();
  p = QOR (a, b);
  p_push (kStacktype_num, &p);
}

static void
op_plus (void)
{
  int64_t p;

  p = QADD (*p_popnum (), *p_popnum ());
  p_push (kStacktype_num, &p);
}

static void
op_minus (void)
{
  int64_t a, p;

  a = *p_popnum ();
  p = QSUB (*p_popnum (), a);
  p_push (kStacktype_num, &p);
}

static void
op_mult (void)
{
  int64_t p;

  p = QMUL (*p_popnum (), *p_popnum ());
  p_push (kStacktype_num, &p);
}

static void
op_div (void)
{
  int64_t a, p;

  a = *p_popnum ();
  p = QDIV (*p_popnum (), a);
  p_push (kStacktype_num, &p);
}

static void
op_neg (void)
{
  int64_t p;

  p = QNEG (*p_popnum ());
  p_push (kStacktype_num, &p);
}

static char *
op_num (char *pc)
{

  pc++;
  if (verbose != 0)
    printf ("#%s ", QTOSTR (*(int64_t *) pc));
  p_push (kStacktype_num, pc);
  pc += sizeof (int64_t) - 1;
  return pc;
}

static char *
op_paren (char *pc)
{
  pc++;
  if (verbose != 0)
    printf ("(%s", pc);
  p_push (kStacktype_string, pc);
  while (*pc != '\0')
    pc++;
  if (verbose != 0)
    printf (") ");
  return pc;
}
