/* This file is part of SIS (SPARC instruction simulator)

   Copyright (C) 1995-2016 Free Software Foundation, Inc.
   Contributed by Jiri Gaisler, European Space Agency

   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 "sis.h"
#include <math.h>
#include <stdio.h>

extern int32    sis_verbose, sparclite;
int ext_irl = 0;

/* Load/store interlock delay */
#define FLSTHOLD 1

/* Load delay (delete if unwanted - speeds up simulation) */
#define LOAD_DEL 1

#define T_LD	2
#define T_LDD	3
#define T_ST	3
#define T_STD	4
#define T_LDST	4
#define T_JMPL	2
#define T_RETT	2

#define FSR_QNE 	0x2000
#define FP_EXE_MODE 0
#define	FP_EXC_PE   1
#define FP_EXC_MODE 2

#define	FBA	8
#define	FBN	0
#define	FBNE	1
#define	FBLG	2
#define	FBUL	3
#define	FBL 	4
#define	FBUG	5
#define	FBG 	6
#define	FBU 	7
#define FBA	8
#define FBE	9
#define FBUE	10
#define FBGE	11
#define FBUGE	12
#define FBLE	13
#define FBULE	14
#define FBO	15

#define	FCC_E 	0
#define	FCC_L 	1
#define	FCC_G 	2
#define	FCC_U 	3

#define PSR_ET 0x20
#define PSR_EF 0x1000
#define PSR_PS 0x40
#define PSR_S  0x80
#define PSR_N  0x0800000
#define PSR_Z  0x0400000
#define PSR_V  0x0200000
#define PSR_C  0x0100000
#define PSR_CC 0x0F00000
#define PSR_CWP 0x7
#define PSR_PIL 0x0f00

#define ICC_N	(icc >> 3)
#define ICC_Z	(icc >> 2)
#define ICC_V	(icc >> 1)
#define ICC_C	(icc)

#define FP_PRES	(sregs->fpu_pres)

#define TRAP_IEXC 1
#define TRAP_UNIMP 2
#define TRAP_PRIVI 3
#define TRAP_FPDIS 4
#define TRAP_WOFL 5
#define TRAP_WUFL 6
#define TRAP_UNALI 7
#define TRAP_FPEXC 8
#define TRAP_DEXC 9
#define TRAP_TAG 10
#define TRAP_DIV0 0x2a

#define FSR_TT		0x1C000
#define FP_IEEE		0x04000
#define FP_UNIMP	0x0C000
#define FP_SEQ_ERR	0x10000

#define	BICC_BN		0
#define	BICC_BE		1
#define	BICC_BLE	2
#define	BICC_BL		3
#define	BICC_BLEU	4
#define	BICC_BCS	5
#define	BICC_NEG	6
#define	BICC_BVS	7
#define	BICC_BA		8
#define	BICC_BNE	9
#define	BICC_BG		10
#define	BICC_BGE	11
#define	BICC_BGU	12
#define	BICC_BCC	13
#define	BICC_POS	14
#define	BICC_BVC	15

#define INST_SIMM13 0x1fff
#define INST_RS2    0x1f
#define INST_I	    0x2000
#define ADD 	0x00
#define ADDCC 	0x10
#define ADDX 	0x08
#define ADDXCC 	0x18
#define TADDCC 	0x20
#define TSUBCC  0x21
#define TADDCCTV 0x22
#define TSUBCCTV 0x23
#define IAND 	0x01
#define IANDCC 	0x11
#define IANDN 	0x05
#define IANDNCC	0x15
#define MULScc 	0x24
#define DIVScc 	0x1D
#define SMUL	0x0B
#define SMULCC	0x1B
#define UMUL	0x0A
#define UMULCC	0x1A
#define SDIV	0x0F
#define SDIVCC	0x1F
#define UDIV	0x0E
#define UDIVCC	0x1E
#define IOR 	0x02
#define IORCC 	0x12
#define IORN 	0x06
#define IORNCC 	0x16
#define SLL 	0x25
#define SRA 	0x27
#define SRL 	0x26
#define SUB 	0x04
#define SUBCC 	0x14
#define SUBX 	0x0C
#define SUBXCC 	0x1C
#define IXNOR 	0x07
#define IXNORCC	0x17
#define IXOR 	0x03
#define IXORCC 	0x13
#define SETHI 	0x04
#define BICC 	0x02
#define FPBCC 	0x06
#define RDY 	0x28
#define RDPSR 	0x29
#define RDWIM 	0x2A
#define RDTBR 	0x2B
#define SCAN 	0x2C
#define WRY	0x30
#define WRPSR	0x31
#define WRWIM	0x32
#define WRTBR	0x33
#define JMPL 	0x38
#define RETT 	0x39
#define TICC 	0x3A
#define SAVE 	0x3C
#define RESTORE 0x3D
#define LDD	0x03
#define LDDA	0x13
#define LD	0x00
#define LDA	0x10
#define LDF	0x20
#define LDDF	0x23
#define LDSTUB	0x0D
#define LDSTUBA	0x1D
#define LDUB	0x01
#define LDUBA	0x11
#define LDSB	0x09
#define LDSBA	0x19
#define LDUH	0x02
#define LDUHA	0x12
#define LDSH	0x0A
#define LDSHA	0x1A
#define LDFSR	0x21
#define ST	0x04
#define STA	0x14
#define STB	0x05
#define STBA	0x15
#define STD	0x07
#define STDA	0x17
#define STF	0x24
#define STDFQ	0x26
#define STDF	0x27
#define STFSR	0x25
#define STH	0x06
#define STHA	0x16
#define SWAP	0x0F
#define SWAPA	0x1F
#define FLUSH	0x3B

#define SIGN_BIT 0x80000000

/* # of cycles overhead when a trap is taken */
#define TRAP_C  3

/* Forward declarations */

static uint32	sub_cc (uint32 psr, int32 operand1, int32 operand2,
			int32 result);
static uint32	add_cc (uint32 psr, int32 operand1, int32 operand2,
			int32 result);
static void	log_cc (int32 result, struct pstate *sregs);
static int	fpexec (uint32 op3, uint32 rd, uint32 rs1, uint32 rs2,
			struct pstate *sregs);
static int	chk_asi (struct pstate *sregs, uint32 *asi, uint32 op3);


extern struct estate ebase;
extern int32    nfp,ift;

#ifdef ERRINJ
extern uint32 errtt, errftt;
#endif

static uint32
sub_cc(psr, operand1, operand2, result)
    uint32          psr;
    int32           operand1;
    int32           operand2;
    int32           result;
{
    psr = ((psr & ~PSR_N) | ((result >> 8) & PSR_N));
    if (result)
	psr &= ~PSR_Z;
    else
	psr |= PSR_Z;
    psr = (psr & ~PSR_V) | ((((operand1 & ~operand2 & ~result) |
			   (~operand1 & operand2 & result)) >> 10) & PSR_V);
    psr = (psr & ~PSR_C) | ((((~operand1 & operand2) |
			 ((~operand1 | operand2) & result)) >> 11) & PSR_C);
    return psr;
}

uint32
add_cc(psr, operand1, operand2, result)
    uint32          psr;
    int32           operand1;
    int32           operand2;
    int32           result;
{
    psr = ((psr & ~PSR_N) | ((result >> 8) & PSR_N));
    if (result)
	psr &= ~PSR_Z;
    else
	psr |= PSR_Z;
    psr = (psr & ~PSR_V) | ((((operand1 & operand2 & ~result) |
			  (~operand1 & ~operand2 & result)) >> 10) & PSR_V);
    psr = (psr & ~PSR_C) | ((((operand1 & operand2) |
			 ((operand1 | operand2) & ~result)) >> 11) & PSR_C);
    return psr;
}

static void
log_cc(result, sregs)
    int32           result;
    struct pstate  *sregs;
{
    sregs->psr &= ~(PSR_CC);	/* Zero CC bits */
    sregs->psr = (sregs->psr | ((result >> 8) & PSR_N));
    if (result == 0)
	sregs->psr |= PSR_Z;
}

/* Add two unsigned 32-bit integers, and calculate the carry out. */

static uint32
add32 (uint32 n1, uint32 n2, int *carry)
{
  uint32 result = n1 + n2;

  *carry = result < n1 || result < n2;
  return result;
}

/* Multiply two 32-bit integers.  */

static void
mul64 (uint32 n1, uint32 n2, uint32 *result_hi, uint32 *result_lo, int msigned)
{
  uint32 lo, mid1, mid2, hi, reg_lo, reg_hi;
  int carry;
  int sign = 0;

  /* If this is a signed multiply, calculate the sign of the result
     and make the operands positive.  */
  if (msigned)
    {
      sign = (n1 ^ n2) & SIGN_BIT;
      if (n1 & SIGN_BIT)
	n1 = -n1;
      if (n2 & SIGN_BIT)
	n2 = -n2;
      
    }
  
  /* We can split the 32x32 into four 16x16 operations. This ensures
     that we do not lose precision on 32bit only hosts: */
  lo =   ((n1 & 0xFFFF) * (n2 & 0xFFFF));
  mid1 = ((n1 & 0xFFFF) * ((n2 >> 16) & 0xFFFF));
  mid2 = (((n1 >> 16) & 0xFFFF) * (n2 & 0xFFFF));
  hi =   (((n1 >> 16) & 0xFFFF) * ((n2 >> 16) & 0xFFFF));
  
  /* We now need to add all of these results together, taking care
     to propogate the carries from the additions: */
  reg_lo = add32 (lo, (mid1 << 16), &carry);
  reg_hi = carry;
  reg_lo = add32 (reg_lo, (mid2 << 16), &carry);
  reg_hi += (carry + ((mid1 >> 16) & 0xFFFF) + ((mid2 >> 16) & 0xFFFF) + hi);

  /* Negate result if necessary. */
  if (sign)
    {
      reg_hi = ~ reg_hi;
      reg_lo = - reg_lo;
      if (reg_lo == 0)
	reg_hi++;
    }
  
  *result_lo = reg_lo;
  *result_hi = reg_hi;
}


/* Divide a 64-bit integer by a 32-bit integer.  We cheat and assume
   that the host compiler supports long long operations.  */

static void
div64 (uint32 n1_hi, uint32 n1_low, uint32 n2, uint32 *result, int msigned)
{
  uint64 n1;

  n1 = ((uint64) n1_hi) << 32;
  n1 |= ((uint64) n1_low) & 0xffffffff;

  if (msigned)
    {
      int64 n1_s = (int64) n1;
      int32 n2_s = (int32) n2;
      n1_s = n1_s / n2_s;
      n1 = (uint64) n1_s;
    }
  else
    n1 = n1 / n2;

  *result = (uint32) (n1 & 0xffffffff);
}


static int
extract_short (uint32 data, uint32 address)
{
    return ((data >> ((2 - (address & 2)) * 8)) & 0xffff);
}

static int
extract_short_signed (uint32 data, uint32 address)
{
    uint32 tmp = ((data >> ((2 - (address & 2)) * 8)) & 0xffff);
    if (tmp & 0x8000)
        tmp |= 0xffff0000;
    return tmp;
}

static int
extract_byte (uint32 data, uint32 address)
{
    return ((data >> ((3 - (address & 3)) * 8)) & 0xff);
}

static int
extract_byte_signed (uint32 data, uint32 address)
{
    uint32 tmp = ((data >> ((3 - (address & 3)) * 8)) & 0xff);
    if (tmp & 0x80)
        tmp |= 0xffffff00;
    return tmp;
}

int
dispatch_instruction(sregs)
    struct pstate  *sregs;
{

    uint32          cwp, op, op2, op3, asi, rd, cond, rs1,
                    rs2;
    uint32          ldep, icc;
    int32           operand1, operand2, *rdd, result, eicc,
                    new_cwp;
    int32           pc, npc, data, address, ws, mexc, fcc;
    int32	    ddata[2];

    sregs->ninst++;
    cwp = ((sregs->psr & PSR_CWP) << 4);
    op = sregs->inst >> 30;
    pc = sregs->npc;
    npc = sregs->npc + 4;
    op3 = rd = rs1 = operand2 = eicc = 0;
    rdd = 0;
    if (op & 2) {

	op3 = (sregs->inst >> 19) & 0x3f;
	rs1 = (sregs->inst >> 14) & 0x1f;
	rd = (sregs->inst >> 25) & 0x1f;

#ifdef LOAD_DEL

	/* Check if load dependecy is possible */
	if (ebase.simtime <= sregs->ildtime)
	    ldep = (((op3 & 0x38) != 0x28) && ((op3 & 0x3e) != 0x34) && (sregs->ildreg != 0));
        else
	    ldep = 0;
	if (sregs->inst & INST_I) {
	    if (ldep && (sregs->ildreg == rs1))
		sregs->hold++;
	    operand2 = sregs->inst;
	    operand2 = ((operand2 << 19) >> 19);	/* sign extend */
	} else {
	    rs2 = sregs->inst & INST_RS2;
	    if (rs2 > 7)
		operand2 = sregs->r[(cwp + rs2) & 0x7f];
	    else
		operand2 = sregs->g[rs2];
	    if (ldep && ((sregs->ildreg == rs1) || (sregs->ildreg == rs2)))
		sregs->hold++;
	}
#else
	if (sregs->inst & INST_I) {
	    operand2 = sregs->inst;
	    operand2 = ((operand2 << 19) >> 19);	/* sign extend */
	} else {
	    rs2 = sregs->inst & INST_RS2;
	    if (rs2 > 7)
		operand2 = sregs->r[(cwp + rs2) & 0x7f];
	    else
		operand2 = sregs->g[rs2];
	}
#endif

	if (rd > 7)
	    rdd = &(sregs->r[(cwp + rd) & 0x7f]);
	else
	    rdd = &(sregs->g[rd]);
	if (rs1 > 7)
	    rs1 = sregs->r[(cwp + rs1) & 0x7f];
	else
	    rs1 = sregs->g[rs1];
    }
    switch (op) {
    case 0:
	op2 = (sregs->inst >> 22) & 0x7;
	switch (op2) {
	case SETHI:
	    rd = (sregs->inst >> 25) & 0x1f;
	    if (rd > 7)
		rdd = &(sregs->r[(cwp + rd) & 0x7f]);
	    else
		rdd = &(sregs->g[rd]);
	    *rdd = sregs->inst << 10;
	    break;
	case BICC:
#ifdef STAT
	    sregs->nbranch++;
#endif
	    icc = sregs->psr >> 20;
	    cond = ((sregs->inst >> 25) & 0x0f);
	    switch (cond) {
	    case BICC_BN:
		eicc = 0;
		break;
	    case BICC_BE:
		eicc = ICC_Z;
		break;
	    case BICC_BLE:
		eicc = ICC_Z | (ICC_N ^ ICC_V);
		break;
	    case BICC_BL:
		eicc = (ICC_N ^ ICC_V);
		break;
	    case BICC_BLEU:
		eicc = ICC_C | ICC_Z;
		break;
	    case BICC_BCS:
		eicc = ICC_C;
		break;
	    case BICC_NEG:
		eicc = ICC_N;
		break;
	    case BICC_BVS:
		eicc = ICC_V;
		break;
	    case BICC_BA:
		eicc = 1;
		if (sregs->inst & 0x20000000)
		    sregs->annul = 1;
		break;
	    case BICC_BNE:
		eicc = ~(ICC_Z);
		break;
	    case BICC_BG:
		eicc = ~(ICC_Z | (ICC_N ^ ICC_V));
		break;
	    case BICC_BGE:
		eicc = ~(ICC_N ^ ICC_V);
		break;
	    case BICC_BGU:
		eicc = ~(ICC_C | ICC_Z);
		break;
	    case BICC_BCC:
		eicc = ~(ICC_C);
		break;
	    case BICC_POS:
		eicc = ~(ICC_N);
		break;
	    case BICC_BVC:
		eicc = ~(ICC_V);
		break;
	    }
	    if (eicc & 1) {
		operand1 = sregs->inst;
		operand1 = ((operand1 << 10) >> 8);	/* sign extend */
		npc = sregs->pc + operand1;
	    } else {
		if (sregs->inst & 0x20000000)
		    sregs->annul = 1;
	    }
	    break;
	case FPBCC:
#ifdef STAT
	    sregs->nbranch++;
#endif
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (ebase.simtime < sregs->ftime) {
		sregs->ftime = ebase.simtime + sregs->hold;
	    }
	    cond = ((sregs->inst >> 25) & 0x0f);
	    fcc = (sregs->fsr >> 10) & 0x3;
	    switch (cond) {
	    case FBN:
		eicc = 0;
		break;
	    case FBNE:
		eicc = (fcc != FCC_E);
		break;
	    case FBLG:
		eicc = (fcc == FCC_L) || (fcc == FCC_G);
		break;
	    case FBUL:
		eicc = (fcc == FCC_L) || (fcc == FCC_U);
		break;
	    case FBL:
		eicc = (fcc == FCC_L);
		break;
	    case FBUG:
		eicc = (fcc == FCC_G) || (fcc == FCC_U);
		break;
	    case FBG:
		eicc = (fcc == FCC_G);
		break;
	    case FBU:
		eicc = (fcc == FCC_U);
		break;
	    case FBA:
		eicc = 1;
		if (sregs->inst & 0x20000000)
		    sregs->annul = 1;
		break;
	    case FBE:
		eicc = !(fcc != FCC_E);
		break;
	    case FBUE:
		eicc = !((fcc == FCC_L) || (fcc == FCC_G));
		break;
	    case FBGE:
		eicc = !((fcc == FCC_L) || (fcc == FCC_U));
		break;
	    case FBUGE:
		eicc = !(fcc == FCC_L);
		break;
	    case FBLE:
		eicc = !((fcc == FCC_G) || (fcc == FCC_U));
		break;
	    case FBULE:
		eicc = !(fcc == FCC_G);
		break;
	    case FBO:
		eicc = !(fcc == FCC_U);
		break;
	    }
	    if (eicc) {
		operand1 = sregs->inst;
		operand1 = ((operand1 << 10) >> 8);	/* sign extend */
		npc = sregs->pc + operand1;
	    } else {
		if (sregs->inst & 0x20000000)
		    sregs->annul = 1;
	    }
	    break;

	default:
	    sregs->trap = TRAP_UNIMP;
	    break;
	}
	break;
    case 1:			/* CALL */
#ifdef STAT
	sregs->nbranch++;
#endif
	sregs->r[(cwp + 15) & 0x7f] = sregs->pc;
	npc = sregs->pc + (sregs->inst << 2);
	break;

    case 2:
	if ((op3 >> 1) == 0x1a) {
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
	    } else {
		rs1 = (sregs->inst >> 14) & 0x1f;
		rs2 = sregs->inst & 0x1f;
		sregs->trap = fpexec(op3, rd, rs1, rs2, sregs);
	    }
	} else {

	    switch (op3) {
	    case TICC:
	        icc = sregs->psr >> 20;
	        cond = ((sregs->inst >> 25) & 0x0f);
	        switch (cond) {
		case BICC_BN:
		    eicc = 0;
		    break;
		case BICC_BE:
		    eicc = ICC_Z;
		    break;
		case BICC_BLE:
		    eicc = ICC_Z | (ICC_N ^ ICC_V);
		    break;
		case BICC_BL:
		    eicc = (ICC_N ^ ICC_V);
		    break;
		case BICC_BLEU:
		    eicc = ICC_C | ICC_Z;
		    break;
		case BICC_BCS:
		    eicc = ICC_C;
		    break;
		case BICC_NEG:
		    eicc = ICC_N;
		    break;
		case BICC_BVS:
		    eicc = ICC_V;
		    break;
	        case BICC_BA:
		    eicc = 1;
		    break;
	        case BICC_BNE:
		    eicc = ~(ICC_Z);
		    break;
	        case BICC_BG:
		    eicc = ~(ICC_Z | (ICC_N ^ ICC_V));
		    break;
	        case BICC_BGE:
		    eicc = ~(ICC_N ^ ICC_V);
		    break;
	        case BICC_BGU:
		    eicc = ~(ICC_C | ICC_Z);
		    break;
	        case BICC_BCC:
		    eicc = ~(ICC_C);
		    break;
	        case BICC_POS:
		    eicc = ~(ICC_N);
		    break;
	        case BICC_BVC:
		    eicc = ~(ICC_V);
		    break;
		}
		if (eicc & 1) {
		    sregs->trap = (0x80 | ((rs1 + operand2) & 0x7f));
		}
		break;

	    case MULScc:
		operand1 =
		    (((sregs->psr & PSR_V) ^ ((sregs->psr & PSR_N) >> 2))
		     << 10) | (rs1 >> 1);
		if ((sregs->y & 1) == 0)
		    operand2 = 0;
		*rdd = operand1 + operand2;
		sregs->y = (rs1 << 31) | (sregs->y >> 1);
		sregs->psr = add_cc(sregs->psr, operand1, operand2, *rdd);
		break;
	    case DIVScc:
		{
		  int sign;
		  uint32 result, remainder;
		  int c0, y31;

		  if (!sparclite) {
		     sregs->trap = TRAP_UNIMP;
                     break;
		  }

		  sign = ((sregs->psr & PSR_V) != 0) ^ ((sregs->psr & PSR_N) != 0);

		  remainder = (sregs->y << 1) | (rs1 >> 31);

		  /* If true sign is positive, calculate remainder - divisor.
		     Otherwise, calculate remainder + divisor.  */
		  if (sign == 0)
		    operand2 = ~operand2 + 1;
		  result = remainder + operand2;

		  /* The SPARClite User's Manual is not clear on how
		     the "carry out" of the above ALU operation is to
		     be calculated.  From trial and error tests
		     on the the chip itself, it appears that it is
		     a normal addition carry, and not a subtraction borrow,
		     even in cases where the divisor is subtracted
		     from the remainder.  FIXME: get the true story
		     from Fujitsu. */
		  c0 = result < (uint32) remainder
		       || result < (uint32) operand2;

		  if (result & 0x80000000)
		    sregs->psr |= PSR_N;
		  else
		    sregs->psr &= ~PSR_N;

		  y31 = (sregs->y & 0x80000000) == 0x80000000;

		  if (result == 0 && sign == y31)
		    sregs->psr |= PSR_Z;
		  else
		    sregs->psr &= ~PSR_Z;

		  sign = (sign && !y31) || (!c0 && (sign || !y31));

		  if (sign ^ (result >> 31))
		    sregs->psr |= PSR_V;
		  else
		    sregs->psr &= ~PSR_V;

		  if (!sign)
		    sregs->psr |= PSR_C;
		  else
		    sregs->psr &= ~PSR_C;

		  sregs->y = result;

		  if (rd != 0)
		    *rdd = (rs1 << 1) | !sign;
		}
		break;
	    case SMUL:
		{
		  mul64 (rs1, operand2, &sregs->y, rdd, 1);
		}
		break;
	    case SMULCC:
		{
		  uint32 result;

		  mul64 (rs1, operand2, &sregs->y, &result, 1);

		  if (result & 0x80000000)
		    sregs->psr |= PSR_N;
		  else
		    sregs->psr &= ~PSR_N;

		  if (result == 0)
		    sregs->psr |= PSR_Z;
		  else
		    sregs->psr &= ~PSR_Z;

		  *rdd = result;
		}
		break;
	    case UMUL:
		{
		  mul64 (rs1, operand2, &sregs->y, rdd, 0);
		}
		break;
	    case UMULCC:
		{
		  uint32 result;

		  mul64 (rs1, operand2, &sregs->y, &result, 0);

		  if (result & 0x80000000)
		    sregs->psr |= PSR_N;
		  else
		    sregs->psr &= ~PSR_N;

		  if (result == 0)
		    sregs->psr |= PSR_Z;
		  else
		    sregs->psr &= ~PSR_Z;

		  *rdd = result;
		}
		break;
	    case SDIV:
		{
		  if (sparclite) {
		     sregs->trap = TRAP_UNIMP;
                     break;
		  }

		  if (operand2 == 0) {
		    sregs->trap = TRAP_DIV0;
		    break;
		  }

		  div64 (sregs->y, rs1, operand2, rdd, 1);
		}
		break;
	    case SDIVCC:
		{
		  uint32 result;

		  if (sparclite) {
		     sregs->trap = TRAP_UNIMP;
                     break;
		  }

		  if (operand2 == 0) {
		    sregs->trap = TRAP_DIV0;
		    break;
		  }

		  div64 (sregs->y, rs1, operand2, &result, 1);

		  if (result & 0x80000000)
		    sregs->psr |= PSR_N;
		  else
		    sregs->psr &= ~PSR_N;

		  if (result == 0)
		    sregs->psr |= PSR_Z;
		  else
		    sregs->psr &= ~PSR_Z;

		  /* FIXME: should set overflow flag correctly.  */
		  sregs->psr &= ~(PSR_C | PSR_V);

		  *rdd = result;
		}
		break;
	    case UDIV:
		{
		  if (sparclite) {
		     sregs->trap = TRAP_UNIMP;
                     break;
		  }

		  if (operand2 == 0) {
		    sregs->trap = TRAP_DIV0;
		    break;
		  }

		  div64 (sregs->y, rs1, operand2, rdd, 0);
		}
		break;
	    case UDIVCC:
		{
		  uint32 result;

		  if (sparclite) {
		     sregs->trap = TRAP_UNIMP;
                     break;
		  }

		  if (operand2 == 0) {
		    sregs->trap = TRAP_DIV0;
		    break;
		  }

		  div64 (sregs->y, rs1, operand2, &result, 0);

		  if (result & 0x80000000)
		    sregs->psr |= PSR_N;
		  else
		    sregs->psr &= ~PSR_N;

		  if (result == 0)
		    sregs->psr |= PSR_Z;
		  else
		    sregs->psr &= ~PSR_Z;

		  /* FIXME: should set overflow flag correctly.  */
		  sregs->psr &= ~(PSR_C | PSR_V);

		  *rdd = result;
		}
		break;
	    case IXNOR:
		*rdd = rs1 ^ ~operand2;
		break;
	    case IXNORCC:
		*rdd = rs1 ^ ~operand2;
		log_cc(*rdd, sregs);
		break;
	    case IXOR:
		*rdd = rs1 ^ operand2;
		break;
	    case IXORCC:
		*rdd = rs1 ^ operand2;
		log_cc(*rdd, sregs);
		break;
	    case IOR:
		*rdd = rs1 | operand2;
		break;
	    case IORCC:
		*rdd = rs1 | operand2;
		log_cc(*rdd, sregs);
		break;
	    case IORN:
		*rdd = rs1 | ~operand2;
		break;
	    case IORNCC:
		*rdd = rs1 | ~operand2;
		log_cc(*rdd, sregs);
		break;
	    case IANDNCC:
		*rdd = rs1 & ~operand2;
		log_cc(*rdd, sregs);
		break;
	    case IANDN:
		*rdd = rs1 & ~operand2;
		break;
	    case IAND:
		*rdd = rs1 & operand2;
		break;
	    case IANDCC:
		*rdd = rs1 & operand2;
		log_cc(*rdd, sregs);
		break;
	    case SUB:
		*rdd = rs1 - operand2;
		break;
	    case SUBCC:
		*rdd = rs1 - operand2;
		sregs->psr = sub_cc(sregs->psr, rs1, operand2, *rdd);
		break;
	    case SUBX:
		*rdd = rs1 - operand2 - ((sregs->psr >> 20) & 1);
		break;
	    case SUBXCC:
		*rdd = rs1 - operand2 - ((sregs->psr >> 20) & 1);
		sregs->psr = sub_cc(sregs->psr, rs1, operand2, *rdd);
		break;
	    case ADD:
		*rdd = rs1 + operand2;
		break;
	    case ADDCC:
		*rdd = rs1 + operand2;
		sregs->psr = add_cc(sregs->psr, rs1, operand2, *rdd);
		break;
	    case ADDX:
		*rdd = rs1 + operand2 + ((sregs->psr >> 20) & 1);
		break;
	    case ADDXCC:
		*rdd = rs1 + operand2 + ((sregs->psr >> 20) & 1);
		sregs->psr = add_cc(sregs->psr, rs1, operand2, *rdd);
		break;
	    case TADDCC:
		*rdd = rs1 + operand2;
		sregs->psr = add_cc(sregs->psr, rs1, operand2, *rdd);
		if ((rs1 | operand2) & 0x3)
		    sregs->psr |= PSR_V;
		break;
	    case TSUBCC:
		*rdd = rs1 - operand2;
		sregs->psr = sub_cc (sregs->psr, rs1, operand2, *rdd);
		if ((rs1 | operand2) & 0x3)
		    sregs->psr |= PSR_V;
		break;
	    case TADDCCTV:
		*rdd = rs1 + operand2;
		result = add_cc(0, rs1, operand2, *rdd);
		if ((rs1 | operand2) & 0x3)
		    result |= PSR_V;
		if (result & PSR_V) {
		    sregs->trap = TRAP_TAG;
		} else {
		    sregs->psr = (sregs->psr & ~PSR_CC) | result;
		}
		break;
	    case TSUBCCTV:
		*rdd = rs1 - operand2;
		result = add_cc (0, rs1, operand2, *rdd);
		if ((rs1 | operand2) & 0x3)
		    result |= PSR_V;
		if (result & PSR_V)
		  {
		      sregs->trap = TRAP_TAG;
		  }
		else
		  {
		      sregs->psr = (sregs->psr & ~PSR_CC) | result;
		  }
		break;
	    case SLL:
		*rdd = rs1 << (operand2 & 0x1f);
		break;
	    case SRL:
		*rdd = rs1 >> (operand2 & 0x1f);
		break;
	    case SRA:
		*rdd = ((int) rs1) >> (operand2 & 0x1f);
		break;
	    case FLUSH:
		if (ift) sregs->trap = TRAP_UNIMP;
		break;
	    case SAVE:
		new_cwp = ((sregs->psr & PSR_CWP) - 1) & PSR_CWP;
		if (sregs->wim & (1 << new_cwp)) {
		    sregs->trap = TRAP_WOFL;
		    break;
		}
		if (rd > 7)
		    rdd = &(sregs->r[((new_cwp << 4) + rd) & 0x7f]);
		*rdd = rs1 + operand2;
		sregs->psr = (sregs->psr & ~PSR_CWP) | new_cwp;
		break;
	    case RESTORE:

		new_cwp = ((sregs->psr & PSR_CWP) + 1) & PSR_CWP;
		if (sregs->wim & (1 << new_cwp)) {
		    sregs->trap = TRAP_WUFL;
		    break;
		}
		if (rd > 7)
		    rdd = &(sregs->r[((new_cwp << 4) + rd) & 0x7f]);
		*rdd = rs1 + operand2;
		sregs->psr = (sregs->psr & ~PSR_CWP) | new_cwp;
		break;
	    case RDPSR:
		if (!(sregs->psr & PSR_S)) {
		    sregs->trap = TRAP_PRIVI;
		    break;
		}
		*rdd = sregs->psr;
		break;
	    case RDY:
                if (!sparclite)
                    *rdd = sregs->y;
                else {
                    int rs1_is_asr = (sregs->inst >> 14) & 0x1f;
                    if ( 0 == rs1_is_asr )
                        *rdd = sregs->y;
                    else if ( 17 == rs1_is_asr )
                        *rdd = sregs->asr17;
                    else {
                        sregs->trap = TRAP_UNIMP;
                        break;
                    }
                }
		break;
	    case RDWIM:
		if (!(sregs->psr & PSR_S)) {
		    sregs->trap = TRAP_PRIVI;
		    break;
		}
		*rdd = sregs->wim;
		break;
	    case RDTBR:
		if (!(sregs->psr & PSR_S)) {
		    sregs->trap = TRAP_PRIVI;
		    break;
		}
		*rdd = sregs->tbr;
		break;
	    case WRPSR:
		if ((sregs->psr & 0x1f) > 7) {
		    sregs->trap = TRAP_UNIMP;
		    break;
		}
		if (!(sregs->psr & PSR_S)) {
		    sregs->trap = TRAP_PRIVI;
		    break;
		}
		sregs->psr = (sregs->psr & 0xff000000) |
			(rs1 ^ operand2) & 0x00f03fff;
		break;
	    case WRWIM:
		if (!(sregs->psr & PSR_S)) {
		    sregs->trap = TRAP_PRIVI;
		    break;
		}
		sregs->wim = (rs1 ^ operand2) & 0x0ff;
		break;
	    case WRTBR:
		if (!(sregs->psr & PSR_S)) {
		    sregs->trap = TRAP_PRIVI;
		    break;
		}
		sregs->tbr = (sregs->tbr & 0x00000ff0) |
		    ((rs1 ^ operand2) & 0xfffff000);
		break;
	    case WRY:
                if (!sparclite)
                    sregs->y = (rs1 ^ operand2);
                else {
                    if ( 0 == rd )
                        sregs->y = (rs1 ^ operand2);
                    else if ( 17 == rd )
                        sregs->asr17 = (rs1 ^ operand2);
                    else {
                        sregs->trap = TRAP_UNIMP;
                        break;
                    }
                }
		break;
	    case JMPL:

#ifdef STAT
		sregs->nbranch++;
#endif
		sregs->icnt = T_JMPL;	/* JMPL takes two cycles */
		if (rs1 & 0x3) {
		    sregs->trap = TRAP_UNALI;
		    break;
		}
		*rdd = sregs->pc;
		npc = rs1 + operand2;
		break;
	    case RETT:
		address = rs1 + operand2;
		new_cwp = ((sregs->psr & PSR_CWP) + 1) & PSR_CWP;
		sregs->icnt = T_RETT;	/* RETT takes two cycles */
		if (sregs->psr & PSR_ET) {
		    sregs->trap = TRAP_UNIMP;
		    break;
		}
		if (!(sregs->psr & PSR_S)) {
		    sregs->trap = TRAP_PRIVI;
		    break;
		}
		if (sregs->wim & (1 << new_cwp)) {
		    sregs->trap = TRAP_WUFL;
		    break;
		}
		if (address & 0x3) {
		    sregs->trap = TRAP_UNALI;
		    break;
		}
		sregs->psr = (sregs->psr & ~PSR_CWP) | new_cwp | PSR_ET;
		sregs->psr =
		    (sregs->psr & ~PSR_S) | ((sregs->psr & PSR_PS) << 1);
		npc = address;
		break;

	    case SCAN:
		{
		  uint32 result, mask;
		  int i;

		  if (!sparclite) {
		     sregs->trap = TRAP_UNIMP;
                     break;
		  }
		  mask = (operand2 & 0x80000000) | (operand2 >> 1);
		  result = rs1 ^ mask;

		  for (i = 0; i < 32; i++) {
		    if (result & 0x80000000)
		      break;
		    result <<= 1;
		  }

		  *rdd = i == 32 ? 63 : i;
		}
		break;

	    default:
		sregs->trap = TRAP_UNIMP;
		break;
	    }
	}
	break;
    case 3:			/* Load/store instructions */

	address = rs1 + operand2;

	if (sregs->psr & PSR_S)
	    asi = 11;
	 else
	    asi = 10;

	if (op3 & 4) {
	    sregs->icnt = T_ST;	/* Set store instruction count */
#ifdef STAT
	    sregs->nstore++;
#endif
	} else {
	    sregs->icnt = T_LD;	/* Set load instruction count */
#ifdef STAT
	    sregs->nload++;
#endif
	}

	/* Decode load/store instructions */

	switch (op3) {
	case LDDA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case LDD:
	    if (address & 0x7) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    if (rd & 1) {
		rd &= 0x1e;
		if (rd > 7)
		    rdd = &(sregs->r[(cwp + rd) & 0x7f]);
		else
		    rdd = &(sregs->g[rd]);
	    }
	    mexc = memory_read (asi, address, ddata, 2, &ws);
	    sregs->hold += ws;
	    mexc |= memory_read (asi, address+4, &ddata[1], 2, &ws);
	    sregs->hold += ws;
	    sregs->icnt = T_LDD;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    } else {
		rdd[0] = ddata[0];
		rdd[1] = ddata[1];
#ifdef STAT
		sregs->nload++;	/* Double load counts twice */
#endif
	    }
	    break;

	case LDA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case LD:
	    if (address & 0x3) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    mexc = memory_read(asi, address, &data, 2, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    } else {
		*rdd = data;
	    }
	    break;
	case LDSTUBA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case LDSTUB:
	    mexc = memory_read(asi, address, &data, 0, &ws);
	    sregs->hold += ws;
	    sregs->icnt = T_LDST;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
		break;
	    }
	    data = extract_byte (data, address);
	    *rdd = data;
	    data = 0x0ff;
	    mexc = memory_write(asi, address, &data, 0, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    }
#ifdef STAT
	    sregs->nload++;
#endif
	    break;
	case LDSBA:
	case LDUBA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case LDSB:
	case LDUB:
	    mexc = memory_read(asi, address, &data, 0, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
		break;
	    }
	    if (op3 == LDSB)
	        data = extract_byte_signed (data, address);
	    else
	        data = extract_byte (data, address);
	    *rdd = data;
	    break;
	case LDSHA:
	case LDUHA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case LDSH:
	case LDUH:
	    if (address & 0x1) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    mexc = memory_read(asi, address, &data, 1, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
		break;
	    }
	    if (op3 == LDSH)
	        data = extract_short_signed (data, address);
	    else
	        data = extract_short (data, address);
	    *rdd = data;
	    break;
	case LDF:
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (address & 0x3) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    if (ebase.simtime < sregs->ftime) {
		if ((sregs->frd == rd) || (sregs->frs1 == rd) ||
		    (sregs->frs2 == rd))
		    sregs->fhold += (sregs->ftime - ebase.simtime);
	    }
	    mexc = memory_read(asi, address, &data, 2, &ws);
	    sregs->hold += ws;
	    sregs->flrd = rd;
	    sregs->ltime = ebase.simtime + sregs->icnt + FLSTHOLD +
		sregs->hold + sregs->fhold;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    } else {
		sregs->fs[rd] = *((float32 *) & data);
	    }
	    break;
	case LDDF:
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (address & 0x7) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    if (ebase.simtime < sregs->ftime) {
		if (((sregs->frd >> 1) == (rd >> 1)) ||
		    ((sregs->frs1 >> 1) == (rd >> 1)) ||
		    ((sregs->frs2 >> 1) == (rd >> 1)))
		    sregs->fhold += (sregs->ftime - ebase.simtime);
	    }
	    mexc = memory_read (asi, address, ddata, 2, &ws);
	    sregs->hold += ws;
	    mexc |= memory_read (asi, address+4, &ddata[1], 2, &ws);
	    sregs->hold += ws;
	    sregs->icnt = T_LDD;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    } else {
		rd &= 0x1E;
		sregs->flrd = rd;
		sregs->fs[rd] = *((float32 *) & ddata[0]);
#ifdef STAT
		sregs->nload++;	/* Double load counts twice */
#endif
		sregs->fs[rd + 1] = *((float32 *) & ddata[1]);
		sregs->ltime = ebase.simtime + sregs->icnt + FLSTHOLD +
			       sregs->hold + sregs->fhold;
	    }
	    break;
	case LDFSR:
	    if (ebase.simtime < sregs->ftime) {
		sregs->fhold += (sregs->ftime - ebase.simtime);
	    }
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (address & 0x3) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    mexc = memory_read(asi, address, &data, 2, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    } else {
		sregs->fsr =
		    (sregs->fsr & 0x7FF000) | (data & ~0x7FF000);
		set_fsr(sregs->fsr);
	    }
	    break;
	case STFSR:
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (address & 0x3) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    if (ebase.simtime < sregs->ftime) {
		sregs->fhold += (sregs->ftime - ebase.simtime);
	    }
	    mexc = memory_write(asi, address, &sregs->fsr, 2, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    }
	    break;

	case STA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case ST:
	    if (address & 0x3) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    mexc = memory_write(asi, address, rdd, 2, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    }
	    break;
	case STBA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case STB:
	    mexc = memory_write(asi, address, rdd, 0, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    }
	    break;
	case STDA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case STD:
	    if (address & 0x7) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    if (rd & 1) {
		rd &= 0x1e;
		if (rd > 7)
		    rdd = &(sregs->r[(cwp + rd) & 0x7f]);
		else
		    rdd = &(sregs->g[rd]);
	    }
	    mexc = memory_write(asi, address, rdd, 3, &ws);
	    sregs->hold += ws;
	    sregs->icnt = T_STD;
#ifdef STAT
	    sregs->nstore++;	/* Double store counts twice */
#endif
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
		break;
	    }
	    break;
	case STDFQ:
	    if ((sregs->psr & 0x1f) > 7) {
		sregs->trap = TRAP_UNIMP;
		break;
	    }
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (address & 0x7) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    if (!(sregs->fsr & FSR_QNE)) {
		sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_SEQ_ERR;
		break;
	    }
	    rdd = &(sregs->fpq[0]);
	    mexc = memory_write(asi, address, rdd, 3, &ws);
	    sregs->hold += ws;
	    sregs->icnt = T_STD;
#ifdef STAT
	    sregs->nstore++;	/* Double store counts twice */
#endif
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
		break;
	    } else {
		sregs->fsr &= ~FSR_QNE;
		sregs->fpstate = FP_EXE_MODE;
	    }
	    break;
	case STHA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case STH:
	    if (address & 0x1) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    mexc = memory_write(asi, address, rdd, 1, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    }
	    break;
	case STF:
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (address & 0x3) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    if (ebase.simtime < sregs->ftime) {
		if (sregs->frd == rd)
		    sregs->fhold += (sregs->ftime - ebase.simtime);
	    }
	    mexc = memory_write(asi, address, &sregs->fsi[rd], 2, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    }
	    break;
	case STDF:
	    if (!((sregs->psr & PSR_EF) && FP_PRES)) {
		sregs->trap = TRAP_FPDIS;
		break;
	    }
	    if (address & 0x7) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    rd &= 0x1E;
	    if (ebase.simtime < sregs->ftime) {
		if ((sregs->frd == rd) || (sregs->frd + 1 == rd))
		    sregs->fhold += (sregs->ftime - ebase.simtime);
	    }
	    mexc = memory_write(asi, address, &sregs->fsi[rd], 3, &ws);
	    sregs->hold += ws;
	    sregs->icnt = T_STD;
#ifdef STAT
	    sregs->nstore++;	/* Double store counts twice */
#endif
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
	    }
	    break;
	case SWAPA:
	    if (!chk_asi(sregs, &asi, op3)) break;
	case SWAP:
	    if (address & 0x3) {
		sregs->trap = TRAP_UNALI;
		break;
	    }
	    mexc = memory_read(asi, address, &data, 2, &ws);
	    sregs->hold += ws;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
		break;
	    }
	    mexc = memory_write(asi, address, rdd, 2, &ws);
	    sregs->hold += ws;
	    sregs->icnt = T_LDST;
	    if (mexc) {
		sregs->trap = TRAP_DEXC;
		break;
	    } else
		*rdd = data;
#ifdef STAT
	    sregs->nload++;
#endif
	    break;


	default:
	    sregs->trap = TRAP_UNIMP;
	    break;
	}

#ifdef LOAD_DEL

	if (!(op3 & 4)) {
	    sregs->ildtime = ebase.simtime + sregs->hold + sregs->icnt;
	    sregs->ildreg = rd;
	    if ((op3 | 0x10) == 0x13)
		sregs->ildreg |= 1;	/* Double load, odd register loaded
					 * last */
	}
#endif
	break;

    default:
	sregs->trap = TRAP_UNIMP;
	break;
    }
    sregs->g[0] = 0;
    if (!sregs->trap) {
	sregs->pc = pc;
	sregs->npc = npc;
    }
    return 0;
}

#define T_FABSs		2
#define T_FADDs		4
#define T_FADDd		4
#define T_FCMPs		4
#define T_FCMPd		4
#define T_FDIVs		20
#define T_FDIVd		35
#define T_FMOVs		2
#define T_FMULs		5
#define T_FMULd		9
#define T_FNEGs		2
#define T_FSQRTs	37
#define T_FSQRTd	65
#define T_FSUBs		4
#define T_FSUBd		4
#define T_FdTOi		7
#define T_FdTOs		3
#define T_FiTOs		6
#define T_FiTOd		6
#define T_FsTOi		6
#define T_FsTOd		2

#define FABSs	0x09
#define FADDs	0x41
#define FADDd	0x42
#define FCMPs	0x51
#define FCMPd	0x52
#define FCMPEs	0x55
#define FCMPEd	0x56
#define FDIVs	0x4D
#define FDIVd	0x4E
#define FMOVs	0x01
#define FMULs	0x49
#define FMULd	0x4A
#define FNEGs	0x05
#define FSQRTs	0x29
#define FSQRTd	0x2A
#define FSUBs	0x45
#define FSUBd	0x46
#define FdTOi	0xD2
#define FdTOs	0xC6
#define FiTOs	0xC4
#define FiTOd	0xC8
#define FsTOi	0xD1
#define FsTOd	0xC9


static int
fpexec(op3, rd, rs1, rs2, sregs)
    uint32          op3, rd, rs1, rs2;
    struct pstate  *sregs;
{
    uint32          opf, tem, accex;
    int32           fcc;
    uint32          ldadj;

    if (sregs->fpstate == FP_EXC_MODE) {
	sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_SEQ_ERR;
	sregs->fpstate = FP_EXC_PE;
	return 0;
    }
    if (sregs->fpstate == FP_EXC_PE) {
	sregs->fpstate = FP_EXC_MODE;
	return TRAP_FPEXC;
    }
    opf = (sregs->inst >> 5) & 0x1ff;

    /*
     * Check if we already have an FPop in the pipe. If so, halt until it is
     * finished by incrementing fhold with the remaining execution time
     */

    if (ebase.simtime < sregs->ftime) {
	sregs->fhold = (sregs->ftime - ebase.simtime);
    } else {
	sregs->fhold = 0;

	/* Check load dependencies. */

	if (ebase.simtime < sregs->ltime) {

	    /* Don't check rs1 if single operand instructions */

	    if (((opf >> 6) == 0) || ((opf >> 6) == 3))
		rs1 = 32;

	    /* Adjust for double floats */

	    ldadj = opf & 1;
	    if (!(((sregs->flrd - rs1) >> ldadj) && ((sregs->flrd - rs2) >> ldadj)))
		sregs->fhold++;
	}
    }

    sregs->finst++;

    sregs->frs1 = rs1;		/* Store src and dst for dependecy check */
    sregs->frs2 = rs2;
    sregs->frd = rd;

    sregs->ftime = ebase.simtime + sregs->hold + sregs->fhold;

    /* SPARC is big-endian - swap double floats if host is little-endian */
    /* This is ugly - I know ... */

    /* FIXME: should use (HOST_BYTE_ORDER == CURRENT_TARGET_BYTE_ORDER)
       but what about machines where float values are different endianness
       from integer values? */

#ifdef HOST_LITTLE_ENDIAN
    rs1 &= 0x1f;
    switch (opf) {
	case FADDd:
	case FDIVd:
	case FMULd:
	case FSQRTd:
	case FSUBd:
        case FCMPd:
        case FCMPEd:
	case FdTOi:
	case FdTOs:
    	    sregs->fdp[rs1 | 1] = sregs->fs[rs1 & ~1];
    	    sregs->fdp[rs1 & ~1] = sregs->fs[rs1 | 1];
    	    sregs->fdp[rs2 | 1] = sregs->fs[rs2 & ~1];
    	    sregs->fdp[rs2 & ~1] = sregs->fs[rs2 | 1];
    default:
      break;
    }
#endif

    clear_accex();

    switch (opf) {
    case FABSs:
	sregs->fs[rd] = fabs(sregs->fs[rs2]);
	sregs->ftime += T_FABSs;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FADDs:
	sregs->fs[rd] = sregs->fs[rs1] + sregs->fs[rs2];
	sregs->ftime += T_FADDs;
	break;
    case FADDd:
	sregs->fd[rd >> 1] = sregs->fd[rs1 >> 1] + sregs->fd[rs2 >> 1];
	sregs->ftime += T_FADDd;
	break;
    case FCMPs:
    case FCMPEs:
	if (sregs->fs[rs1] == sregs->fs[rs2])
	    fcc = 3;
	else if (sregs->fs[rs1] < sregs->fs[rs2])
	    fcc = 2;
	else if (sregs->fs[rs1] > sregs->fs[rs2])
	    fcc = 1;
	else
	    fcc = 0;
	sregs->fsr |= 0x0C00;
	sregs->fsr &= ~(fcc << 10);
	sregs->ftime += T_FCMPs;
	sregs->frd = 32;	/* rd ignored */
	if ((fcc == 0) && (opf == FCMPEs)) {
	    sregs->fpstate = FP_EXC_PE;
	    sregs->fsr = (sregs->fsr & ~0x1C000) | (1 << 14);
	}
	break;
    case FCMPd:
    case FCMPEd:
	if (sregs->fd[rs1 >> 1] == sregs->fd[rs2 >> 1])
	    fcc = 3;
	else if (sregs->fd[rs1 >> 1] < sregs->fd[rs2 >> 1])
	    fcc = 2;
	else if (sregs->fd[rs1 >> 1] > sregs->fd[rs2 >> 1])
	    fcc = 1;
	else
	    fcc = 0;
	sregs->fsr |= 0x0C00;
	sregs->fsr &= ~(fcc << 10);
	sregs->ftime += T_FCMPd;
	sregs->frd = 32;	/* rd ignored */
	if ((fcc == 0) && (opf == FCMPEd)) {
	    sregs->fpstate = FP_EXC_PE;
	    sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_IEEE;
	}
	break;
    case FDIVs:
	sregs->fs[rd] = sregs->fs[rs1] / sregs->fs[rs2];
	sregs->ftime += T_FDIVs;
	break;
    case FDIVd:
	sregs->fd[rd >> 1] = sregs->fd[rs1 >> 1] / sregs->fd[rs2 >> 1];
	sregs->ftime += T_FDIVd;
	break;
    case FMOVs:
	sregs->fs[rd] = sregs->fs[rs2];
	sregs->ftime += T_FMOVs;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FMULs:
	sregs->fs[rd] = sregs->fs[rs1] * sregs->fs[rs2];
	sregs->ftime += T_FMULs;
	break;
    case FMULd:
	sregs->fd[rd >> 1] = sregs->fd[rs1 >> 1] * sregs->fd[rs2 >> 1];
	sregs->ftime += T_FMULd;
	break;
    case FNEGs:
	sregs->fs[rd] = -sregs->fs[rs2];
	sregs->ftime += T_FNEGs;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FSQRTs:
	if (sregs->fs[rs2] < 0.0) {
	    sregs->fpstate = FP_EXC_PE;
	    sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_IEEE;
	    sregs->fsr = (sregs->fsr & 0x1f) | 0x10;
	    break;
	}
	sregs->fs[rd] = sqrt(sregs->fs[rs2]);
	sregs->ftime += T_FSQRTs;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FSQRTd:
	if (sregs->fd[rs2 >> 1] < 0.0) {
	    sregs->fpstate = FP_EXC_PE;
	    sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_IEEE;
	    sregs->fsr = (sregs->fsr & 0x1f) | 0x10;
	    break;
	}
	sregs->fd[rd >> 1] = sqrt(sregs->fd[rs2 >> 1]);
	sregs->ftime += T_FSQRTd;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FSUBs:
	sregs->fs[rd] = sregs->fs[rs1] - sregs->fs[rs2];
	sregs->ftime += T_FSUBs;
	break;
    case FSUBd:
	sregs->fd[rd >> 1] = sregs->fd[rs1 >> 1] - sregs->fd[rs2 >> 1];
	sregs->ftime += T_FSUBd;
	break;
    case FdTOi:
	sregs->fsi[rd] = (int) sregs->fd[rs2 >> 1];
	sregs->ftime += T_FdTOi;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FdTOs:
	sregs->fs[rd] = (float32) sregs->fd[rs2 >> 1];
	sregs->ftime += T_FdTOs;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FiTOs:
	sregs->fs[rd] = (float32) sregs->fsi[rs2];
	sregs->ftime += T_FiTOs;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FiTOd:
	sregs->fd[rd >> 1] = (float64) sregs->fsi[rs2];
	sregs->ftime += T_FiTOd;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FsTOi:
	sregs->fsi[rd] = (int) sregs->fs[rs2];
	sregs->ftime += T_FsTOi;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;
    case FsTOd:
	sregs->fd[rd >> 1] = sregs->fs[rs2];
	sregs->ftime += T_FsTOd;
	sregs->frs1 = 32;	/* rs1 ignored */
	break;

    default:
	sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_UNIMP;
	sregs->fpstate = FP_EXC_PE;
    }

#ifdef ERRINJ
    if (errftt) {
	sregs->fsr = (sregs->fsr & ~FSR_TT) | (errftt << 14);
	sregs->fpstate = FP_EXC_PE;
	if (sis_verbose) printf("Inserted fpu error %X\n",errftt);
	errftt = 0;
    }
#endif

    accex = get_accex();

#ifdef HOST_LITTLE_ENDIAN
    switch (opf) {
    case FADDd:
    case FDIVd:
    case FMULd:
    case FSQRTd:
    case FSUBd:
    case FiTOd:
    case FsTOd:
	sregs->fs[rd & ~1] = sregs->fdp[rd | 1];
	sregs->fs[rd | 1] = sregs->fdp[rd & ~1];
    default:
      break;
    }
#endif
    if (sregs->fpstate == FP_EXC_PE) {
	sregs->fpq[0] = sregs->pc;
	sregs->fpq[1] = sregs->inst;
	sregs->fsr |= FSR_QNE;
    } else {
	tem = (sregs->fsr >> 23) & 0x1f;
	if (tem & accex) {
	    sregs->fpstate = FP_EXC_PE;
	    sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_IEEE;
	    sregs->fsr = ((sregs->fsr & ~0x1f) | accex);
	} else {
	    sregs->fsr = ((((sregs->fsr >> 5) | accex) << 5) | accex);
	}
	if (sregs->fpstate == FP_EXC_PE) {
	    sregs->fpq[0] = sregs->pc;
	    sregs->fpq[1] = sregs->inst;
	    sregs->fsr |= FSR_QNE;
	}
    }
    clear_accex();

    return 0;


}

static int
chk_asi(sregs, asi, op3)
    struct pstate  *sregs;
    uint32 *asi, op3;

{
    if (!(sregs->psr & PSR_S)) {
	sregs->trap = TRAP_PRIVI;
	return 0;
    } else if (sregs->inst & INST_I) {
	sregs->trap = TRAP_UNIMP;
	return 0;
    } else
	*asi = (sregs->inst >> 5) & 0x0ff;
    return 1;
}

int
execute_trap(sregs)
    struct pstate  *sregs;
{
    int32           cwp;

    if (sregs->trap == 256) {
	sregs->pc = 0;
	sregs->npc = 4;
	sregs->trap = 0;
    } else if (sregs->trap == 257) {
	    return ERROR;
    } else {

	if ((sregs->psr & PSR_ET) == 0)
	    return ERROR;

	sregs->tbr = (sregs->tbr & 0xfffff000) | (sregs->trap << 4);
	sregs->trap = 0;
	sregs->psr &= ~PSR_ET;
	sregs->psr |= ((sregs->psr & PSR_S) >> 1);
	sregs->annul = 0;
	sregs->psr = (((sregs->psr & PSR_CWP) - 1) & 0x7) | (sregs->psr & ~PSR_CWP);
	cwp = ((sregs->psr & PSR_CWP) << 4);
	sregs->r[(cwp + 17) & 0x7f] = sregs->pc;
	sregs->r[(cwp + 18) & 0x7f] = sregs->npc;
	sregs->psr |= PSR_S;
	sregs->pc = sregs->tbr;
	sregs->npc = sregs->tbr + 4;

        if ( 0 != (1 & sregs->asr17) ) {
            /* single vector trapping! */
            sregs->pc = sregs->tbr & 0xfffff000;
            sregs->npc = sregs->pc + 4;
        }

	/* Increase simulator time */
	sregs->icnt = TRAP_C;

    }


    return 0;

}

extern struct irqcell irqarr[16];

int
check_interrupts(sregs)
    struct pstate  *sregs;
{
#ifdef ERRINJ
    if (errtt) {
	sregs->trap = errtt;
	if (sis_verbose) printf("Inserted error trap 0x%02X\n",errtt);
	errtt = 0;
    }
#endif

    if ((ext_irl) && (sregs->psr & PSR_ET) &&
	((ext_irl == 15) || (ext_irl > (int) ((sregs->psr & PSR_PIL) >> 8)))) {
	if (sregs->trap == 0) {
	    sregs->trap = 16 + ext_irl;
	    irqarr[ext_irl & 0x0f].callback(irqarr[ext_irl & 0x0f].arg);
	    return 1;
	}
    }
    return 0;
}

void
init_regs(sregs)
    struct pstate  *sregs;
{
    sregs->pc = 0;
    sregs->npc = 4;
    sregs->trap = 0;
    sregs->psr &= 0x00f03fdf;
    sregs->psr |= 0x11000080;	/* Set supervisor bit */
    sregs->breakpoint = 0;
    sregs->annul = 0;
    sregs->fpstate = FP_EXE_MODE;
    sregs->fpqn = 0;
    sregs->ftime = 0;
    sregs->ltime = 0;
    sregs->err_mode = 0;
    ext_irl = 0;
    sregs->g[0] = 0;
#ifdef HOST_LITTLE_ENDIAN
    sregs->fdp = (float32 *) sregs->fd;
    sregs->fsi = (int32 *) sregs->fs;
#else
    sregs->fs = (float32 *) sregs->fd;
    sregs->fsi = (int32 *) sregs->fd;
#endif
    sregs->fsr = 0;
    sregs->fpu_pres = !nfp;
    set_fsr(sregs->fsr);
    sregs->bphit = 0;
    sregs->ildreg = 0;
    sregs->ildtime = 0;

    sregs->y = 0;
    sregs->asr17 = 0;

    sregs->rett_err = 0;
    sregs->jmpltime = 0;
}
