/* support routines for interpreted instructions
   Copyright (C) 1992, 1993 Free Software Foundation, Inc.

This file is part of Z8KSIM

Z8KSIM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

Z8KSIM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Z8KZIM; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "config.h"

#include <ansidecl.h>
#include <signal.h>
#include <errno.h>

#include "tm.h"
#include "sim.h"
#include "mem.h"
#include <stdio.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIMES_H
#include <sys/times.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include "gdb/callback.h"
#include "gdb/remote-sim.h"
#include "syscall.h"

static int get_now PARAMS ((void));
static int now_persec PARAMS ((void));
static int put_long PARAMS ((sim_state_type * context, int ptr, int value));
static int put_short PARAMS ((sim_state_type * context, int ptr, int value));

int sim_z8001_mode;

static int
get_now ()
{
#ifdef HAVE_SYS_TIMES_H
  struct tms b;

  times (&b);
  return b.tms_utime + b.tms_stime;
#else
  return time (0);
#endif
}

static int
now_persec ()
{
  return 50;
}


/* #define LOG /* define this to print instruction use counts */

#ifdef __GNUC__
#define INLINE __inline__
#include "inlines.h"
#else
#include "inlines.h"
#endif

/* This holds the entire cpu context */
static sim_state_type the_state;

int
fail (context, dummy)
     sim_state_type *context;
     int dummy;
{
  context->exception = SIM_BAD_INST;
  return 1;
}

void
sfop_bad1 (context)
     sim_state_type *context;
{
  context->exception
    = SIM_BAD_INST;
}

void
bfop_bad1 (context)
     sim_state_type *context;
{
  context->exception
    = SIM_BAD_INST;
}

void
fop_bad (context)
     sim_state_type *context;
{
  context->exception =
    SIM_BAD_INST;
}

/* Table of bit counts for all byte values */

char the_parity[256] =
{
  0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3,
  4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4,
  4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2,
  3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,
  4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4,
  5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
  3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2,
  3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
  4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
  6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5,
  5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6,
  7, 7, 8};


int read ();
int write ();
int open ();
int close ();
int open ();
int close ();

int link ();
int fstat ();

static int
put_short (context, ptr, value)
     sim_state_type *context;
     int ptr;
     int value;
{
  put_word_mem_da (context, ptr, value);
  return ptr + 2;
}

static int
put_long (context, ptr, value)
     sim_state_type *context;
     int
       ptr;
     int value;
{
  put_long_mem_da (context, ptr, value);
  return ptr + 4;
}

#define aptr(x) ((sitoptr(x)) + (char *)(context->memory))

static int args[3];
static int arg_index;		/* Translate a z8k system call into a host system call */
void
support_call (context, sc)
     sim_state_type *context;
     int sc;
{
  extern int errno;
  int ret;
  int retnext = 0;
  int fd;

  int olderrno = errno;
  errno = 0;
  switch (sc)
    {
    case SYS_ARG:
      args[arg_index++] = context->regs[0].word << 16 | context->regs[1].word;
      break;
    case SYS_exit:
      context->exception = SIM_DONE;
      ret = args[0];
      arg_index = 0;
      break;
    case SYS_close:
      ret = close ((int) (args[0]));
      arg_index = 0;
      break;
    case SYS_creat:
      ret = creat (aptr (args[0]), args[1]);
      arg_index = 0;
      break;
    case SYS_isatty:
      ret = isatty (args[0]);
      arg_index = 0;
      break;
    case SYS_open:
      ret = open (aptr (args[0]), args[1], args[2]);
      arg_index = 0;
      break;
    case SYS_lseek:
      ret = lseek (args[0], (off_t) args[1], args[2]);
      arg_index = 0;
      break;
    case SYS_read:
      ret = read (args[0], aptr (args[1]), args[2]);
      arg_index = 0;
      break;
    case SYS_write:
      ret = write (args[0],aptr (args[1]), args[2]);
      arg_index = 0;
      break;
    case SYS_time:
      {
	int dst = args[0];

	ret = time (0);
	if (dst)
	  {
	    put_long_mem_da (context,
			     sitoptr (dst), ret);
	  }
	retnext = ret;
	ret = retnext >> 16;
	arg_index = 0;
      }
      break;
    case SYS_fstat:
      {
	int buf;
	struct stat host_stat;
	fd = args[0];
	buf = sitoptr (args[1]);
	ret = fstat (fd, &host_stat);
	buf = put_short (context, buf, host_stat.st_dev);
	buf = put_short (context, buf, host_stat.st_ino);
	/* FIXME: Isn't mode_t 4 bytes?  */
	buf = put_short (context, buf, host_stat.st_mode);
	buf = put_short (context, buf, host_stat.st_nlink);
	buf = put_short (context, buf, host_stat.st_uid);
	buf = put_short (context, buf, host_stat.st_uid);
	buf = put_short (context, buf, host_stat.st_rdev);
	buf = put_long (context, buf, host_stat.st_size);
	buf = put_long (context, buf, host_stat.st_atime);
	arg_index = 0;
      } break;
    default:
    case SYS_link:
      context->exception = SIM_BAD_SYSCALL;
      arg_index = 0;
      break;
    }
  context->regs[2].word = ret;
  context->regs[3].word = retnext;
  context->regs[5].word = errno;


  /* support for the stdcall calling convention */
  context->regs[6].word = retnext;
  context->regs[7].word = ret;

  errno = olderrno;
}

#undef get_word_mem_da

int
get_word_mem_da (context, addr)
     sim_state_type *context;
     int addr;
{
  return (get_byte_mem_da (context, addr) << 8) | (get_byte_mem_da (context, addr + 1));

}

#undef get_word_reg
int 
get_word_reg (context, reg) sim_state_type
* context;
     int reg;
{
  return context->regs[reg].word;
}

#ifdef LOG
int log[64 * 1024];

#endif

void
tm_store_register (regno, value)
     int regno;
     int value;
{
  switch
    (regno)
    {
    case REG_PC:
      the_state.sometimes_pc = value;
      break;

    default:
      put_word_reg (&the_state, regno, value);
    }
}

void
swap_long (buf, val)
     char *buf;
     int val;
{
  buf[0] = val >> 24;
  buf[1] = val >> 16;
  buf[2] = val >> 8;
  buf[3] = val >> 0;
}

void
swap_word (buf, val)
     char *buf;
     int val;
{
  buf[0] = val >> 8;
  buf[1] = val >> 0;
}

void
tm_fetch_register (regno, buf)
     int regno;
     char *buf;
{
  switch
    (regno)
    {
    case REG_CYCLES:
      swap_long (buf, the_state.cycles);
      break;
    case REG_INSTS:
      swap_long (buf, the_state.insts);
      break;
      case
    REG_TIME:
      swap_long (buf, the_state.ticks);
      break;
    case REG_PC:
      swap_long (buf, the_state.sometimes_pc);
      break;
    case REG_SP:
      {
	if (sim_z8001_mode)
	  {
	    swap_long (buf, get_long_reg (&the_state, 14));
	  }
	else
	  {
	    swap_long (buf, get_word_reg (&the_state, 15));
	  }
      }
      break;
      case
    REG_FP:
      {
	if (sim_z8001_mode)
	  {
	    swap_long (buf, get_long_reg
		       (&the_state, 10));
	  }
	else
	  {
	    swap_long (buf,
		       get_word_reg (&the_state, 10));
	  }
      }
      break;
    default:
      {
	swap_word (buf,
		   get_word_reg (&the_state, regno));
      }
    }
}

void
tm_resume (step)
     int step;
{
  int now = get_now ();
  struct op_info
   *p;
  int word;
  int pc;
  extern int (*(sfop_table[])) ();
  extern int (*(bfop_table[])) ();
  int (*((*table))) ();
  sim_state_type *context = &the_state;

  if (step)
    {
      context->exception = SIM_SINGLE_STEP;
    }
  else
    {
      context->exception = 0;
    }

  pc = context->sometimes_pc;
  if (sim_z8001_mode)
    {
      table = bfop_table;
      pc = MAP_PHYSICAL_TO_LOGICAL (pc);
    }
  else
    {
      table = sfop_table;
    }


  do
    {
      word = get_word_mem_da (context, pc);
      p = op_info_table + word;

#ifdef LOG
      log[word]++;
#endif
      pc = table[p->exec] (context, pc, word);
      context->insts++;

    }
  while (!context->exception);



  context->sometimes_pc = MAP_LOGICAL_TO_PHYSICAL (pc);
  context->ticks += get_now () - now;
}

int
tm_signal ()
{
  return the_state.exception;
}

void
tm_info_print (x)
     sim_state_type *x;
{
  double timetaken = (double) x->ticks / (double) now_persec ();
  double virttime = x->cycles / 4.0e6;

  printf ("instructions executed            : %9d\n", x->insts);
  printf ("cycles counted                   : %9d \n", x->cycles);
  printf ("cycles / inst                    : %9.1f \n", (double) x->cycles / (double) x->insts);
  printf ("virtual time taked (at 4 Mhz)    : %9.1f \n", virttime);
  printf ("real time taken                  : %9.1f \n", timetaken);

  if (timetaken)
    {
      printf ("virtual instructions per second  : %9.1f\n", x->insts / timetaken);
      printf ("emulation speed                  : %9.1f%%\n", virttime / timetaken * 100.0);
    }
#ifdef LOG
  {
    extern int quick[];

    for (i = 0; quick[i]; i++)
      {
	log[quick[i]] += 100000;
      }
  }

  for (i = 0; i < 64 * 1024; i++)
    {
      if (log[i])
	{
	  printf ("			/*%7d*/ 0x%x,\n", log[i], i);
	}
    }
#endif

}

int
sim_trace (sd)
     SIM_DESC sd;
{
  int i;
  char buffer[10];
  int r;

  printf ("\n");
  for (r = 0; r < 16; r++)
    {
      int m;

      printf ("r%2d", r);
      printf ("=%04x ", get_word_reg (&the_state,
				      r));
      for (m = -4; m < 8; m++)
	{
	  if (m == 0)
	    printf (">");
	  printf ("%04x ",
		  get_word_mem_da (&the_state, (0xfffe & get_word_reg (&the_state, r)) + m * 2));
	}
      printf ("\n");
    }

  printf ("\n");
  printf ("%9d %9d %08x ", the_state.cycles,
	  the_state.insts, the_state.sometimes_pc);

  for (i = 0; i < 6; i++)
    {
      buffer[i] = get_byte_mem_da (&the_state,
				   the_state.sometimes_pc + i);
    }

  print_insn_z8001 (the_state.sometimes_pc, buffer, stdout);
  printf
    ("\n");
  tm_resume (1);
  if (the_state.exception != SIM_SINGLE_STEP)
    return 1;
  return 0;
}

void
tm_state (x)
     sim_state_type *x;
{
  *x = the_state;
}

void
tm_exception (x)
     int x;
{
  the_state.exception = x;
}

int
tm_read_byte (x)
     int x;
{
  x &= 0x3f00ffff;
  return sim_read_byte (&the_state, x);
}

void
tm_write_byte (x, y)
     int x, y;
{
  x &= 0x3f00ffff;
  sim_write_byte (&the_state, x, y);
}

#define SIGN(x) ((x) & MASK)
normal_flags_32(context,d,sa,sb,sub)
sim_state_type *context;
unsigned int d;
unsigned int sa;
unsigned int sb;
unsigned int sub;
{
#undef MASK
#define MASK (1<<31)
  context->broken_flags = 0;	
  if (sub)                        
    PSW_CARRY = sa < sb; 		
  else 				
    PSW_CARRY = d < sa; 		
  if (sub)
    PSW_OVERFLOW = (SIGN(sa) != SIGN(sb)) && (SIGN(d) == SIGN(sb));
  else
    PSW_OVERFLOW = (SIGN(sa) == SIGN(sb)) && (SIGN(d) != SIGN(sb));

  PSW_SIGN = ((int)d) <0; 
  PSW_ZERO = d == 0;
}

normal_flags_16(context,d,sal,sbl,sub)
sim_state_type *context;
unsigned  int d;
unsigned  int sal;
unsigned  int sbl;
unsigned short int sub;
{
  unsigned short sa = sal;
  unsigned short sb = sbl;
#undef MASK
#define MASK (1<<15)
  context->broken_flags = 0;	
  if (sub)                        
    PSW_CARRY = sal < sbl; 		
  else 			
    PSW_CARRY = (d & 0x10000) != 0;

  if (sub)
    PSW_OVERFLOW = (SIGN(sa) != SIGN(sb)) && (SIGN(d) == SIGN(sb));
  else
    PSW_OVERFLOW = (SIGN(sa) == SIGN(sb)) && (SIGN(d) != SIGN(sb));

  PSW_SIGN = ((short int)d) <0; 
  PSW_ZERO = ((short)d) == 0;
}

normal_flags_8(context,d,sa,sb,sub)
sim_state_type *context;
unsigned char d;
unsigned char sa;
unsigned char sb;
unsigned char sub;
{
#undef MASK
#define MASK (1<<7)
  context->broken_flags = 0;	
  if (sub)                        
    PSW_CARRY = sa < sb; 		
  else 				
    PSW_CARRY = d < sa; 		
  if (sub)
    PSW_OVERFLOW = (SIGN(sa) != SIGN(sb)) && (SIGN(d) == SIGN(sb));
  else
    PSW_OVERFLOW = (SIGN(sa) == SIGN(sb)) && (SIGN(d) != SIGN(sb));
  PSW_SIGN = ((char)d) <0; 
  PSW_ZERO = d == 0;
}


static int
is_cond_true (context, c)
     sim_state_type *context;
     int c;
{
  switch (c)
    {
    case T:
      return 1;
    case F:
      return 0;			/* F */
    case LE:
      return (PSW_ZERO | (PSW_SIGN ^ PSW_OVERFLOW)) & 1;	/*LE */
    case GT:
      return (~(PSW_ZERO | (PSW_SIGN ^ PSW_OVERFLOW))) & 1;	/*GT */
    case 0x5:
      return (PSW_SIGN & 1);	/* sign */
    case 0xd:
      return (~(PSW_SIGN)) & 1;	/* not sign */
    case 0x3:
      return ((PSW_CARRY | PSW_ZERO) & 1);	/* ule*/
    case UGT:
      return ((~(PSW_CARRY | PSW_ZERO)) & 1);	/* ugt */
    case 0x4:
      return (PSW_OVERFLOW & 1);/* overflow */
    case 0xc:
      return (~(PSW_OVERFLOW)) & 1;	/* not overflow */
    case LT:
      return (PSW_SIGN ^ PSW_OVERFLOW) & 1;	/* LT */
    case GE:
      return (~(PSW_SIGN ^ PSW_OVERFLOW)) & 1;	/* GE */
    case EQ:
      return (PSW_ZERO) & 1;	/* zero */
    case NE:
      return ((~PSW_ZERO) & 1);	/* not zero */
    case 0x7:
      return (PSW_CARRY) & 1;	/* carry */
    case 0xf:
      return (~PSW_CARRY) & 1;	/* not carry */
    default:
      abort ();
    }
}

int
COND (context, c)
     sim_state_type *context;
     int c;
{
  if (c == 8)
    return 1;

  /* We can calculate what the flags would have been by
     looking at the src and dst and size of the operation */

  if (context->broken_flags)
    {
      int slow = 0;
      int size;
      int dst;
      int srca;
      int srcb;
      int mask;
      int ans;

      /* see if we can short-cut the nasty flag calcs */

      switch (size = context->size)
	{
	 default:
	  abort();
	  return 0;
	case 8:
	  srca = (char) (context->srca);
	  srcb = (char) (context->srcb);
	  dst = (char) (context->dst);
	  mask = 0xff;
	  break;
	case 16:
	  srca = (short) (context->srca);
	  srcb = (short) (context->srcb);
	  dst = (short) (context->dst);
	  mask = 0xffff;
	  break;
	case 32:
	  srca = (long) (context->srca);
	  srcb = (long) (context->srcb);
	  dst = (long) (context->dst);
	  mask = 0xffffffff;
	  break;
	}

      switch (c)
	{
	case T:
	  return 1;
	case F:
	  return 0;
	case EQ:
	  return !dst;
	case NE:
	  return dst;
	case GT:
	  ans = ((dst)) > 0;
	  if (slow)
	    {
	      if (is_cond_true (context, c) != ans)
		abort ();
	    }
	  return ans;
	case LE:
	  ans = ((dst)) <= 0;
	  if (slow)
	    {
	      if (is_cond_true (context, c) != ans)
		abort ();
	    }
	  return ans;
	case GE:
	  ans = ((dst)) >= 0;
	  if (slow)
	    {
	      if (is_cond_true (context, c) != ans)
		abort ();
	    }
	  return ans;
	case LT:
	  ans = ((dst)) < 0;
	  if (slow)
	    {
	      if (is_cond_true (context, c) != ans)
		abort ();
	    }
	  return ans;
	default:
	  break;
	}

      /* Can't fake it, we'll have to work out the flags the
         hard way */

      makeflags (context, mask);
    }

  /* don't know how to fake a test, inspect the flags
     the hard way */

  return is_cond_true (context, c);
}