/*
**  This file is part of Zterp, and is
**  Copyright 1992, 1993 Charles Hannum
*/

#include <sys/types.h>
#include "main.h"
#include "variable.h"
#include "jump.h"

#define FUNCTION 0x100
#define PROCEDURE 0

void
return_or_jump_true ()
{
  return_or_jump (1);
}

void
return_or_jump (a)
  /* jump */
  uword a;
{
  sword b = get_byte_pc ();
  if (b & 0x80)
  {
    ++a;
    b &= 0x7f;
  }
  if (b & 0x40)
    b &= 0x3f;
  else
  {
    b = (b << 8) | get_byte_pc ();
    if (b & 0x2000)
      b |= 0xc000;
  }
  if (--a)
    switch (b)
    {
      case 0:
      case 1:
	ret (b);
	break;
      default:
	set_pc (pc + b - 2);
	break;
    }
}

void
je (args, argp)
  /* jump if not equal */
  int args;
  uword *argp;
{
  while (--args)
    if (argp[args] == argp[0])
    {
      return_or_jump (1);
      return;
    }
  return_or_jump (0);
}

void
jl (a, b)
  /* jump if less than or equal */
  sword a, b;
{
  return_or_jump (a < b);
}

void
jg (a, b)
  /* jump if greater than or equal */
  sword a, b;
{
  return_or_jump (a > b);
}

void
djl (n, b)
  /* decrement and jump if greater than or equal */
  uword n;
  sword b;
{
  sword x = fetch_variable_nopop (n);
  store_variable_nopush (n, --x);
  return_or_jump (x < b);
}

void
ijg (n, b)
  /* increment and jump if less than or equal */
  uword n;
  sword b;
{
  sword x = fetch_variable_nopop (n);
  store_variable_nopush (n, ++x);
  return_or_jump (x > b);
}

void
test (a, b)
  /* jump if bits set */
  uword a, b;
{
  return_or_jump (! (b & ~a));
}

static void
call (type, args, argp)
  uword type;
  int args;
  uword *argp;
{
  if (! argp[0])
  {
    if (type & FUNCTION)
      store_variable_push (0);
  }
  else
  {
    int n;
    --args;
    *(sp++) = (uword) (((int) (pc-game)) >> 9);
    *(sp++) = (uword) (((int) (pc-game)) & 0x1ff);
    *(sp++) = type | (zbyte) (args);
    *(sp) = (uword) (fp - stack);
    fp = sp++;
    set_pc (far_address (argp[0]));
    n = get_byte_pc ();
    if (zil_version < 5)
    {
      while (args--)
      {
        if (! n--)
	  return;
        *(sp++) = *++argp;
        (void) get_word_pc ();
      }
      while (n--)
        *(sp++) = get_word_pc ();
    }
    else
    {
      while (args--)
      {
	if (! n--)
	  return;
	*(sp++) = *++argp;
      }
      while (n--)
	*(sp++) = 0;
    }
  }
}

void
jz (x)
  uword x;
{
  return_or_jump (x == 0);
}

void
call_function_1 (a)
  uword a;
{
  call (FUNCTION, 1, &a);
}

void
call_procedure_1 (a)
  uword a;
{
  call (PROCEDURE, 1, &a);
}

void
ret (x)
  uword x;
{
  uword type;
  sp = fp;
  fp = *sp + stack;
  type = *(--sp);
  set_pc ((zpointer) (game + (sp[-1] | (sp[-2] << 9))));
  sp -= 2;
  if (type & FUNCTION)
    store_variable_push (x);
}

void
jmp (a)
  sword a;
{
  set_pc (pc + ((int) a) - 2);
}

void
ret_true ()
{
  ret (1);
}

void
ret_false ()
{
  ret (0);
}

void
call_function (args, argp)
  int args;
  uword *argp;
{
  call (FUNCTION, args, argp);
}

void
call_procedure (args, argp)
  int args;
  uword *argp;
{
  call (PROCEDURE, args, argp);
}

void
num_args (n)
  uword n;
{
  return_or_jump ((zbyte) (fp[-1]) >= n);
}
