/* This file is part of SIS (SPARC instruction simulator) Copyright (C) 1995-2024 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 . */ /* This must come before any other includes. */ #include "defs.h" #include "sis.h" #include #include extern int32_t 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_t sub_cc (uint32_t psr, int32_t operand1, int32_t operand2, int32_t result); static uint32_t add_cc (uint32_t psr, int32_t operand1, int32_t operand2, int32_t result); static void log_cc (int32_t result, struct pstate *sregs); static int fpexec (uint32_t op3, uint32_t rd, uint32_t rs1, uint32_t rs2, struct pstate *sregs); static int chk_asi (struct pstate *sregs, uint32_t *asi, uint32_t op3); extern struct estate ebase; extern int32_t nfp,ift; #ifdef ERRINJ extern uint32_t errtt, errftt; #endif static uint32_t sub_cc(uint32_t psr, int32_t operand1, int32_t operand2, int32_t 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_t add_cc(uint32_t psr, int32_t operand1, int32_t operand2, int32_t 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(int32_t 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_t add32 (uint32_t n1, uint32_t n2, int *carry) { uint32_t result = n1 + n2; *carry = result < n1 || result < n2; return result; } /* Multiply two 32-bit integers. */ static void mul64 (uint32_t n1, uint32_t n2, uint32_t *result_hi, uint32_t *result_lo, int msigned) { uint32_t 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 propagate 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_t n1_hi, uint32_t n1_low, uint32_t n2, uint32_t *result, int msigned) { uint64_t n1; n1 = ((uint64_t) n1_hi) << 32; n1 |= ((uint64_t) n1_low) & 0xffffffff; if (msigned) { int64_t n1_s = (int64_t) n1; int32_t n2_s = (int32_t) n2; n1_s = n1_s / n2_s; n1 = (uint64_t) n1_s; } else n1 = n1 / n2; *result = (uint32_t) (n1 & 0xffffffff); } static int extract_short (uint32_t data, uint32_t address) { return ((data >> ((2 - (address & 2)) * 8)) & 0xffff); } static int extract_short_signed (uint32_t data, uint32_t address) { uint32_t tmp = ((data >> ((2 - (address & 2)) * 8)) & 0xffff); if (tmp & 0x8000) tmp |= 0xffff0000; return tmp; } static int extract_byte (uint32_t data, uint32_t address) { return ((data >> ((3 - (address & 3)) * 8)) & 0xff); } static int extract_byte_signed (uint32_t data, uint32_t address) { uint32_t tmp = ((data >> ((3 - (address & 3)) * 8)) & 0xff); if (tmp & 0x80) tmp |= 0xffffff00; return tmp; } int dispatch_instruction(struct pstate *sregs) { uint32_t cwp, op, op2, op3, asi, rd, cond, rs1, rs2; uint32_t ldep, icc, data, *rdd; int32_t operand1, operand2, result, eicc, new_cwp; int32_t pc, npc, address, ws, mexc, fcc; uint32_t 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_t uresult, 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; uresult = 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 = uresult < remainder || uresult < (uint32_t) operand2; if (uresult & 0x80000000) sregs->psr |= PSR_N; else sregs->psr &= ~PSR_N; y31 = (sregs->y & 0x80000000) == 0x80000000; if (uresult == 0 && sign == y31) sregs->psr |= PSR_Z; else sregs->psr &= ~PSR_Z; sign = (sign && !y31) || (!c0 && (sign || !y31)); if (sign ^ (uresult >> 31)) sregs->psr |= PSR_V; else sregs->psr &= ~PSR_V; if (!sign) sregs->psr |= PSR_C; else sregs->psr &= ~PSR_C; sregs->y = uresult; if (rd != 0) *rdd = (rs1 << 1) | !sign; } break; case SMUL: { mul64 (rs1, operand2, &sregs->y, rdd, 1); } break; case SMULCC: { uint32_t uresult; mul64 (rs1, operand2, &sregs->y, &uresult, 1); if (uresult & 0x80000000) sregs->psr |= PSR_N; else sregs->psr &= ~PSR_N; if (uresult == 0) sregs->psr |= PSR_Z; else sregs->psr &= ~PSR_Z; *rdd = uresult; } break; case UMUL: { mul64 (rs1, operand2, &sregs->y, rdd, 0); } break; case UMULCC: { uint32_t uresult; mul64 (rs1, operand2, &sregs->y, &uresult, 0); if (uresult & 0x80000000) sregs->psr |= PSR_N; else sregs->psr &= ~PSR_N; if (uresult == 0) sregs->psr |= PSR_Z; else sregs->psr &= ~PSR_Z; *rdd = uresult; } 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_t uresult; if (sparclite) { sregs->trap = TRAP_UNIMP; break; } if (operand2 == 0) { sregs->trap = TRAP_DIV0; break; } div64 (sregs->y, rs1, operand2, &uresult, 1); if (uresult & 0x80000000) sregs->psr |= PSR_N; else sregs->psr &= ~PSR_N; if (uresult == 0) sregs->psr |= PSR_Z; else sregs->psr &= ~PSR_Z; /* FIXME: should set overflow flag correctly. */ sregs->psr &= ~(PSR_C | PSR_V); *rdd = uresult; } 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_t uresult; if (sparclite) { sregs->trap = TRAP_UNIMP; break; } if (operand2 == 0) { sregs->trap = TRAP_DIV0; break; } div64 (sregs->y, rs1, operand2, &uresult, 0); if (uresult & 0x80000000) sregs->psr |= PSR_N; else sregs->psr &= ~PSR_N; if (uresult == 0) sregs->psr |= PSR_Z; else sregs->psr &= ~PSR_Z; /* FIXME: should set overflow flag correctly. */ sregs->psr &= ~(PSR_C | PSR_V); *rdd = uresult; } 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_t uresult, mask; int i; if (!sparclite) { sregs->trap = TRAP_UNIMP; break; } mask = (operand2 & 0x80000000) | (operand2 >> 1); uresult = rs1 ^ mask; for (i = 0; i < 32; i++) { if (uresult & 0x80000000) break; uresult <<= 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; ATTRIBUTE_FALLTHROUGH; 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; ATTRIBUTE_FALLTHROUGH; 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; ATTRIBUTE_FALLTHROUGH; 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; ATTRIBUTE_FALLTHROUGH; 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; ATTRIBUTE_FALLTHROUGH; 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 { memcpy (&sregs->fs[rd], &data, sizeof (sregs->fs[rd])); } 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; memcpy (&sregs->fs[rd], &ddata[0], sizeof (sregs->fs[rd])); #ifdef STAT sregs->nload++; /* Double load counts twice */ #endif memcpy (&sregs->fs[rd + 1], &ddata[1], sizeof (sregs->fs[rd + 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; ATTRIBUTE_FALLTHROUGH; 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; ATTRIBUTE_FALLTHROUGH; 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; ATTRIBUTE_FALLTHROUGH; 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; ATTRIBUTE_FALLTHROUGH; 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, (uint32_t *)&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, (uint32_t *)&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; ATTRIBUTE_FALLTHROUGH; 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(uint32_t op3, uint32_t rd, uint32_t rs1, uint32_t rs2, struct pstate *sregs) { uint32_t opf, tem, accex; int32_t fcc; uint32_t 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(struct pstate *sregs, uint32_t *asi, uint32_t 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(struct pstate *sregs) { int32_t 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(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(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_t *) sregs->fs; #else sregs->fs = (float32 *) sregs->fd; sregs->fsi = (int32_t *) 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; }