X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=sim%2Ferc32%2Fexec.c;h=81e50dd1f205395eb313c1b52ac800dbae6b1772;hb=3922b302645fda04da42a5279399578ae2f6206c;hp=8690cea9d898ff1614b35a27e4047d2933aef637;hpb=f4d2ff34bef1789eef9bed93572993ee023270e2;p=deliverable%2Fbinutils-gdb.git diff --git a/sim/erc32/exec.c b/sim/erc32/exec.c index 8690cea9d8..81e50dd1f2 100644 --- a/sim/erc32/exec.c +++ b/sim/erc32/exec.c @@ -1,31 +1,28 @@ -/* - * 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 . */ + +#include "config.h" #include "sis.h" -#include "end.h" #include #include -extern int32 ext_irl, irqpend, iurev0, sis_verbose; +extern int32 sis_verbose, sparclite; +int ext_irl = 0; /* Load/store interlock delay */ #define FLSTHOLD 1 @@ -55,6 +52,14 @@ extern int32 ext_irl, irqpend, iurev0, sis_verbose; #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 @@ -73,10 +78,12 @@ extern int32 ext_irl, irqpend, iurev0, sis_verbose; #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 @@ -88,6 +95,7 @@ extern int32 ext_irl, irqpend, iurev0, sis_verbose; #define TRAP_FPEXC 8 #define TRAP_DEXC 9 #define TRAP_TAG 10 +#define TRAP_DIV0 0x2a #define FSR_TT 0x1C000 #define FP_IEEE 0x04000 @@ -103,6 +111,13 @@ extern int32 ext_irl, irqpend, iurev0, sis_verbose; #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 @@ -112,12 +127,23 @@ extern int32 ext_irl, irqpend, iurev0, sis_verbose; #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 @@ -140,6 +166,7 @@ extern int32 ext_irl, irqpend, iurev0, sis_verbose; #define RDPSR 0x29 #define RDWIM 0x2A #define RDTBR 0x2B +#define SCAN 0x2C #define WRY 0x30 #define WRPSR 0x31 #define WRWIM 0x32 @@ -180,52 +207,71 @@ extern int32 ext_irl, irqpend, iurev0, sis_verbose; #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; @@ -236,25 +282,142 @@ log_cc(result, 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; @@ -263,14 +426,15 @@ dispatch_instruction(sregs) #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) @@ -282,9 +446,8 @@ dispatch_instruction(sregs) } #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) @@ -319,8 +482,9 @@ dispatch_instruction(sregs) #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; @@ -345,20 +509,39 @@ dispatch_instruction(sregs) 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; @@ -366,7 +549,7 @@ dispatch_instruction(sregs) #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; } @@ -375,7 +558,7 @@ dispatch_instruction(sregs) } cond = ((sregs->inst >> 25) & 0x0f); fcc = (sregs->fsr >> 10) & 0x3; - switch (cond & 0x7) { + switch (cond) { case FBN: eicc = 0; break; @@ -400,19 +583,39 @@ dispatch_instruction(sregs) 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; @@ -432,7 +635,7 @@ dispatch_instruction(sregs) 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; @@ -443,8 +646,9 @@ dispatch_instruction(sregs) 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; @@ -469,11 +673,32 @@ dispatch_instruction(sregs) 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; @@ -486,7 +711,211 @@ dispatch_instruction(sregs) 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; @@ -535,47 +964,66 @@ dispatch_instruction(sregs) 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; @@ -585,6 +1033,9 @@ dispatch_instruction(sregs) 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)) { @@ -598,16 +1049,6 @@ dispatch_instruction(sregs) 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; @@ -624,26 +1065,21 @@ dispatch_instruction(sregs) 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)) { @@ -668,7 +1104,8 @@ dispatch_instruction(sregs) 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)) { @@ -686,14 +1123,21 @@ dispatch_instruction(sregs) ((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 @@ -706,14 +1150,6 @@ dispatch_instruction(sregs) 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 */ @@ -739,6 +1175,28 @@ dispatch_instruction(sregs) 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; @@ -749,29 +1207,10 @@ dispatch_instruction(sregs) 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 */ @@ -789,6 +1228,7 @@ dispatch_instruction(sregs) switch (op3) { case LDDA: + if (!chk_asi(sregs, &asi, op3)) break; case LDD: if (address & 0x7) { sregs->trap = TRAP_UNALI; @@ -801,34 +1241,30 @@ dispatch_instruction(sregs) 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; @@ -836,16 +1272,17 @@ dispatch_instruction(sregs) *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); @@ -859,42 +1296,44 @@ dispatch_instruction(sregs) 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; } @@ -907,7 +1346,7 @@ dispatch_instruction(sregs) (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 + @@ -919,7 +1358,7 @@ dispatch_instruction(sregs) } break; case LDDF: - if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) { + if (!((sregs->psr & PSR_EF) && FP_PRES)) { sregs->trap = TRAP_FPDIS; break; } @@ -933,7 +1372,9 @@ dispatch_instruction(sregs) ((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) { @@ -941,26 +1382,20 @@ dispatch_instruction(sregs) } 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; } @@ -968,7 +1403,7 @@ dispatch_instruction(sregs) 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; @@ -979,7 +1414,7 @@ dispatch_instruction(sregs) } break; case STFSR: - if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) { + if (!((sregs->psr & PSR_EF) && FP_PRES)) { sregs->trap = TRAP_FPDIS; break; } @@ -997,8 +1432,9 @@ dispatch_instruction(sregs) } break; - case ST: case STA: + if (!chk_asi(sregs, &asi, op3)) break; + case ST: if (address & 0x3) { sregs->trap = TRAP_UNALI; break; @@ -1009,16 +1445,18 @@ dispatch_instruction(sregs) 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; @@ -1046,7 +1484,7 @@ dispatch_instruction(sregs) 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; } @@ -1074,6 +1512,7 @@ dispatch_instruction(sregs) } break; case STHA: + if (!chk_asi(sregs, &asi, op3)) break; case STH: if (address & 0x1) { sregs->trap = TRAP_UNALI; @@ -1086,7 +1525,7 @@ dispatch_instruction(sregs) } break; case STF: - if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) { + if (!((sregs->psr & PSR_EF) && FP_PRES)) { sregs->trap = TRAP_FPDIS; break; } @@ -1105,7 +1544,7 @@ dispatch_instruction(sregs) } break; case STDF: - if (!((sregs->psr & PSR_EF) && chk_fp(sregs))) { + if (!((sregs->psr & PSR_EF) && FP_PRES)) { sregs->trap = TRAP_FPDIS; break; } @@ -1128,13 +1567,14 @@ dispatch_instruction(sregs) 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; @@ -1180,7 +1620,7 @@ dispatch_instruction(sregs) sregs->pc = pc; sregs->npc = npc; } - return (0); + return 0; } #define T_FABSs 2 @@ -1230,26 +1670,23 @@ dispatch_instruction(sregs) #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; @@ -1290,7 +1727,12 @@ fpexec(op3, rd, rs1, rs2, sregs) /* 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: @@ -1307,6 +1749,7 @@ fpexec(op3, rd, rs1, rs2, sregs) sregs->fdp[rs2 | 1] = sregs->fs[rs2 & ~1]; sregs->fdp[rs2 & ~1] = sregs->fs[rs2 | 1]; default: + break; } #endif @@ -1341,7 +1784,7 @@ fpexec(op3, rd, rs1, rs2, sregs) 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; @@ -1360,7 +1803,7 @@ fpexec(op3, rd, rs1, rs2, sregs) 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; @@ -1392,7 +1835,7 @@ fpexec(op3, rd, rs1, rs2, sregs) 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; @@ -1403,7 +1846,7 @@ fpexec(op3, rd, rs1, rs2, sregs) 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; @@ -1453,12 +1896,21 @@ fpexec(op3, rd, rs1, rs2, sregs) 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: @@ -1470,6 +1922,7 @@ fpexec(op3, rd, rs1, rs2, sregs) 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) { @@ -1493,11 +1946,28 @@ fpexec(op3, rd, rs1, rs2, sregs) } 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; @@ -1508,10 +1978,12 @@ execute_trap(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; @@ -1526,42 +1998,56 @@ execute_trap(sregs) 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; @@ -1570,9 +2056,8 @@ init_regs(sregs) 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 @@ -1586,12 +2071,9 @@ init_regs(sregs) 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); -}