/* rx.c --- opcode semantics for stand-alone RX simulator.

Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.

This file is part of the GNU simulators.

This program 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 3 of the License, or
(at your option) any later version.

This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include "opcode/rx.h"
#include "cpu.h"
#include "mem.h"
#include "syscalls.h"
#include "fpu.h"
#include "err.h"
#include "misc.h"

#ifdef CYCLE_STATS
static const char * id_names[] = {
  "RXO_unknown",
  "RXO_mov",	/* d = s (signed) */
  "RXO_movbi",	/* d = [s,s2] (signed) */
  "RXO_movbir",	/* [s,s2] = d (signed) */
  "RXO_pushm",	/* s..s2 */
  "RXO_popm",	/* s..s2 */
  "RXO_xchg",	/* s <-> d */
  "RXO_stcc",	/* d = s if cond(s2) */
  "RXO_rtsd",	/* rtsd, 1=imm, 2-0 = reg if reg type */

  /* These are all either d OP= s or, if s2 is set, d = s OP s2.  Note
     that d may be "None".  */
  "RXO_and",
  "RXO_or",
  "RXO_xor",
  "RXO_add",
  "RXO_sub",
  "RXO_mul",
  "RXO_div",
  "RXO_divu",
  "RXO_shll",
  "RXO_shar",
  "RXO_shlr",

  "RXO_adc",	/* d = d + s + carry */
  "RXO_sbb",	/* d = d - s - ~carry */
  "RXO_abs",	/* d = |s| */
  "RXO_max",	/* d = max(d,s) */
  "RXO_min",	/* d = min(d,s) */
  "RXO_emul",	/* d:64 = d:32 * s */
  "RXO_emulu",	/* d:64 = d:32 * s (unsigned) */

  "RXO_rolc",	/* d <<= 1 through carry */
  "RXO_rorc",	/* d >>= 1 through carry*/
  "RXO_rotl",	/* d <<= #s without carry */
  "RXO_rotr",	/* d >>= #s without carry*/
  "RXO_revw",	/* d = revw(s) */
  "RXO_revl",	/* d = revl(s) */
  "RXO_branch",	/* pc = d if cond(s) */
  "RXO_branchrel",/* pc += d if cond(s) */
  "RXO_jsr",	/* pc = d */
  "RXO_jsrrel",	/* pc += d */
  "RXO_rts",
  "RXO_nop",
  "RXO_nop2",
  "RXO_nop3",

  "RXO_scmpu",
  "RXO_smovu",
  "RXO_smovb",
  "RXO_suntil",
  "RXO_swhile",
  "RXO_smovf",
  "RXO_sstr",

  "RXO_rmpa",
  "RXO_mulhi",
  "RXO_mullo",
  "RXO_machi",
  "RXO_maclo",
  "RXO_mvtachi",
  "RXO_mvtaclo",
  "RXO_mvfachi",
  "RXO_mvfacmi",
  "RXO_mvfaclo",
  "RXO_racw",

  "RXO_sat",	/* sat(d) */
  "RXO_satr",

  "RXO_fadd",	/* d op= s */
  "RXO_fcmp",
  "RXO_fsub",
  "RXO_ftoi",
  "RXO_fmul",
  "RXO_fdiv",
  "RXO_round",
  "RXO_itof",

  "RXO_bset",	/* d |= (1<<s) */
  "RXO_bclr",	/* d &= ~(1<<s) */
  "RXO_btst",	/* s & (1<<s2) */
  "RXO_bnot",	/* d ^= (1<<s) */
  "RXO_bmcc",	/* d<s> = cond(s2) */

  "RXO_clrpsw",	/* flag index in d */
  "RXO_setpsw",	/* flag index in d */
  "RXO_mvtipl",	/* new IPL in s */

  "RXO_rtfi",
  "RXO_rte",
  "RXO_rtd",	/* undocumented */
  "RXO_brk",
  "RXO_dbt",	/* undocumented */
  "RXO_int",	/* vector id in s */
  "RXO_stop",
  "RXO_wait",

  "RXO_sccnd",	/* d = cond(s) ? 1 : 0 */
};

static const char * optype_names[] = {
  " -  ",
  "#Imm",	/* #addend */
  " Rn ",	/* Rn */
  "[Rn]",	/* [Rn + addend] */
  "Ps++",	/* [Rn+] */
  "--Pr",	/* [-Rn] */
  " cc ",	/* eq, gtu, etc */
  "Flag",	/* [UIOSZC] */
  "RbRi"	/* [Rb + scale * Ri] */
};

#define N_RXO (sizeof(id_names)/sizeof(id_names[0]))
#define N_RXT (sizeof(optype_names)/sizeof(optype_names[0]))
#define N_MAP 30

static unsigned long long benchmark_start_cycle;
static unsigned long long benchmark_end_cycle;

static int op_cache[N_RXT][N_RXT][N_RXT];
static int op_cache_rev[N_MAP];
static int op_cache_idx = 0;

static int
op_lookup (int a, int b, int c)
{
  if (op_cache[a][b][c])
    return op_cache[a][b][c];
  op_cache_idx ++;
  if (op_cache_idx >= N_MAP)
    {
      printf("op_cache_idx exceeds %d\n", N_MAP);
      exit(1);
    }
  op_cache[a][b][c] = op_cache_idx;
  op_cache_rev[op_cache_idx] = (a<<8) | (b<<4) | c;
  return op_cache_idx;
}

static char *
op_cache_string (int map)
{
  static int ci;
  static char cb[5][20];
  int a, b, c;

  map = op_cache_rev[map];
  a = (map >> 8) & 15;
  b = (map >> 4) & 15;
  c = (map >> 0) & 15;
  ci = (ci + 1) % 5;
  sprintf(cb[ci], "%s %s %s", optype_names[a], optype_names[b], optype_names[c]);
  return cb[ci];
}

static unsigned long long cycles_per_id[N_RXO][N_MAP];
static unsigned long long times_per_id[N_RXO][N_MAP];
static unsigned long long memory_stalls;
static unsigned long long register_stalls;
static unsigned long long branch_stalls;
static unsigned long long branch_alignment_stalls;
static unsigned long long fast_returns;

static unsigned long times_per_pair[N_RXO][N_MAP][N_RXO][N_MAP];
static int prev_opcode_id = RXO_unknown;
static int po0;

#define STATS(x) x

#else
#define STATS(x)
#endif /* CYCLE_STATS */


#ifdef CYCLE_ACCURATE

static int new_rt = -1;

/* Number of cycles to add if an insn spans an 8-byte boundary.  */
static int branch_alignment_penalty = 0;

#endif

static int running_benchmark = 1;

#define tprintf if (trace && running_benchmark) printf

jmp_buf decode_jmp_buf;
unsigned int rx_cycles = 0;

#ifdef CYCLE_ACCURATE
/* If nonzero, memory was read at some point and cycle latency might
   take effect.  */
static int memory_source = 0;
/* If nonzero, memory was written and extra cycles might be
   needed.  */
static int memory_dest = 0;

static void
cycles (int throughput)
{
  tprintf("%d cycles\n", throughput);
  regs.cycle_count += throughput;
}

/* Number of execution (E) cycles the op uses.  For memory sources, we
   include the load micro-op stall as two extra E cycles.  */
#define E(c) cycles (memory_source ? c + 2 : c)
#define E1 cycles (1)
#define E2 cycles (2)
#define EBIT cycles (memory_source ? 2 : 1)

/* Check to see if a read latency must be applied for a given register.  */
#define RL(r) \
  if (regs.rt == r )							\
    {									\
      tprintf("register %d load stall\n", r);				\
      regs.cycle_count ++;						\
      STATS(register_stalls ++);					\
      regs.rt = -1;							\
    }

#define RLD(r)					\
  if (memory_source)				\
    {						\
      tprintf ("Rt now %d\n", r);		\
      new_rt = r;				\
    }

static int
lsb_count (unsigned long v, int is_signed)
{
  int i, lsb;
  if (is_signed && (v & 0x80000000U))
    v = (unsigned long)(long)(-v);
  for (i=31; i>=0; i--)
    if (v & (1 << i))
      {
	/* v is 0..31, we want 1=1-2, 2=3-4, 3=5-6, etc. */
	lsb = (i + 2) / 2;
	return lsb;
      }
  return 0;
}

static int
divu_cycles(unsigned long num, unsigned long den)
{
  int nb = lsb_count (num, 0);
  int db = lsb_count (den, 0);
  int rv;

  if (nb < db)
    rv = 2;
  else
    rv = 3 + nb - db;
  E (rv);
  return rv;
}

static int
div_cycles(long num, long den)
{
  int nb = lsb_count ((unsigned long)num, 1);
  int db = lsb_count ((unsigned long)den, 1);
  int rv;

  if (nb < db)
    rv = 3;
  else
    rv = 5 + nb - db;
  E (rv);
  return rv;
}

#else /* !CYCLE_ACCURATE */

#define cycles(t)
#define E(c)
#define E1
#define E2
#define EBIT
#define RL(r)
#define RLD(r)

#define divu_cycles(n,d)
#define div_cycles(n,d)

#endif /* else CYCLE_ACCURATE */

static int size2bytes[] = {
  4, 1, 1, 1, 2, 2, 2, 3, 4
};

typedef struct {
  unsigned long dpc;
} RX_Data;

#define rx_abort() _rx_abort(__FILE__, __LINE__)
static void
_rx_abort (const char *file, int line)
{
  if (strrchr (file, '/'))
    file = strrchr (file, '/') + 1;
  fprintf(stderr, "abort at %s:%d\n", file, line);
  abort();
}

static unsigned char *get_byte_base;
static RX_Opcode_Decoded **decode_cache_base;
static SI get_byte_page;

void
reset_decoder (void)
{
  get_byte_base = 0;
  decode_cache_base = 0;
  get_byte_page = 0;
}

static inline void
maybe_get_mem_page (SI tpc)
{
  if (((tpc ^ get_byte_page) & NONPAGE_MASK) || enable_counting)
    {
      get_byte_page = tpc & NONPAGE_MASK;
      get_byte_base = rx_mem_ptr (get_byte_page, MPA_READING) - get_byte_page;
      decode_cache_base = rx_mem_decode_cache (get_byte_page) - get_byte_page;
    }
}

/* This gets called a *lot* so optimize it.  */
static int
rx_get_byte (void *vdata)
{
  RX_Data *rx_data = (RX_Data *)vdata;
  SI tpc = rx_data->dpc;

  /* See load.c for an explanation of this.  */
  if (rx_big_endian)
    tpc ^= 3;

  maybe_get_mem_page (tpc);

  rx_data->dpc ++;
  return get_byte_base [tpc];
}

static int
get_op (const RX_Opcode_Decoded *rd, int i)
{
  const RX_Opcode_Operand *o = rd->op + i;
  int addr, rv = 0;

  switch (o->type)
    {
    case RX_Operand_None:
      rx_abort ();

    case RX_Operand_Immediate:	/* #addend */
      return o->addend;

    case RX_Operand_Register:	/* Rn */
      RL (o->reg);
      rv = get_reg (o->reg);
      break;

    case RX_Operand_Predec:	/* [-Rn] */
      put_reg (o->reg, get_reg (o->reg) - size2bytes[o->size]);
      /* fall through */
    case RX_Operand_Postinc:	/* [Rn+] */
    case RX_Operand_Indirect:	/* [Rn + addend] */
    case RX_Operand_TwoReg:	/* [Rn + scale * R2] */
#ifdef CYCLE_ACCURATE
      RL (o->reg);
      if (o->type == RX_Operand_TwoReg)
	RL (rd->op[2].reg);
      regs.rt = -1;
      if (regs.m2m == M2M_BOTH)
	{
	  tprintf("src memory stall\n");
#ifdef CYCLE_STATS
	  memory_stalls ++;
#endif
	  regs.cycle_count ++;
	  regs.m2m = 0;
	}

      memory_source = 1;
#endif

      if (o->type == RX_Operand_TwoReg)
	addr = get_reg (o->reg) * size2bytes[rd->size] + get_reg (rd->op[2].reg);
      else
	addr = get_reg (o->reg) + o->addend;

      switch (o->size)
	{
	case RX_AnySize:
	  rx_abort ();

	case RX_Byte: /* undefined extension */
	case RX_UByte:
	case RX_SByte:
	  rv = mem_get_qi (addr);
	  break;

	case RX_Word: /* undefined extension */
	case RX_UWord:
	case RX_SWord:
	  rv = mem_get_hi (addr);
	  break;

	case RX_3Byte:
	  rv = mem_get_psi (addr);
	  break;

	case RX_Long:
	  rv = mem_get_si (addr);
	  break;
	}

      if (o->type == RX_Operand_Postinc)
	put_reg (o->reg, get_reg (o->reg) + size2bytes[o->size]);

      break;

    case RX_Operand_Condition:	/* eq, gtu, etc */
      return condition_true (o->reg);

    case RX_Operand_Flag:	/* [UIOSZC] */
      return (regs.r_psw & (1 << o->reg)) ? 1 : 0;
    }

  /* if we've gotten here, we need to clip/extend the value according
     to the size.  */
  switch (o->size)
    {
    case RX_AnySize:
      rx_abort ();

    case RX_Byte: /* undefined extension */
      rv |= 0xdeadbe00; /* keep them honest */
      break;

    case RX_UByte:
      rv &= 0xff;
      break;

    case RX_SByte:
      rv = sign_ext (rv, 8);
      break;

    case RX_Word: /* undefined extension */
      rv |= 0xdead0000; /* keep them honest */
      break;

    case RX_UWord:
      rv &=  0xffff;
      break;

    case RX_SWord:
      rv = sign_ext (rv, 16);
      break;

    case RX_3Byte:
      rv &= 0xffffff;
      break;

    case RX_Long:
      break;
    }
  return rv;
}

static void
put_op (const RX_Opcode_Decoded *rd, int i, int v)
{
  const RX_Opcode_Operand *o = rd->op + i;
  int addr;

  switch (o->size)
    {
    case RX_AnySize:
      if (o->type != RX_Operand_Register)
	rx_abort ();
      break;

    case RX_Byte: /* undefined extension */
      v |= 0xdeadbe00; /* keep them honest */
      break;

    case RX_UByte:
      v &= 0xff;
      break;

    case RX_SByte:
      v = sign_ext (v, 8);
      break;

    case RX_Word: /* undefined extension */
      v |= 0xdead0000; /* keep them honest */
      break;

    case RX_UWord:
      v &=  0xffff;
      break;

    case RX_SWord:
      v = sign_ext (v, 16);
      break;

    case RX_3Byte:
      v &= 0xffffff;
      break;

    case RX_Long:
      break;
    }

  switch (o->type)
    {
    case RX_Operand_None:
      /* Opcodes like TST and CMP use this.  */
      break;

    case RX_Operand_Immediate:	/* #addend */
    case RX_Operand_Condition:	/* eq, gtu, etc */
      rx_abort ();

    case RX_Operand_Register:	/* Rn */
      put_reg (o->reg, v);
      RLD (o->reg);
      break;

    case RX_Operand_Predec:	/* [-Rn] */
      put_reg (o->reg, get_reg (o->reg) - size2bytes[o->size]);
      /* fall through */
    case RX_Operand_Postinc:	/* [Rn+] */
    case RX_Operand_Indirect:	/* [Rn + addend] */
    case RX_Operand_TwoReg:	/* [Rn + scale * R2] */

#ifdef CYCLE_ACCURATE
      if (regs.m2m == M2M_BOTH)
	{
	  tprintf("dst memory stall\n");
	  regs.cycle_count ++;
#ifdef CYCLE_STATS
	  memory_stalls ++;
#endif
	  regs.m2m = 0;
	}
      memory_dest = 1;
#endif

      if (o->type == RX_Operand_TwoReg)
	addr = get_reg (o->reg) * size2bytes[rd->size] + get_reg (rd->op[2].reg);
      else
	addr = get_reg (o->reg) + o->addend;

      switch (o->size)
	{
	case RX_AnySize:
	  rx_abort ();

	case RX_Byte: /* undefined extension */
	case RX_UByte:
	case RX_SByte:
	  mem_put_qi (addr, v);
	  break;

	case RX_Word: /* undefined extension */
	case RX_UWord:
	case RX_SWord:
	  mem_put_hi (addr, v);
	  break;

	case RX_3Byte:
	  mem_put_psi (addr, v);
	  break;

	case RX_Long:
	  mem_put_si (addr, v);
	  break;
	}

      if (o->type == RX_Operand_Postinc)
	put_reg (o->reg, get_reg (o->reg) + size2bytes[o->size]);

      break;

    case RX_Operand_Flag:	/* [UIOSZC] */
      if (v)
	regs.r_psw |= (1 << o->reg);
      else
	regs.r_psw &= ~(1 << o->reg);
      break;
    }
}

#define PD(x) put_op (opcode, 0, x)
#define PS(x) put_op (opcode, 1, x)
#define PS2(x) put_op (opcode, 2, x)
#define GD() get_op (opcode, 0)
#define GS() get_op (opcode, 1)
#define GS2() get_op (opcode, 2)
#define DSZ() size2bytes[opcode->op[0].size]
#define SSZ() size2bytes[opcode->op[0].size]
#define S2SZ() size2bytes[opcode->op[0].size]

/* "Universal" sources.  */
#define US1() ((opcode->op[2].type == RX_Operand_None) ? GD() : GS())
#define US2() ((opcode->op[2].type == RX_Operand_None) ? GS() : GS2())

static void
push(int val)
{
  int rsp = get_reg (sp);
  rsp -= 4;
  put_reg (sp, rsp);
  mem_put_si (rsp, val);
}

/* Just like the above, but tag the memory as "pushed pc" so if anyone
   tries to write to it, it will cause an error.  */
static void
pushpc(int val)
{
  int rsp = get_reg (sp);
  rsp -= 4;
  put_reg (sp, rsp);
  mem_put_si (rsp, val);
  mem_set_content_range (rsp, rsp+3, MC_PUSHED_PC);
}

static int
pop()
{
  int rv;
  int rsp = get_reg (sp);
  rv = mem_get_si (rsp);
  rsp += 4;
  put_reg (sp, rsp);
  return rv;
}

static int
poppc()
{
  int rv;
  int rsp = get_reg (sp);
  if (mem_get_content_type (rsp) != MC_PUSHED_PC)
    execution_error (SIM_ERR_CORRUPT_STACK, rsp);
  rv = mem_get_si (rsp);
  mem_set_content_range (rsp, rsp+3, MC_UNINIT);
  rsp += 4;
  put_reg (sp, rsp);
  return rv;
}

#define MATH_OP(vop,c)				\
{ \
  umb = US2(); \
  uma = US1(); \
  ll = (unsigned long long) uma vop (unsigned long long) umb vop c; \
  tprintf ("0x%x " #vop " 0x%x " #vop " 0x%x = 0x%llx\n", uma, umb, c, ll); \
  ma = sign_ext (uma, DSZ() * 8);					\
  mb = sign_ext (umb, DSZ() * 8);					\
  sll = (long long) ma vop (long long) mb vop c; \
  tprintf ("%d " #vop " %d " #vop " %d = %lld\n", ma, mb, c, sll); \
  set_oszc (sll, DSZ(), (long long) ll > ((1 vop 1) ? (long long) b2mask[DSZ()] : (long long) -1)); \
  PD (sll); \
  E (1);    \
}

#define LOGIC_OP(vop) \
{ \
  mb = US2(); \
  ma = US1(); \
  v = ma vop mb; \
  tprintf("0x%x " #vop " 0x%x = 0x%x\n", ma, mb, v); \
  set_sz (v, DSZ()); \
  PD(v); \
  E (1); \
}

#define SHIFT_OP(val, type, count, OP, carry_mask)	\
{ \
  int i, c=0; \
  count = US2(); \
  val = (type)US1();				\
  tprintf("%lld " #OP " %d\n", val, count); \
  for (i = 0; i < count; i ++) \
    { \
      c = val & carry_mask; \
      val OP 1; \
    } \
  if (count) \
    set_oszc (val, 4, c); \
  PD (val); \
}

typedef union {
  int i;
  float f;
} FloatInt;

static inline int
float2int (float f)
{
  FloatInt fi;
  fi.f = f;
  return fi.i;
}

static inline float
int2float (int i)
{
  FloatInt fi;
  fi.i = i;
  return fi.f;
}

static int
fop_fadd (fp_t s1, fp_t s2, fp_t *d)
{
  *d = rxfp_add (s1, s2);
  return 1;
}

static int
fop_fmul (fp_t s1, fp_t s2, fp_t *d)
{
  *d = rxfp_mul (s1, s2);
  return 1;
}

static int
fop_fdiv (fp_t s1, fp_t s2, fp_t *d)
{
  *d = rxfp_div (s1, s2);
  return 1;
}

static int
fop_fsub (fp_t s1, fp_t s2, fp_t *d)
{
  *d = rxfp_sub (s1, s2);
  return 1;
}

#define FPPENDING() (regs.r_fpsw & (FPSWBITS_CE | (FPSWBITS_FMASK & (regs.r_fpsw << FPSW_EFSH))))
#define FPCLEAR() regs.r_fpsw &= FPSWBITS_CLEAR
#define FPCHECK() \
  if (FPPENDING()) \
    return do_fp_exception (opcode_pc)

#define FLOAT_OP(func) \
{ \
  int do_store;   \
  fp_t fa, fb, fc; \
  FPCLEAR(); \
  fb = GS (); \
  fa = GD (); \
  do_store = fop_##func (fa, fb, &fc); \
  tprintf("%g " #func " %g = %g %08x\n", int2float(fa), int2float(fb), int2float(fc), fc); \
  FPCHECK(); \
  if (do_store) \
    PD (fc);	\
  mb = 0; \
  if ((fc & 0x80000000UL) != 0) \
    mb |= FLAGBIT_S; \
  if ((fc & 0x7fffffffUL) == 0)			\
    mb |= FLAGBIT_Z; \
  set_flags (FLAGBIT_S | FLAGBIT_Z, mb); \
}

#define carry (FLAG_C ? 1 : 0)

static struct {
  unsigned long vaddr;
  const char *str;
  int signal;
} exception_info[] = {
  { 0xFFFFFFD0UL, "priviledged opcode", SIGILL },
  { 0xFFFFFFD4UL, "access violation", SIGSEGV },
  { 0xFFFFFFDCUL, "undefined opcode", SIGILL },
  { 0xFFFFFFE4UL, "floating point", SIGFPE }
};
#define EX_PRIVILEDGED	0
#define EX_ACCESS	1
#define EX_UNDEFINED	2
#define EX_FLOATING	3
#define EXCEPTION(n)  \
  return generate_exception (n, opcode_pc)

#define PRIVILEDGED() \
  if (FLAG_PM) \
    EXCEPTION (EX_PRIVILEDGED)

static int
generate_exception (unsigned long type, SI opcode_pc)
{
  SI old_psw, old_pc, new_pc;

  new_pc = mem_get_si (exception_info[type].vaddr);
  /* 0x00020000 is the value used to initialise the known
     exception vectors (see rx.ld), but it is a reserved
     area of memory so do not try to access it, and if the
     value has not been changed by the program then the
     vector has not been installed.  */
  if (new_pc == 0 || new_pc == 0x00020000)
    {
      if (rx_in_gdb)
	return RX_MAKE_STOPPED (exception_info[type].signal);

      fprintf(stderr, "Unhandled %s exception at pc = %#lx\n",
	      exception_info[type].str, (unsigned long) opcode_pc);
      if (type == EX_FLOATING)
	{
	  int mask = FPPENDING ();
	  fprintf (stderr, "Pending FP exceptions:");
	  if (mask & FPSWBITS_FV)
	    fprintf(stderr, " Invalid");
	  if (mask & FPSWBITS_FO)
	    fprintf(stderr, " Overflow");
	  if (mask & FPSWBITS_FZ)
	    fprintf(stderr, " Division-by-zero");
	  if (mask & FPSWBITS_FU)
	    fprintf(stderr, " Underflow");
	  if (mask & FPSWBITS_FX)
	    fprintf(stderr, " Inexact");
	  if (mask & FPSWBITS_CE)
	    fprintf(stderr, " Unimplemented");
	  fprintf(stderr, "\n");
	}
      return RX_MAKE_EXITED (1);
    }

  tprintf ("Triggering %s exception\n", exception_info[type].str);

  old_psw = regs.r_psw;
  regs.r_psw &= ~ (FLAGBIT_I | FLAGBIT_U | FLAGBIT_PM);
  old_pc = opcode_pc;
  regs.r_pc = new_pc;
  pushpc (old_psw);
  pushpc (old_pc);
  return RX_MAKE_STEPPED ();
}

void
generate_access_exception (void)
{
  int rv;

  rv = generate_exception (EX_ACCESS, regs.r_pc);
  if (RX_EXITED (rv))
    longjmp (decode_jmp_buf, rv);
}

static int
do_fp_exception (unsigned long opcode_pc)
{
  while (FPPENDING())
    EXCEPTION (EX_FLOATING);
  return RX_MAKE_STEPPED ();
}

static int
op_is_memory (const RX_Opcode_Decoded *rd, int i)
{
  switch (rd->op[i].type)
    {
    case RX_Operand_Predec:
    case RX_Operand_Postinc:
    case RX_Operand_Indirect:
      return 1;
    default:
      return 0;
    }
}
#define OM(i) op_is_memory (opcode, i)

#define DO_RETURN(x) { longjmp (decode_jmp_buf, x); }

int
decode_opcode ()
{
  unsigned int uma=0, umb=0;
  int ma=0, mb=0;
  int opcode_size, v;
  unsigned long long ll;
  long long sll;
  unsigned long opcode_pc;
  RX_Data rx_data;
  const RX_Opcode_Decoded *opcode;
#ifdef CYCLE_STATS
  unsigned long long prev_cycle_count;
#endif
#ifdef CYCLE_ACCURATE
  unsigned int tx;
#endif

#ifdef CYCLE_STATS
  prev_cycle_count = regs.cycle_count;
#endif

#ifdef CYCLE_ACCURATE
  memory_source = 0;
  memory_dest = 0;
#endif

  rx_cycles ++;

  maybe_get_mem_page (regs.r_pc);

  opcode_pc = regs.r_pc;

  /* Note that we don't word-swap this point, there's no point.  */
  if (decode_cache_base[opcode_pc] == NULL)
    {
      RX_Opcode_Decoded *opcode_w;
      rx_data.dpc = opcode_pc;
      opcode_w = decode_cache_base[opcode_pc] = calloc (1, sizeof (RX_Opcode_Decoded));
      opcode_size = rx_decode_opcode (opcode_pc, opcode_w,
				      rx_get_byte, &rx_data);
      opcode = opcode_w;
    }
  else
    {
      opcode = decode_cache_base[opcode_pc];
      opcode_size = opcode->n_bytes;
    }

#ifdef CYCLE_ACCURATE
  if (branch_alignment_penalty)
    {
      if ((regs.r_pc ^ (regs.r_pc + opcode_size - 1)) & ~7)
	{
	  tprintf("1 cycle branch alignment penalty\n");
	  cycles (branch_alignment_penalty);
#ifdef CYCLE_STATS
	  branch_alignment_stalls ++;
#endif
	}
      branch_alignment_penalty = 0;
    }
#endif

  regs.r_pc += opcode_size;

  rx_flagmask = opcode->flags_s;
  rx_flagand = ~(int)opcode->flags_0;
  rx_flagor = opcode->flags_1;

  switch (opcode->id)
    {
    case RXO_abs:
      sll = GS ();
      tprintf("|%lld| = ", sll);
      if (sll < 0)
	sll = -sll;
      tprintf("%lld\n", sll);
      PD (sll);
      set_osz (sll, 4);
      E (1);
      break;

    case RXO_adc:
      MATH_OP (+,carry);
      break;

    case RXO_add:
      MATH_OP (+,0);
      break;

    case RXO_and:
      LOGIC_OP (&);
      break;

    case RXO_bclr:
      ma = GD ();
      mb = GS ();
      if (opcode->op[0].type == RX_Operand_Register)
	mb &= 0x1f;
      else
	mb &= 0x07;
      ma &= ~(1 << mb);
      PD (ma);
      EBIT;
      break;

    case RXO_bmcc:
      ma = GD ();
      mb = GS ();
      if (opcode->op[0].type == RX_Operand_Register)
	mb &= 0x1f;
      else
	mb &= 0x07;
      if (GS2 ())
	ma |= (1 << mb);
      else
	ma &= ~(1 << mb);
      PD (ma);
      EBIT;
      break;

    case RXO_bnot:
      ma = GD ();
      mb = GS ();
      if (opcode->op[0].type == RX_Operand_Register)
	mb &= 0x1f;
      else
	mb &= 0x07;
      ma ^= (1 << mb);
      PD (ma);
      EBIT;
      break;

    case RXO_branch:
      if (opcode->op[1].type == RX_Operand_None || GS())
	{
#ifdef CYCLE_ACCURATE
	  SI old_pc = regs.r_pc;
	  int delta;
#endif
	  regs.r_pc = GD();
#ifdef CYCLE_ACCURATE
	  delta = regs.r_pc - old_pc;
	  if (delta >= 0 && delta < 16
	      && opcode_size > 1)
	    {
	      tprintf("near forward branch bonus\n");
	      cycles (2);
	    }
	  else
	    {
	      cycles (3);
	      branch_alignment_penalty = 1;
	    }
#ifdef CYCLE_STATS
	  branch_stalls ++;
#endif
#endif
	}
#ifdef CYCLE_ACCURATE
      else
	cycles (1);
#endif
      break;

    case RXO_branchrel:
      if (opcode->op[1].type == RX_Operand_None || GS())
	{
	  int delta = GD();
	  regs.r_pc = opcode_pc + delta;
#ifdef CYCLE_ACCURATE
	  /* Note: specs say 3, chip says 2.  */
	  if (delta >= 0 && delta < 16
	      && opcode_size > 1)
	    {
	      tprintf("near forward branch bonus\n");
	      cycles (2);
	    }
	  else
	    {
	      cycles (3);
	      branch_alignment_penalty = 1;
	    }
#ifdef CYCLE_STATS
	  branch_stalls ++;
#endif
#endif
	}
#ifdef CYCLE_ACCURATE
      else
	cycles (1);
#endif
      break;

    case RXO_brk:
      {
	int old_psw = regs.r_psw;
	if (rx_in_gdb)
	  DO_RETURN (RX_MAKE_HIT_BREAK ());
	if (regs.r_intb == 0)
	  {
	    tprintf("BREAK hit, no vector table.\n");
	    DO_RETURN (RX_MAKE_EXITED(1));
	  }
	regs.r_psw &= ~(FLAGBIT_I | FLAGBIT_U | FLAGBIT_PM);
	pushpc (old_psw);
	pushpc (regs.r_pc);
	regs.r_pc = mem_get_si (regs.r_intb);
	cycles(6);
      }
      break;

    case RXO_bset:
      ma = GD ();
      mb = GS ();
      if (opcode->op[0].type == RX_Operand_Register)
	mb &= 0x1f;
      else
	mb &= 0x07;
      ma |= (1 << mb);
      PD (ma);
      EBIT;
      break;

    case RXO_btst:
      ma = GS ();
      mb = GS2 ();
      if (opcode->op[1].type == RX_Operand_Register)
	mb &= 0x1f;
      else
	mb &= 0x07;
      umb = ma & (1 << mb);
      set_zc (! umb, umb);
      EBIT;
      break;

    case RXO_clrpsw:
      v = 1 << opcode->op[0].reg;
      if (FLAG_PM
	  && (v == FLAGBIT_I
	      || v == FLAGBIT_U))
	break;
      regs.r_psw &= ~v;
      cycles (1);
      break;

    case RXO_div: /* d = d / s */
      ma = GS();
      mb = GD();
      tprintf("%d / %d = ", mb, ma);
      if (ma == 0 || (ma == -1 && (unsigned int) mb == 0x80000000))
	{
	  tprintf("#NAN\n");
	  set_flags (FLAGBIT_O, FLAGBIT_O);
	  cycles (3);
	}
      else
	{
	  v = mb/ma;
	  tprintf("%d\n", v);
	  set_flags (FLAGBIT_O, 0);
	  PD (v);
	  div_cycles (mb, ma);
	}
      break;

    case RXO_divu: /* d = d / s */
      uma = GS();
      umb = GD();
      tprintf("%u / %u = ", umb, uma);
      if (uma == 0)
	{
	  tprintf("#NAN\n");
	  set_flags (FLAGBIT_O, FLAGBIT_O);
	  cycles (2);
	}
      else
	{
	  v = umb / uma;
	  tprintf("%u\n", v);
	  set_flags (FLAGBIT_O, 0);
	  PD (v);
	  divu_cycles (umb, uma);
	}
      break;

    case RXO_emul:
      ma = GD ();
      mb = GS ();
      sll = (long long)ma * (long long)mb;
      tprintf("%d * %d = %lld\n", ma, mb, sll);
      put_reg (opcode->op[0].reg, sll);
      put_reg (opcode->op[0].reg + 1, sll >> 32);
      E2;
      break;

    case RXO_emulu:
      uma = GD ();
      umb = GS ();
      ll = (long long)uma * (long long)umb;
      tprintf("%#x * %#x = %#llx\n", uma, umb, ll);
      put_reg (opcode->op[0].reg, ll);
      put_reg (opcode->op[0].reg + 1, ll >> 32);
      E2;
      break;

    case RXO_fadd:
      FLOAT_OP (fadd);
      E (4);
      break;

    case RXO_fcmp:
      ma = GD();
      mb = GS();
      FPCLEAR ();
      rxfp_cmp (ma, mb);
      FPCHECK ();
      E (1);
      break;

    case RXO_fdiv:
      FLOAT_OP (fdiv);
      E (16);
      break;

    case RXO_fmul:
      FLOAT_OP (fmul);
      E (3);
      break;

    case RXO_rtfi:
      PRIVILEDGED ();
      regs.r_psw = regs.r_bpsw;
      regs.r_pc = regs.r_bpc;
#ifdef CYCLE_ACCURATE
      regs.fast_return = 0;
      cycles(3);
#endif
      break;

    case RXO_fsub:
      FLOAT_OP (fsub);
      E (4);
      break;

    case RXO_ftoi:
      ma = GS ();
      FPCLEAR ();
      mb = rxfp_ftoi (ma, FPRM_ZERO);
      FPCHECK ();
      PD (mb);
      tprintf("(int) %g = %d\n", int2float(ma), mb);
      set_sz (mb, 4);
      E (2);
      break;

    case RXO_int:
      v = GS ();
      if (v == 255)
	{
	  int rc = rx_syscall (regs.r[5]);
	  if (! RX_STEPPED (rc))
	    DO_RETURN (rc);
	}
      else
	{
	  int old_psw = regs.r_psw;
	  regs.r_psw &= ~(FLAGBIT_I | FLAGBIT_U | FLAGBIT_PM);
	  pushpc (old_psw);
	  pushpc (regs.r_pc);
	  regs.r_pc = mem_get_si (regs.r_intb + 4 * v);
	}
      cycles (6);
      break;

    case RXO_itof:
      ma = GS ();
      FPCLEAR ();
      mb = rxfp_itof (ma, regs.r_fpsw);
      FPCHECK ();
      tprintf("(float) %d = %x\n", ma, mb);
      PD (mb);
      set_sz (ma, 4);
      E (2);
      break;

    case RXO_jsr:
    case RXO_jsrrel:
      {
#ifdef CYCLE_ACCURATE
	int delta;
	regs.m2m = 0;
#endif
	v = GD ();
#ifdef CYCLE_ACCURATE
	regs.link_register = regs.r_pc;
#endif
	pushpc (get_reg (pc));
	if (opcode->id == RXO_jsrrel)
	  v += regs.r_pc;
#ifdef CYCLE_ACCURATE
	delta = v - regs.r_pc;
#endif
	put_reg (pc, v);
#ifdef CYCLE_ACCURATE
	/* Note: docs say 3, chip says 2 */
	if (delta >= 0 && delta < 16)
	  {
	    tprintf ("near forward jsr bonus\n");
	    cycles (2);
	  }
	else
	  {
	    branch_alignment_penalty = 1;
	    cycles (3);
	  }
	regs.fast_return = 1;
#endif
      }
      break;

    case RXO_machi:
      ll = (long long)(signed short)(GS() >> 16) * (long long)(signed short)(GS2 () >> 16);
      ll <<= 16;
      put_reg64 (acc64, ll + regs.r_acc);
      E1;
      break;

    case RXO_maclo:
      ll = (long long)(signed short)(GS()) * (long long)(signed short)(GS2 ());
      ll <<= 16;
      put_reg64 (acc64, ll + regs.r_acc);
      E1;
      break;

    case RXO_max:
      mb = GS();
      ma = GD();
      if (ma > mb)
	PD (ma);
      else
	PD (mb);
      E (1);
      break;

    case RXO_min:
      mb = GS();
      ma = GD();
      if (ma < mb)
	PD (ma);
      else
	PD (mb);
      E (1);
      break;

    case RXO_mov:
      v = GS ();

      if (opcode->op[1].type == RX_Operand_Register
	  && opcode->op[1].reg == 17 /* PC */)
	{
	  /* Special case.  We want the address of the insn, not the
	     address of the next insn.  */
	  v = opcode_pc;
	}

      if (opcode->op[0].type == RX_Operand_Register
	  && opcode->op[0].reg == 16 /* PSW */)
	{
	  /* Special case, LDC and POPC can't ever modify PM.  */
	  int pm = regs.r_psw & FLAGBIT_PM;
	  v &= ~ FLAGBIT_PM;
	  v |= pm;
	  if (pm)
	    {
	      v &= ~ (FLAGBIT_I | FLAGBIT_U | FLAGBITS_IPL);
	      v |= pm;
	    }
	}
      if (FLAG_PM)
	{
	  /* various things can't be changed in user mode.  */
	  if (opcode->op[0].type == RX_Operand_Register)
	    if (opcode->op[0].reg == 32)
	      {
		v &= ~ (FLAGBIT_I | FLAGBIT_U | FLAGBITS_IPL);
		v |= regs.r_psw & (FLAGBIT_I | FLAGBIT_U | FLAGBITS_IPL);
	      }
	  if (opcode->op[0].reg == 34 /* ISP */
	      || opcode->op[0].reg == 37 /* BPSW */
	      || opcode->op[0].reg == 39 /* INTB */
	      || opcode->op[0].reg == 38 /* VCT */)
	    /* These are ignored.  */
	    break;
	}
      if (OM(0) && OM(1))
	cycles (2);
      else
	cycles (1);

      PD (v);

#ifdef CYCLE_ACCURATE
      if ((opcode->op[0].type == RX_Operand_Predec
	   && opcode->op[1].type == RX_Operand_Register)
	  || (opcode->op[0].type == RX_Operand_Postinc
	      && opcode->op[1].type == RX_Operand_Register))
	{
	  /* Special case: push reg doesn't cause a memory stall.  */
	  memory_dest = 0;
	  tprintf("push special case\n");
	}
#endif

      set_sz (v, DSZ());
      break;

    case RXO_movbi:
      PD (GS ());
      cycles (1);
      break;

    case RXO_movbir:
      PS (GD ());
      cycles (1);
      break;

    case RXO_mul:
      v = US2 ();
      ll = (unsigned long long) US1() * (unsigned long long) v;
      PD(ll);
      E (1);
      break;

    case RXO_mulhi:
      v = GS2 ();
      ll = (long long)(signed short)(GS() >> 16) * (long long)(signed short)(v >> 16);
      ll <<= 16;
      put_reg64 (acc64, ll);
      E1;
      break;

    case RXO_mullo:
      v = GS2 ();
      ll = (long long)(signed short)(GS()) * (long long)(signed short)(v);
      ll <<= 16;
      put_reg64 (acc64, ll);
      E1;
      break;

    case RXO_mvfachi:
      PD (get_reg (acchi));
      E1;
      break;

    case RXO_mvfaclo:
      PD (get_reg (acclo));
      E1;
      break;

    case RXO_mvfacmi:
      PD (get_reg (accmi));
      E1;
      break;

    case RXO_mvtachi:
      put_reg (acchi, GS ());
      E1;
      break;

    case RXO_mvtaclo:
      put_reg (acclo, GS ());
      E1;
      break;

    case RXO_mvtipl:
      regs.r_psw &= ~ FLAGBITS_IPL;
      regs.r_psw |= (GS () << FLAGSHIFT_IPL) & FLAGBITS_IPL;
      E1;
      break;

    case RXO_nop:
    case RXO_nop2:
    case RXO_nop3:
      E1;
      break;

    case RXO_or:
      LOGIC_OP (|);
      break;

    case RXO_popm:
      /* POPM cannot pop R0 (sp).  */
      if (opcode->op[1].reg == 0 || opcode->op[2].reg == 0)
	EXCEPTION (EX_UNDEFINED);
      if (opcode->op[1].reg >= opcode->op[2].reg)
	{
	  regs.r_pc = opcode_pc;
	  DO_RETURN (RX_MAKE_STOPPED (SIGILL));
	}
      for (v = opcode->op[1].reg; v <= opcode->op[2].reg; v++)
	{
	  cycles (1);
	  RLD (v);
	  put_reg (v, pop ());
	}
      break;

    case RXO_pushm:
      /* PUSHM cannot push R0 (sp).  */
      if (opcode->op[1].reg == 0 || opcode->op[2].reg == 0)
	EXCEPTION (EX_UNDEFINED);
      if (opcode->op[1].reg >= opcode->op[2].reg)
	{
	  regs.r_pc = opcode_pc;
	  return RX_MAKE_STOPPED (SIGILL);
	}
      for (v = opcode->op[2].reg; v >= opcode->op[1].reg; v--)
	{
	  RL (v);
	  push (get_reg (v));
	}
      cycles (opcode->op[2].reg - opcode->op[1].reg + 1);
      break;

    case RXO_racw:
      ll = get_reg64 (acc64) << GS ();
      ll += 0x80000000ULL;
      if ((signed long long)ll > (signed long long)0x00007fff00000000ULL)
	ll = 0x00007fff00000000ULL;
      else if ((signed long long)ll < (signed long long)0xffff800000000000ULL)
	ll = 0xffff800000000000ULL;
      else
	ll &= 0xffffffff00000000ULL;
      put_reg64 (acc64, ll);
      E1;
      break;

    case RXO_rte:
      PRIVILEDGED ();
      regs.r_pc = poppc ();
      regs.r_psw = poppc ();
      if (FLAG_PM)
	regs.r_psw |= FLAGBIT_U;
#ifdef CYCLE_ACCURATE
      regs.fast_return = 0;
      cycles (6);
#endif
      break;

    case RXO_revl:
      uma = GS ();
      umb = (((uma >> 24) & 0xff)
	     | ((uma >> 8) & 0xff00)
	     | ((uma << 8) & 0xff0000)
	     | ((uma << 24) & 0xff000000UL));
      PD (umb);
      E1;
      break;

    case RXO_revw:
      uma = GS ();
      umb = (((uma >> 8) & 0x00ff00ff)
	     | ((uma << 8) & 0xff00ff00UL));
      PD (umb);
      E1;
      break;

    case RXO_rmpa:
      RL(4);
      RL(5);
#ifdef CYCLE_ACCURATE
      tx = regs.r[3];
#endif

      while (regs.r[3] != 0)
	{
	  long long tmp;

	  switch (opcode->size)
	    {
	    case RX_Long:
	      ma = mem_get_si (regs.r[1]);
	      mb = mem_get_si (regs.r[2]);
	      regs.r[1] += 4;
	      regs.r[2] += 4;
	      break;
	    case RX_Word:
	      ma = sign_ext (mem_get_hi (regs.r[1]), 16);
	      mb = sign_ext (mem_get_hi (regs.r[2]), 16);
	      regs.r[1] += 2;
	      regs.r[2] += 2;
	      break;
	    case RX_Byte:
	      ma = sign_ext (mem_get_qi (regs.r[1]), 8);
	      mb = sign_ext (mem_get_qi (regs.r[2]), 8);
	      regs.r[1] += 1;
	      regs.r[2] += 1;
	      break;
	    default:
	      abort ();
	    }
	  /* We do the multiply as a signed value.  */
	  sll = (long long)ma * (long long)mb;
	  tprintf("        %016llx = %d * %d\n", sll, ma, mb);
	  /* but we do the sum as unsigned, while sign extending the operands.  */
	  tmp = regs.r[4] + (sll & 0xffffffffUL);
	  regs.r[4] = tmp & 0xffffffffUL;
	  tmp >>= 32;
	  sll >>= 32;
	  tmp += regs.r[5] + (sll & 0xffffffffUL);
	  regs.r[5] = tmp & 0xffffffffUL;
	  tmp >>= 32;
	  sll >>= 32;
	  tmp += regs.r[6] + (sll & 0xffffffffUL);
	  regs.r[6] = tmp & 0xffffffffUL;
	  tprintf("%08lx\033[36m%08lx\033[0m%08lx\n",
		  (unsigned long) regs.r[6],
		  (unsigned long) regs.r[5],
		  (unsigned long) regs.r[4]);

	  regs.r[3] --;
	}
      if (regs.r[6] & 0x00008000)
	regs.r[6] |= 0xffff0000UL;
      else
	regs.r[6] &= 0x0000ffff;
      ma = (regs.r[6] & 0x80000000UL) ? FLAGBIT_S : 0;
      if (regs.r[6] != 0 && regs.r[6] != 0xffffffffUL)
	set_flags (FLAGBIT_O|FLAGBIT_S, ma | FLAGBIT_O);
      else
	set_flags (FLAGBIT_O|FLAGBIT_S, ma);
#ifdef CYCLE_ACCURATE
      switch (opcode->size)
	{
	case RX_Long:
	  cycles (6 + 4 * tx);
	  break;
	case RX_Word:
	  cycles (6 + 5 * (tx / 2) + 4 * (tx % 2));
	  break;
	case RX_Byte:
	  cycles (6 + 7 * (tx / 4) + 4 * (tx % 4));
	  break;
	default:
	  abort ();
	}
#endif
      break;

    case RXO_rolc:
      v = GD ();
      ma = v & 0x80000000UL;
      v <<= 1;
      v |= carry;
      set_szc (v, 4, ma);
      PD (v);
      E1;
      break;

    case RXO_rorc:
      uma = GD ();
      mb = uma & 1;
      uma >>= 1;
      uma |= (carry ? 0x80000000UL : 0);
      set_szc (uma, 4, mb);
      PD (uma);
      E1;
      break;

    case RXO_rotl:
      mb = GS ();
      uma = GD ();
      if (mb)
	{
	  uma = (uma << mb) | (uma >> (32-mb));
	  mb = uma & 1;
	}
      set_szc (uma, 4, mb);
      PD (uma);
      E1;
      break;

    case RXO_rotr:
      mb = GS ();
      uma = GD ();
      if (mb)
	{
	  uma = (uma >> mb) | (uma << (32-mb));
	  mb = uma & 0x80000000;
	}
      set_szc (uma, 4, mb);
      PD (uma);
      E1;
      break;

    case RXO_round:
      ma = GS ();
      FPCLEAR ();
      mb = rxfp_ftoi (ma, regs.r_fpsw);
      FPCHECK ();
      PD (mb);
      tprintf("(int) %g = %d\n", int2float(ma), mb);
      set_sz (mb, 4);
      E (2);
      break;

    case RXO_rts:
      {
#ifdef CYCLE_ACCURATE
	int cyc = 5;
#endif
	regs.r_pc = poppc ();
#ifdef CYCLE_ACCURATE
	/* Note: specs say 5, chip says 3.  */
	if (regs.fast_return && regs.link_register == regs.r_pc)
	  {
#ifdef CYCLE_STATS
	    fast_returns ++;
#endif
	    tprintf("fast return bonus\n");
	    cyc -= 2;
	  }
	cycles (cyc);
	regs.fast_return = 0;
	branch_alignment_penalty = 1;
#endif
      }
      break;

    case RXO_rtsd:
      if (opcode->op[2].type == RX_Operand_Register)
	{
	  int i;
	  /* RTSD cannot pop R0 (sp).  */
	  put_reg (0, get_reg (0) + GS() - (opcode->op[0].reg-opcode->op[2].reg+1)*4);
	  if (opcode->op[2].reg == 0)
	    EXCEPTION (EX_UNDEFINED);
#ifdef CYCLE_ACCURATE
	  tx = opcode->op[0].reg - opcode->op[2].reg + 1;
#endif
	  for (i = opcode->op[2].reg; i <= opcode->op[0].reg; i ++)
	    {
	      RLD (i);
	      put_reg (i, pop ());
	    }
	}
      else
	{
#ifdef CYCLE_ACCURATE
	  tx = 0;
#endif
	  put_reg (0, get_reg (0) + GS());
	}
      put_reg (pc, poppc());
#ifdef CYCLE_ACCURATE
      if (regs.fast_return && regs.link_register == regs.r_pc)
	{
	  tprintf("fast return bonus\n");
#ifdef CYCLE_STATS
	  fast_returns ++;
#endif
	  cycles (tx < 3 ? 3 : tx + 1);
	}
      else
	{
	  cycles (tx < 5 ? 5 : tx + 1);
	}
      regs.fast_return = 0;
      branch_alignment_penalty = 1;
#endif
      break;

    case RXO_sat:
      if (FLAG_O && FLAG_S)
	PD (0x7fffffffUL);
      else if (FLAG_O && ! FLAG_S)
	PD (0x80000000UL);
      E1;
      break;

    case RXO_sbb:
      MATH_OP (-, ! carry);
      break;

    case RXO_sccnd:
      if (GS())
	PD (1);
      else
	PD (0);
      E1;
      break;

    case RXO_scmpu:
#ifdef CYCLE_ACCURATE
      tx = regs.r[3];
#endif
      while (regs.r[3] != 0)
	{
	  uma = mem_get_qi (regs.r[1] ++);
	  umb = mem_get_qi (regs.r[2] ++);
	  regs.r[3] --;
	  if (uma != umb || uma == 0)
	    break;
	}
      if (uma == umb)
	set_zc (1, 1);
      else
	set_zc (0, ((int)uma - (int)umb) >= 0);
      cycles (2 + 4 * (tx / 4) + 4 * (tx % 4));
      break;

    case RXO_setpsw:
      v = 1 << opcode->op[0].reg;
      if (FLAG_PM
	  && (v == FLAGBIT_I
	      || v == FLAGBIT_U))
	break;
      regs.r_psw |= v;
      cycles (1);
      break;

    case RXO_smovb:
      RL (3);
#ifdef CYCLE_ACCURATE
      tx = regs.r[3];
#endif
      while (regs.r[3])
	{
	  uma = mem_get_qi (regs.r[2] --);
	  mem_put_qi (regs.r[1]--, uma);
	  regs.r[3] --;
	}
#ifdef CYCLE_ACCURATE
      if (tx > 3)
	cycles (6 + 3 * (tx / 4) + 3 * (tx % 4));
      else
	cycles (2 + 3 * (tx % 4));
#endif
      break;

    case RXO_smovf:
      RL (3);
#ifdef CYCLE_ACCURATE
      tx = regs.r[3];
#endif
      while (regs.r[3])
	{
	  uma = mem_get_qi (regs.r[2] ++);
	  mem_put_qi (regs.r[1]++, uma);
	  regs.r[3] --;
	}
      cycles (2 + 3 * (int)(tx / 4) + 3 * (tx % 4));
      break;

    case RXO_smovu:
#ifdef CYCLE_ACCURATE
      tx = regs.r[3];
#endif
      while (regs.r[3] != 0)
	{
	  uma = mem_get_qi (regs.r[2] ++);
	  mem_put_qi (regs.r[1]++, uma);
	  regs.r[3] --;
	  if (uma == 0)
	    break;
	}
      cycles (2 + 3 * (int)(tx / 4) + 3 * (tx % 4));
      break;

    case RXO_shar: /* d = ma >> mb */
      SHIFT_OP (sll, int, mb, >>=, 1);
      E (1);
      break;

    case RXO_shll: /* d = ma << mb */
      SHIFT_OP (ll, int, mb, <<=, 0x80000000UL);
      E (1);
      break;

    case RXO_shlr: /* d = ma >> mb */
      SHIFT_OP (ll, unsigned int, mb, >>=, 1);
      E (1);
      break;

    case RXO_sstr:
      RL (3);
#ifdef CYCLE_ACCURATE
      tx = regs.r[3];
#endif
      switch (opcode->size)
	{
	case RX_Long:
	  while (regs.r[3] != 0)
	    {
	      mem_put_si (regs.r[1], regs.r[2]);
	      regs.r[1] += 4;
	      regs.r[3] --;
	    }
	  cycles (2 + tx);
	  break;
	case RX_Word:
	  while (regs.r[3] != 0)
	    {
	      mem_put_hi (regs.r[1], regs.r[2]);
	      regs.r[1] += 2;
	      regs.r[3] --;
	    }
	  cycles (2 + (int)(tx / 2) + tx % 2);
	  break;
	case RX_Byte:
	  while (regs.r[3] != 0)
	    {
	      mem_put_qi (regs.r[1], regs.r[2]);
	      regs.r[1] ++;
	      regs.r[3] --;
	    }
	  cycles (2 + (int)(tx / 4) + tx % 4);
	  break;
	default:
	  abort ();
	}
      break;

    case RXO_stcc:
      if (GS2())
	PD (GS ());
      E1;
      break;

    case RXO_stop:
      PRIVILEDGED ();
      regs.r_psw |= FLAGBIT_I;
      DO_RETURN (RX_MAKE_STOPPED(0));

    case RXO_sub:
      MATH_OP (-, 0);
      break;

    case RXO_suntil:
      RL(3);
#ifdef CYCLE_ACCURATE
      tx = 0;
#endif
      if (regs.r[3] == 0)
	{
	  cycles (3);
	  break;
	}
      switch (opcode->size)
	{
	case RX_Long:
	  uma = get_reg (2);
	  while (regs.r[3] != 0)
	    {
	      regs.r[3] --;
	      umb = mem_get_si (get_reg (1));
	      regs.r[1] += 4;
#ifdef CYCLE_ACCURATE
	      tx ++;
#endif
	      if (umb == uma)
		break;
	    }
#ifdef CYCLE_ACCURATE
	  cycles (3 + 3 * tx);
#endif
	  break;
	case RX_Word:
	  uma = get_reg (2) & 0xffff;
	  while (regs.r[3] != 0)
	    {
	      regs.r[3] --;
	      umb = mem_get_hi (get_reg (1));
	      regs.r[1] += 2;
#ifdef CYCLE_ACCURATE
	      tx ++;
#endif
	      if (umb == uma)
		break;
	    }
#ifdef CYCLE_ACCURATE
	  cycles (3 + 3 * (tx / 2) + 3 * (tx % 2));
#endif
	  break;
	case RX_Byte:
	  uma = get_reg (2) & 0xff;
	  while (regs.r[3] != 0)
	    {
	      regs.r[3] --;
	      umb = mem_get_qi (regs.r[1]);
	      regs.r[1] += 1;
#ifdef CYCLE_ACCURATE
	      tx ++;
#endif
	      if (umb == uma)
		break;
	    }
#ifdef CYCLE_ACCURATE
	  cycles (3 + 3 * (tx / 4) + 3 * (tx % 4));
#endif
	  break;
	default:
	  abort();
	}
      if (uma == umb)
	set_zc (1, 1);
      else
	set_zc (0, ((int)uma - (int)umb) >= 0);
      break;

    case RXO_swhile:
      RL(3);
#ifdef CYCLE_ACCURATE
      tx = 0;
#endif
      if (regs.r[3] == 0)
	break;
      switch (opcode->size)
	{
	case RX_Long:
	  uma = get_reg (2);
	  while (regs.r[3] != 0)
	    {
	      regs.r[3] --;
	      umb = mem_get_si (get_reg (1));
	      regs.r[1] += 4;
#ifdef CYCLE_ACCURATE
	      tx ++;
#endif
	      if (umb != uma)
		break;
	    }
#ifdef CYCLE_ACCURATE
	  cycles (3 + 3 * tx);
#endif
	  break;
	case RX_Word:
	  uma = get_reg (2) & 0xffff;
	  while (regs.r[3] != 0)
	    {
	      regs.r[3] --;
	      umb = mem_get_hi (get_reg (1));
	      regs.r[1] += 2;
#ifdef CYCLE_ACCURATE
	      tx ++;
#endif
	      if (umb != uma)
		break;
	    }
#ifdef CYCLE_ACCURATE
	  cycles (3 + 3 * (tx / 2) + 3 * (tx % 2));
#endif
	  break;
	case RX_Byte:
	  uma = get_reg (2) & 0xff;
	  while (regs.r[3] != 0)
	    {
	      regs.r[3] --;
	      umb = mem_get_qi (regs.r[1]);
	      regs.r[1] += 1;
#ifdef CYCLE_ACCURATE
	      tx ++;
#endif
	      if (umb != uma)
		break;
	    }
#ifdef CYCLE_ACCURATE
	  cycles (3 + 3 * (tx / 4) + 3 * (tx % 4));
#endif
	  break;
	default:
	  abort();
	}
      if (uma == umb)
	set_zc (1, 1);
      else
	set_zc (0, ((int)uma - (int)umb) >= 0);
      break;

    case RXO_wait:
      PRIVILEDGED ();
      regs.r_psw |= FLAGBIT_I;
      DO_RETURN (RX_MAKE_STOPPED(0));

    case RXO_xchg:
#ifdef CYCLE_ACCURATE
      regs.m2m = 0;
#endif
      v = GS (); /* This is the memory operand, if any.  */
      PS (GD ()); /* and this may change the address register.  */
      PD (v);
      E2;
#ifdef CYCLE_ACCURATE
      /* all M cycles happen during xchg's cycles.  */
      memory_dest = 0;
      memory_source = 0;
#endif
      break;

    case RXO_xor:
      LOGIC_OP (^);
      break;

    default:
      EXCEPTION (EX_UNDEFINED);
    }

#ifdef CYCLE_ACCURATE
  regs.m2m = 0;
  if (memory_source)
    regs.m2m |= M2M_SRC;
  if (memory_dest)
    regs.m2m |= M2M_DST;

  regs.rt = new_rt;
  new_rt = -1;
#endif

#ifdef CYCLE_STATS
  if (prev_cycle_count == regs.cycle_count)
    {
      printf("Cycle count not updated! id %s\n", id_names[opcode->id]);
      abort ();
    }
#endif

#ifdef CYCLE_STATS
  if (running_benchmark)
    {
      int omap = op_lookup (opcode->op[0].type, opcode->op[1].type, opcode->op[2].type);


      cycles_per_id[opcode->id][omap] += regs.cycle_count - prev_cycle_count;
      times_per_id[opcode->id][omap] ++;

      times_per_pair[prev_opcode_id][po0][opcode->id][omap] ++;

      prev_opcode_id = opcode->id;
      po0 = omap;
    }
#endif

  return RX_MAKE_STEPPED ();
}

#ifdef CYCLE_STATS
void
reset_pipeline_stats (void)
{
  memset (cycles_per_id, 0, sizeof(cycles_per_id));
  memset (times_per_id, 0, sizeof(times_per_id));
  memory_stalls = 0;
  register_stalls = 0;
  branch_stalls = 0;
  branch_alignment_stalls = 0;
  fast_returns = 0;
  memset (times_per_pair, 0, sizeof(times_per_pair));
  running_benchmark = 1;

  benchmark_start_cycle = regs.cycle_count;
}

void
halt_pipeline_stats (void)
{
  running_benchmark = 0;
  benchmark_end_cycle = regs.cycle_count;
}
#endif

void
pipeline_stats (void)
{
#ifdef CYCLE_STATS
  int i, o1;
  int p, p1;
#endif

#ifdef CYCLE_ACCURATE
  if (verbose == 1)
    {
      printf ("cycles: %llu\n", regs.cycle_count);
      return;
    }

  printf ("cycles: %13s\n", comma (regs.cycle_count));
#endif

#ifdef CYCLE_STATS
  if (benchmark_start_cycle)
    printf ("bmark:  %13s\n", comma (benchmark_end_cycle - benchmark_start_cycle));

  printf("\n");
  for (i = 0; i < N_RXO; i++)
    for (o1 = 0; o1 < N_MAP; o1 ++)
      if (times_per_id[i][o1])
	printf("%13s %13s %7.2f  %s %s\n",
	       comma (cycles_per_id[i][o1]),
	       comma (times_per_id[i][o1]),
	       (double)cycles_per_id[i][o1] / times_per_id[i][o1],
	       op_cache_string(o1),
	       id_names[i]+4);

  printf("\n");
  for (p = 0; p < N_RXO; p ++)
    for (p1 = 0; p1 < N_MAP; p1 ++)
      for (i = 0; i < N_RXO; i ++)
	for (o1 = 0; o1 < N_MAP; o1 ++)
	  if (times_per_pair[p][p1][i][o1])
	    {
	      printf("%13s   %s %-9s  ->  %s %s\n",
		     comma (times_per_pair[p][p1][i][o1]),
		     op_cache_string(p1),
		     id_names[p]+4,
		     op_cache_string(o1),
		     id_names[i]+4);
	    }

  printf("\n");
  printf("%13s memory stalls\n", comma (memory_stalls));
  printf("%13s register stalls\n", comma (register_stalls));
  printf("%13s branches taken (non-return)\n", comma (branch_stalls));
  printf("%13s branch alignment stalls\n", comma (branch_alignment_stalls));
  printf("%13s fast returns\n", comma (fast_returns));
#endif
}
