-/*
- * This file is part of SIS.
- *
- * SIS, SPARC instruction simulator V1.8 Copyright (C) 1995 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 2 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, write to the Free Software Foundation, Inc., 675
- * Mass Ave, Cambridge, MA 02139, USA.
- *
- */
+/* This file is part of SIS (SPARC instruction simulator)
+ Copyright (C) 1995-2020 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 "end.h"
#include <math.h>
#include <stdio.h>
-extern int32 ext_irl, irqpend, iurev0, sis_verbose;
+extern int32 sis_verbose, sparclite;
+int ext_irl = 0;
/* Load/store interlock delay */
#define FLSTHOLD 1
#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 PSR_CWP 0x7
#define PSR_PIL 0x0f00
-#define ICC_N sregs->psr
-#define ICC_Z (sregs->psr << 1)
-#define ICC_V (sregs->psr << 2)
-#define ICC_C (sregs->psr << 3)
+#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_FPEXC 8
#define TRAP_DEXC 9
#define TRAP_TAG 10
+#define TRAP_DIV0 0x2a
#define FSR_TT 0x1C000
#define FP_IEEE 0x04000
#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 ADDX 0x08
#define ADDXCC 0x18
#define TADDCC 0x20
-#define TADDCCTV 0x22
+#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 RDPSR 0x29
#define RDWIM 0x2A
#define RDTBR 0x2B
+#define SCAN 0x2C
#define WRY 0x30
#define WRPSR 0x31
#define WRWIM 0x32
#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
-int32 fpexec();
+/* 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;
+extern int32 nfp,ift;
+
+#ifdef ERRINJ
+extern uint32 errtt, errftt;
+#endif
-sub_cc(operand1, operand2, result, sregs)
+static uint32
+sub_cc(psr, operand1, operand2, result)
+ uint32 psr;
int32 operand1;
int32 operand2;
int32 result;
- struct pstate *sregs;
{
- sregs->psr = ((sregs->psr & ~PSR_N) | ((result >> 8) & PSR_N));
+ psr = ((psr & ~PSR_N) | ((result >> 8) & PSR_N));
if (result)
- sregs->psr &= ~PSR_Z;
+ psr &= ~PSR_Z;
else
- sregs->psr |= PSR_Z;
- sregs->psr = (sregs->psr & ~PSR_V) | ((
- ((operand1 & ~operand2 & ~result) |
+ psr |= PSR_Z;
+ psr = (psr & ~PSR_V) | ((((operand1 & ~operand2 & ~result) |
(~operand1 & operand2 & result)) >> 10) & PSR_V);
- sregs->psr = (sregs->psr & ~PSR_C) | ((
- ((~operand1 & operand2) |
+ psr = (psr & ~PSR_C) | ((((~operand1 & operand2) |
((~operand1 | operand2) & result)) >> 11) & PSR_C);
+ return psr;
}
-add_cc(operand1, operand2, result, psr)
+uint32
+add_cc(psr, operand1, operand2, result)
+ uint32 psr;
int32 operand1;
int32 operand2;
int32 result;
- uint32 *psr;
{
- *psr = ((*psr & ~PSR_N) | ((result >> 8) & PSR_N));
+ psr = ((psr & ~PSR_N) | ((result >> 8) & PSR_N));
if (result)
- *psr &= ~PSR_Z;
+ psr &= ~PSR_Z;
else
- *psr |= PSR_Z;
- *psr = (*psr & ~PSR_V) | ((
- ((operand1 & operand2 & ~result) |
+ psr |= PSR_Z;
+ psr = (psr & ~PSR_V) | ((((operand1 & operand2 & ~result) |
(~operand1 & ~operand2 & result)) >> 10) & PSR_V);
- *psr = (*psr & ~PSR_C) | ((
- ((operand1 & operand2) |
+ 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_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, opf, opc, asi, a, rd, cond, rs1,
+ uint32 cwp, op, op2, op3, asi, rd, cond, rs1,
rs2;
- uint32 ldep;
- int32 operand1, operand2, *rdd, result, i, disp22, eicc,
+ uint32 ldep, icc;
+ int32 operand1, operand2, *rdd, result, eicc,
new_cwp;
int32 pc, npc, data, address, ws, mexc, fcc;
+ int32 ddata[2];
sregs->ninst++;
- sregs->icnt = 1;
cwp = ((sregs->psr & PSR_CWP) << 4);
op = sregs->inst >> 30;
pc = sregs->npc;
npc = sregs->npc + 4;
- if (op > 1) {
+ op3 = rd = rs1 = operand2 = eicc = 0;
+ rdd = 0;
+ if (op & 2) {
op3 = (sregs->inst >> 19) & 0x3f;
rs1 = (sregs->inst >> 14) & 0x1f;
#ifdef LOAD_DEL
/* Check if load dependecy is possible */
- ldep = ((ebase.simtime <= sregs->ildtime) && ((op3 & 0x38) != 0x28) &&
- ((op3 & 0x3e) != 0x34) && (sregs->ildreg != 0));
+ 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 & INST_SIMM13;
- if (operand2 > 0x0fff)
- operand2 |= 0xfffff000;
+ operand2 = sregs->inst;
+ operand2 = ((operand2 << 19) >> 19); /* sign extend */
} else {
rs2 = sregs->inst & INST_RS2;
if (rs2 > 7)
}
#else
if (sregs->inst & INST_I) {
- operand2 = sregs->inst & INST_SIMM13;
- if (operand2 > 0x0fff)
- operand2 |= 0xfffff000;
+ operand2 = sregs->inst;
+ operand2 = ((operand2 << 19) >> 19); /* sign extend */
} else {
rs2 = sregs->inst & INST_RS2;
if (rs2 > 7)
#ifdef STAT
sregs->nbranch++;
#endif
+ icc = sregs->psr >> 20;
cond = ((sregs->inst >> 25) & 0x0f);
- switch (cond & 0x7) {
+ switch (cond) {
case BICC_BN:
eicc = 0;
break;
case BICC_BVS:
eicc = ICC_V;
break;
- }
- eicc &= PSR_N;
- if (sregs->inst & 0x10000000)
- eicc = !eicc;
- a = sregs->inst & 0x20000000;
- if (eicc) {
- operand1 = sregs->inst & 0x3fffff;
- if (sregs->inst & 0x200000)
- operand1 |= 0xffc00000;
- npc = sregs->pc + (operand1 << 2);
- if ((cond == BICC_BA) && (a))
+ 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 (a)
+ if (sregs->inst & 0x20000000)
sregs->annul = 1;
}
break;
#ifdef STAT
sregs->nbranch++;
#endif
- if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
}
cond = ((sregs->inst >> 25) & 0x0f);
fcc = (sregs->fsr >> 10) & 0x3;
- switch (cond & 0x7) {
+ switch (cond) {
case FBN:
eicc = 0;
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 (sregs->inst & 0x10000000)
- eicc = !eicc;
- a = sregs->inst & 0x20000000;
if (eicc) {
- operand1 = sregs->inst & 0x3fffff;
- if (sregs->inst & 0x200000)
- operand1 |= 0xffc00000;
- npc = sregs->pc + (operand1 << 2);
- if ((cond == FBA) && (a))
- sregs->annul = 1;
+ operand1 = sregs->inst;
+ operand1 = ((operand1 << 10) >> 8); /* sign extend */
+ npc = sregs->pc + operand1;
} else {
- if (a)
+ if (sregs->inst & 0x20000000)
sregs->annul = 1;
}
break;
case 2:
if ((op3 >> 1) == 0x1a) {
- if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
} else {
rs1 = (sregs->inst >> 14) & 0x1f;
switch (op3) {
case TICC:
- cond = ((sregs->inst >> 25) & 0x0f);
- switch (cond & 0x7) {
+ icc = sregs->psr >> 20;
+ cond = ((sregs->inst >> 25) & 0x0f);
+ switch (cond) {
case BICC_BN:
eicc = 0;
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;
}
- eicc &= PSR_N;
- if (sregs->inst & 0x10000000)
- eicc = !eicc;
- if (eicc) {
+ if (eicc & 1) {
sregs->trap = (0x80 | ((rs1 + operand2) & 0x7f));
}
break;
operand2 = 0;
*rdd = operand1 + operand2;
sregs->y = (rs1 << 31) | (sregs->y >> 1);
- add_cc(operand1, operand2, *rdd, &sregs->psr);
+ 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 SUBCC:
*rdd = rs1 - operand2;
- sub_cc(rs1, operand2, *rdd, sregs);
+ 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);
- sub_cc(rs1, operand2, *rdd, sregs);
+ sregs->psr = sub_cc(sregs->psr, rs1, operand2, *rdd);
break;
case ADD:
*rdd = rs1 + operand2;
break;
case ADDCC:
*rdd = rs1 + operand2;
- add_cc(rs1, operand2, *rdd, &sregs->psr);
+ 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);
- add_cc(rs1, operand2, *rdd, &sregs->psr);
+ sregs->psr = add_cc(sregs->psr, rs1, operand2, *rdd);
break;
case TADDCC:
*rdd = rs1 + operand2;
- add_cc(rs1, operand2, *rdd, &sregs->psr);
+ 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 = 0;
- add_cc(rs1, operand2, *rdd, &result);
+ 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;
+ 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 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)) {
break;
case RESTORE:
-#ifdef IUREV0
- if ((iurev0) && ((sregs->jmpltime + 1) == sregs->ninst)) {
- if (!(sregs->rett_err)) {
- sregs->rett_err = 1;
- if (sis_verbose)
- printf("IU rev.0 bug mode entered\n");
- }
- }
-#endif
-
new_cwp = ((sregs->psr & PSR_CWP) + 1) & PSR_CWP;
if (sregs->wim & (1 << new_cwp)) {
sregs->trap = TRAP_WUFL;
break;
}
*rdd = sregs->psr;
-#ifdef IUREV0
-
- if (iurev0 & sregs->rett_err) {
- operand2 = sregs->psr;
- *rdd |= PSR_ET;
- *rdd &= ~(PSR_S);
- *rdd |= ((*rdd & PSR_PS) << 1);
- if (sis_verbose) {
- if (operand2 != *rdd)
- printf("rdpsr failed: %08X -> %08X\n", operand2, *rdd);
- }
- }
-#endif
break;
case RDY:
- if (!(sregs->psr & PSR_S)) {
- sregs->trap = TRAP_PRIVI;
- break;
- }
- *rdd = sregs->y;
+ 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;
}
- sregs->psr = (rs1 ^ operand2) & 0x00f03fff;
+ sregs->psr = (sregs->psr & 0xff000000) |
+ (rs1 ^ operand2) & 0x00f03fff;
break;
case WRWIM:
if (!(sregs->psr & PSR_S)) {
((rs1 ^ operand2) & 0xfffff000);
break;
case WRY:
- sregs->y = (rs1 ^ operand2);
+ 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 IUREV0
- if (iurev0)
- sregs->jmpltime = sregs->ninst;
-#endif
#ifdef STAT
sregs->nbranch++;
#endif
npc = rs1 + operand2;
break;
case RETT:
-#ifdef IUREV0
- if (iurev0 && sregs->rett_err) {
- sregs->rett_err = 0;
- if (sis_verbose)
- printf("IU rev.0 bug mode reset\n");
- }
-#endif
-
address = rs1 + operand2;
new_cwp = ((sregs->psr & PSR_CWP) + 1) & PSR_CWP;
sregs->icnt = T_RETT; /* RETT takes two cycles */
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;
address = rs1 + operand2;
- /* Check for load/store to alternate address space */
-
- if ((op3 >> 4) == 1) {
- if (!(sregs->psr & PSR_S)) {
- sregs->trap = TRAP_PRIVI;
- break;
- } else if (sregs->inst & INST_I) {
- sregs->trap = TRAP_UNIMP;
- break;
- } else
- asi = (sregs->inst >> 5) & 0x0ff;
- } else {
- if (sregs->psr & PSR_S)
- asi = 11;
- else
- asi = 10;
-#ifdef IUREV0
- if (iurev0 && sregs->rett_err) {
- asi &= ~0x1;
- asi |= ((sregs->psr & PSR_PS) >> 6);
- }
-#endif
- }
+ if (sregs->psr & PSR_S)
+ asi = 11;
+ else
+ asi = 10;
if (op3 & 4) {
sregs->icnt = T_ST; /* Set store instruction count */
switch (op3) {
case LDDA:
+ if (!chk_asi(sregs, &asi, op3)) break;
case LDD:
if (address & 0x7) {
sregs->trap = TRAP_UNALI;
else
rdd = &(sregs->g[rd]);
}
- mexc = memory_read(asi, address, &data, &ws);
+ 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] = data;
- address += 4;
- mexc = memory_read(asi, address, &data, &ws);
- sregs->hold += ws;
+ rdd[0] = ddata[0];
+ rdd[1] = ddata[1];
#ifdef STAT
sregs->nload++; /* Double load counts twice */
#endif
- if (mexc) {
- sregs->trap = TRAP_DEXC;
- } else {
- rdd[1] = data;
- }
}
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, &ws);
+ mexc = memory_read(asi, address, &data, 2, &ws);
sregs->hold += ws;
if (mexc) {
sregs->trap = TRAP_DEXC;
*rdd = data;
}
break;
- case LDSTUB:
case LDSTUBA:
- mexc = memory_read(asi, address, &data, &ws);
+ 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 = (data >> ((3 - (address & 0x3)) << 3)) & 0x0ff;
+ data = extract_byte (data, address);
*rdd = data;
data = 0x0ff;
mexc = memory_write(asi, address, &data, 0, &ws);
break;
case LDSBA:
case LDUBA:
+ if (!chk_asi(sregs, &asi, op3)) break;
case LDSB:
case LDUB:
- mexc = memory_read(asi, address, &data, &ws);
+ mexc = memory_read(asi, address, &data, 0, &ws);
sregs->hold += ws;
if (mexc) {
sregs->trap = TRAP_DEXC;
break;
}
- data = (data >> ((3 - (address & 0x3)) << 3)) & 0x0ff;
- if ((op3 == LDSB) && (data >> 7))
- data |= 0xffffff00;
+ 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, &ws);
+ mexc = memory_read(asi, address, &data, 1, &ws);
sregs->hold += ws;
if (mexc) {
sregs->trap = TRAP_DEXC;
break;
}
- if (!(address & 0x2))
- data >>= 16;
- data &= 0x0ffff;
- if ((op3 == LDSH) && (data >> 15))
- data |= 0xffff0000;
+ 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) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
(sregs->frs2 == rd))
sregs->fhold += (sregs->ftime - ebase.simtime);
}
- mexc = memory_read(asi, address, &data, &ws);
+ mexc = memory_read(asi, address, &data, 2, &ws);
sregs->hold += ws;
sregs->flrd = rd;
sregs->ltime = ebase.simtime + sregs->icnt + FLSTHOLD +
}
break;
case LDDF:
- if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
((sregs->frs2 >> 1) == (rd >> 1)))
sregs->fhold += (sregs->ftime - ebase.simtime);
}
- mexc = memory_read(asi, address, &data, &ws);
+ 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) {
} else {
rd &= 0x1E;
sregs->flrd = rd;
- sregs->fs[rd] = *((float32 *) & data);
- mexc = memory_read(asi, address + 4, &data, &ws);
- sregs->hold += ws;
+ sregs->fs[rd] = *((float32 *) & ddata[0]);
#ifdef STAT
sregs->nload++; /* Double load counts twice */
#endif
- if (mexc) {
- sregs->trap = TRAP_DEXC;
- } else {
- sregs->fs[rd + 1] = *((float32 *) & data);
- sregs->ltime = ebase.simtime + sregs->icnt + FLSTHOLD +
- sregs->hold + sregs->fhold;
- }
+ 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) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
sregs->trap = TRAP_UNALI;
break;
}
- mexc = memory_read(asi, address, &data, &ws);
+ mexc = memory_read(asi, address, &data, 2, &ws);
sregs->hold += ws;
if (mexc) {
sregs->trap = TRAP_DEXC;
}
break;
case STFSR:
- if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
}
break;
- case ST:
case STA:
+ if (!chk_asi(sregs, &asi, op3)) break;
+ case ST:
if (address & 0x3) {
sregs->trap = TRAP_UNALI;
break;
sregs->trap = TRAP_DEXC;
}
break;
- case STB:
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 STD:
case STDA:
+ if (!chk_asi(sregs, &asi, op3)) break;
+ case STD:
if (address & 0x7) {
sregs->trap = TRAP_UNALI;
break;
sregs->trap = TRAP_UNIMP;
break;
}
- if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
}
break;
case STHA:
+ if (!chk_asi(sregs, &asi, op3)) break;
case STH:
if (address & 0x1) {
sregs->trap = TRAP_UNALI;
}
break;
case STF:
- if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
}
break;
case STDF:
- if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) {
+ if (!((sregs->psr & PSR_EF) && FP_PRES)) {
sregs->trap = TRAP_FPDIS;
break;
}
sregs->trap = TRAP_DEXC;
}
break;
- case SWAP:
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, &ws);
+ mexc = memory_read(asi, address, &data, 2, &ws);
sregs->hold += ws;
if (mexc) {
sregs->trap = TRAP_DEXC;
sregs->pc = pc;
sregs->npc = npc;
}
- return (0);
+ return 0;
}
#define T_FABSs 2
#define FsTOd 0xC9
-int
+static int
fpexec(op3, rd, rs1, rs2, sregs)
uint32 op3, rd, rs1, rs2;
struct pstate *sregs;
{
uint32 opf, tem, accex;
- float32 ftmps;
- float64 ftmpd;
int32 fcc;
- char *res;
uint32 ldadj;
if (sregs->fpstate == FP_EXC_MODE) {
sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_SEQ_ERR;
- sregs->fpstate == FP_EXC_PE;
- return (0);
+ sregs->fpstate = FP_EXC_PE;
+ return 0;
}
if (sregs->fpstate == FP_EXC_PE) {
sregs->fpstate = FP_EXC_MODE;
- return (TRAP_FPEXC);
+ return TRAP_FPEXC;
}
opf = (sregs->inst >> 5) & 0x1ff;
/* SPARC is big-endian - swap double floats if host is little-endian */
/* This is ugly - I know ... */
-#ifdef HOST_LITTLE_ENDIAN_FLOAT
+
+ /* 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:
sregs->fdp[rs2 | 1] = sregs->fs[rs2 & ~1];
sregs->fdp[rs2 & ~1] = sregs->fs[rs2 | 1];
default:
+ break;
}
#endif
sregs->ftime += T_FCMPs;
sregs->frd = 32; /* rd ignored */
if ((fcc == 0) && (opf == FCMPEs)) {
- sregs->fpstate == FP_EXC_PE;
+ sregs->fpstate = FP_EXC_PE;
sregs->fsr = (sregs->fsr & ~0x1C000) | (1 << 14);
}
break;
sregs->ftime += T_FCMPd;
sregs->frd = 32; /* rd ignored */
if ((fcc == 0) && (opf == FCMPEd)) {
- sregs->fpstate == FP_EXC_PE;
+ sregs->fpstate = FP_EXC_PE;
sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_IEEE;
}
break;
break;
case FSQRTs:
if (sregs->fs[rs2] < 0.0) {
- sregs->fpstate == FP_EXC_PE;
+ sregs->fpstate = FP_EXC_PE;
sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_IEEE;
sregs->fsr = (sregs->fsr & 0x1f) | 0x10;
break;
break;
case FSQRTd:
if (sregs->fd[rs2 >> 1] < 0.0) {
- sregs->fpstate == FP_EXC_PE;
+ sregs->fpstate = FP_EXC_PE;
sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_IEEE;
sregs->fsr = (sregs->fsr & 0x1f) | 0x10;
break;
default:
sregs->fsr = (sregs->fsr & ~FSR_TT) | FP_UNIMP;
- sregs->fpstate == FP_EXC_PE;
+ 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_FLOAT
+#ifdef HOST_LITTLE_ENDIAN
switch (opf) {
case FADDd:
case FDIVd:
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) {
}
clear_accex();
- return (0);
+ 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;
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);
+ return ERROR;
sregs->tbr = (sregs->tbr & 0xfffff000) | (sregs->trap << 4);
sregs->trap = 0;
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);
+ return 0;
}
extern struct irqcell irqarr[16];
-void
+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 > ((sregs->psr & PSR_PIL) >> 8)))) {
+ ((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);
- clear_int(ext_irl);
+ return 1;
}
}
+ return 0;
}
+void
init_regs(sregs)
struct pstate *sregs;
{
- int32 i;
-
sregs->pc = 0;
sregs->npc = 4;
sregs->trap = 0;
sregs->psr &= 0x00f03fdf;
- sregs->psr |= 0x080; /* Set supervisor bit */
+ sregs->psr |= 0x11000080; /* Set supervisor bit */
sregs->breakpoint = 0;
sregs->annul = 0;
sregs->fpstate = FP_EXE_MODE;
sregs->ltime = 0;
sregs->err_mode = 0;
ext_irl = 0;
- irqpend = 0;
sregs->g[0] = 0;
-#ifdef HOST_LITTLE_ENDIAN_FLOAT
+#ifdef HOST_LITTLE_ENDIAN
sregs->fdp = (float32 *) sregs->fd;
sregs->fsi = (int32 *) sregs->fs;
#else
sregs->ildreg = 0;
sregs->ildtime = 0;
+ sregs->y = 0;
+ sregs->asr17 = 0;
+
sregs->rett_err = 0;
sregs->jmpltime = 0;
}
-
-chk_fp(sregs)
- struct pstate *sregs;
-{
- return (sregs->fpu_pres);
-}