/* SPIM S20 MIPS simulator. Execute SPIM instructions. Copyright (C) 1990 by James Larus (larus@cs.wisc.edu). SPIM 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 1, or (at your option) any later version. SPIM 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 GNU CC; see the file COPYING. If not, write to James R. Larus, Computer Sciences Department, University of Wisconsin--Madison, 1210 West Dayton Street, Madison, WI 53706, USA or to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Header: /home/aa/projects/spim/public_html/cvsroot/spimsal/run.c,v 1.3 2000/01/11 23:03:46 brg Exp $ */ #define NaN(X) 0 #include #include #include "spim.h" #ifndef _WIN32 #include #include #endif #include "endian.h" #include "inst.h" #include "mem.h" #include "reg.h" #include "sym_tbl.h" #include "spim-syscall.h" #include "y.tab.h" /* Exported functions: */ int run_spim (mem_addr initial_PC, register int steps, int display); /* Local functions: */ static void long_multiply (reg_word v1, reg_word v2); /* Imported variables: */ extern char *ex_file_name; #define SIGN_BIT(X) ((X) & 0x80000000) #define ARITH_OVFL(RESULT, OP1, OP2) (SIGN_BIT (OP1) == SIGN_BIT (OP2) \ && SIGN_BIT (OP1) != SIGN_BIT (RESULT)) /* Delayed branch instructions set delay_PC to the address of their target and then execute another instruction. Note that we take advantage of the MIPS architecture, which leaves undefined the result of executing a delayed instruction in a delay slot. Here we just execute the second branch. */ #define BRANCH_INST(TEST, VALUE) {mem_addr delay_PC = -1; \ if (TEST) \ delay_PC = VALUE; \ if (bare_machine) \ { \ run_spim (PC+BYTES_PER_WORD, 1, display);\ } \ if (delay_PC != -1) \ {PC = delay_PC; \ } \ } #define JUMP_INST(VALUE) {if (bare_machine) \ run_spim (PC+BYTES_PER_WORD, 1, display); \ PC = VALUE - BYTES_PER_WORD; \ } #define LOAD_INST(OP, ADDR, DEST_A, MASK) \ {reg_word tmp = 0; \ OP (tmp, (ADDR)); \ tmp &= (MASK); \ *(DEST_A) = tmp; \ } /* Run a program stored at PC for N steps. If display FLAG is non-zero, print each instruction before it executes. Return non-zero if program's execution can continue. */ int run_spim (mem_addr initial_PC, register int steps, int display) { int last_inst_was_rfe = 0; register instruction *inst; PC = initial_PC; while (steps -- != 0) { PollInput(); /* Check for X-events */ R [0] = 0; /* Maintain invariant value */ READ_MEM_INST (inst, PC); if (inst == NULL) { sprintf(mess_buff, "Attempt to execute non-instruction at 0x%08x\n", PC); run_error (mess_buff); } else if (EXPR (inst) != NULL && EXPR (inst)->symbol != NULL && EXPR (inst)->symbol->addr == 0) { sprintf(mess_buff, "Instruction references undefined symbol at 0x%08x\n", PC); error (mess_buff); print_inst (PC); run_error (""); } if (display) print_inst (PC); #ifdef TEST_ASM test_assembly (inst); #endif switch (OPCODE (inst)) { case Y_ADD_OP: { register reg_word vs = R[RS (inst)], vt = R[RT (inst)]; register reg_word sum = vs + vt; if (ARITH_OVFL (sum, vs, vt)) RAISE_EXCEPTION (OVF_EXCPT, break); R[RD (inst)] = sum; break; } case Y_ADDI_OP: { register reg_word vs = R[RS (inst)], imm = (short) IMM (inst); register reg_word sum = vs + imm; if (ARITH_OVFL (sum, vs, imm)) RAISE_EXCEPTION (OVF_EXCPT, break); R[RT (inst)] = sum; break; } case Y_ADDIU_OP: R[RT (inst)] = R[RS (inst)] + (short) IMM (inst); break; case Y_ADDU_OP: R[RD (inst)] = R[RS (inst)] + R[RT (inst)]; break; case Y_AND_OP: R[RD (inst)] = R[RS (inst)] & R[RT (inst)]; break; case Y_ANDI_OP: R[RT (inst)] = R[RS (inst)] & (0xffff & IMM (inst)); break; case Y_BC0F_OP: case Y_BC2F_OP: case Y_BC3F_OP: BRANCH_INST (CpCond[OPCODE (inst) - Y_BC0F_OP] == 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BC0T_OP: case Y_BC2T_OP: case Y_BC3T_OP: BRANCH_INST (CpCond[OPCODE (inst) - Y_BC0T_OP] != 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BEQ_OP: BRANCH_INST (R[RS (inst)] == R[RT (inst)], PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BGEZ_OP: BRANCH_INST (SIGN_BIT (R[RS (inst)]) == 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BGEZAL_OP: if (bare_machine) R[31] = PC + 2 * BYTES_PER_WORD; else R[31] = PC + BYTES_PER_WORD; BRANCH_INST (SIGN_BIT (R[RS (inst)]) == 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BGTZ_OP: BRANCH_INST (R[RS (inst)] != 0 && SIGN_BIT (R[RS (inst)]) == 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BLEZ_OP: BRANCH_INST (R[RS (inst)] == 0 || SIGN_BIT (R[RS (inst)]) != 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BLTZ_OP: BRANCH_INST (SIGN_BIT (R[RS (inst)]) != 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BLTZAL_OP: if (bare_machine) R[31] = PC + 2 * BYTES_PER_WORD; else R[31] = PC + BYTES_PER_WORD; BRANCH_INST (SIGN_BIT (R[RS (inst)]) != 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BNE_OP: BRANCH_INST (R[RS (inst)] != R[RT (inst)], PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BREAK_OP: if (RD (inst) == 1) RAISE_EXCEPTION (BKPT_EXCPT, return (1)) /* Debugger breakpoint */ else RAISE_EXCEPTION (BKPT_EXCPT, break); case Y_CFC0_OP: case Y_CFC2_OP: case Y_CFC3_OP: R[RT (inst)] = CCR [OPCODE (inst) - Y_CFC0_OP] [RD (inst)]; break; case Y_COP0_OP: case Y_COP1_OP: case Y_COP2_OP: case Y_COP3_OP: CCR [OPCODE (inst) - Y_COP0_OP] [RD (inst)] = R[RT (inst)]; break; case Y_CTC0_OP: case Y_CTC2_OP: case Y_CTC3_OP: CCR [OPCODE (inst) - Y_CTC0_OP] [RD (inst)] = R[RT (inst)]; break; case Y_DIV_OP: if (R[RT (inst)] != 0) { LO = (long) R[RS (inst)] / (long) R[RT (inst)]; HI = (long) R[RS (inst)] % (long) R[RT (inst)]; } break; case Y_DIVU_OP: if (R[RT (inst)] != 0) { LO = (unsigned long) R[RS (inst)] / (unsigned long) R[RT (inst)]; HI = (unsigned long) R[RS (inst)] % (unsigned long) R[RT (inst)]; } break; case Y_J_OP: JUMP_INST (((PC & 0xf0000000) | TARGET (inst) << 2)); break; case Y_JAL_OP: if (bare_machine) R[31] = PC + 2 * BYTES_PER_WORD; else R[31] = PC + BYTES_PER_WORD; JUMP_INST (((PC & 0xf0000000) | (TARGET (inst) << 2))); break; case Y_JALR_OP: { mem_addr tmp = R[RS (inst)]; if (bare_machine) R[RD (inst)] = PC + 2 * BYTES_PER_WORD; else R[RD (inst)] = PC + BYTES_PER_WORD; JUMP_INST (tmp); } break; case Y_JR_OP: { mem_addr tmp = R[RS (inst)]; JUMP_INST (tmp); } break; case Y_LB_OP: LOAD_INST (READ_MEM_BYTE, R[BASE (inst)] + IOFFSET (inst), &R[RT (inst)], 0xffffffff); break; case Y_LBU_OP: LOAD_INST (READ_MEM_BYTE, R[BASE (inst)] + IOFFSET (inst), &R[RT (inst)], 0xff); break; case Y_LH_OP: LOAD_INST (READ_MEM_HALF, R[BASE (inst)] + IOFFSET (inst), &R[RT (inst)], 0xffffffff); break; case Y_LHU_OP: LOAD_INST (READ_MEM_HALF, R[BASE (inst)] + IOFFSET (inst), &R[RT (inst)], 0xffff); break; case Y_LUI_OP: R[RT (inst)] = (IMM (inst) << 16) & 0xffff0000; break; case Y_LW_OP: LOAD_INST (READ_MEM_WORD, R[BASE (inst)] + IOFFSET (inst), &R[RT (inst)], 0xffffffff); break; case Y_LWC0_OP: case Y_LWC2_OP: case Y_LWC3_OP: LOAD_INST (READ_MEM_WORD, R[BASE (inst)] + IOFFSET (inst), &CPR [OPCODE (inst) - Y_LWC0_OP] [RT (inst)], 0xffffffff); break; case Y_LWL_OP: { register mem_addr addr = R[BASE (inst)] + IOFFSET (inst); reg_word word; /* Can't be register */ register int byte = addr & 0x3; reg_word reg_val = R[RT (inst)]; LOAD_INST (READ_MEM_WORD, addr & 0xfffffffc, &word, 0xffffffff); if ((!exception_occurred) || ((Cause >> 2) > LAST_REAL_EXCEPT)) if (IS_BIG_ENDIAN) { switch (byte) { case 0: R[RT (inst)] = word; break; case 1: R[RT (inst)] = (word & 0xffffff) << 8 | (reg_val & 0xff); break; case 2: R[RT (inst)] = (word & 0xffff) << 16 | (reg_val & 0xffff); break; case 3: R[RT (inst)] = (word & 0xff) << 24 | (reg_val & 0xffffff); break; } } else { switch (byte) { case 0: R[RT (inst)] = (word & 0xff) << 24 | (reg_val & 0xffffff); break; case 1: R[RT (inst)] = (word & 0xffff) << 16 | (reg_val & 0xffff); break; case 2: R[RT (inst)] = (word & 0xffffff) << 8 | (reg_val & 0xff); break; case 3: R[RT (inst)] = word; break; } } break; } case Y_LWR_OP: { register mem_addr addr = R[BASE (inst)] + IOFFSET (inst); reg_word word; /* Can't be register */ register int byte = addr & 0x3; reg_word reg_val = R[RT (inst)]; LOAD_INST (READ_MEM_WORD, addr & 0xfffffffc, &word, 0xffffffff); if ((!exception_occurred) || ((Cause >> 2) > LAST_REAL_EXCEPT)) if (IS_BIG_ENDIAN) { switch (byte) { case 0: R[RT (inst)] = (reg_val & 0xffffff00) | ((word & 0xff000000) >> 24); break; case 1: R[RT (inst)] = (reg_val & 0xffff0000) | ((word & 0xffff0000) >> 16); break; case 2: R[RT (inst)] = (reg_val & 0xff000000) | ((word & 0xffffff00) >> 8); break; case 3: R[RT (inst)] = word; break; } } else { switch (byte) { /* NB: The description of the little-endian case in Kane is totally wrong. */ case 0: /* 3 in book */ R[RT (inst)] = reg_val; break; case 1: /* 0 in book */ R[RT (inst)] = (reg_val & 0xff000000) | ((word & 0xffffff00) >> 8); break; case 2: /* 1 in book */ R[RT (inst)] = (reg_val & 0xffff0000) | ((word & 0xffff0000) >> 16); break; case 3: /* 2 in book */ R[RT (inst)] = (reg_val & 0xffffff00) | ((word & 0xff000000) >> 24); break; } } break; } case Y_MFC0_OP: case Y_MFC2_OP: case Y_MFC3_OP: R[RT (inst)] = CPR [OPCODE (inst) - Y_MFC0_OP] [RD (inst)]; break; case Y_MFHI_OP: R[RD (inst)] = HI; break; case Y_MFLO_OP: R[RD (inst)] = LO; break; case Y_MTC0_OP: case Y_MTC2_OP: case Y_MTC3_OP: CPR [OPCODE (inst) - Y_MTC0_OP] [RD (inst)] = R[RT (inst)]; break; case Y_MTHI_OP: HI = R[RS (inst)]; break; case Y_MTLO_OP: LO = R[RS (inst)]; break; case Y_MULT_OP: { reg_word v1 = R[RS (inst)], v2 = R[RT (inst)]; int neg_sign = 0; if (v1 < 0) v1 = - v1, neg_sign = 1; if (v2 < 0) v2 = - v2, neg_sign = ! neg_sign; long_multiply (v1, v2); if (neg_sign) { LO = ~ LO; HI = ~ HI; LO += 1; if (LO == 0) HI += 1; } } break; case Y_MULTU_OP: long_multiply (R [RS (inst)], R[RT (inst)]); break; case Y_NOR_OP: R[RD (inst)] = ~ (R[RS (inst)] | R[RT (inst)]); break; case Y_OR_OP: R[RD (inst)] = R[RS (inst)] | R[RT (inst)]; break; case Y_ORI_OP: R[RT (inst)] = R[RS (inst)] | (0xffff & IMM (inst)); break; case Y_RFE_OP: last_inst_was_rfe = 1; break; case Y_SB_OP: SET_MEM_BYTE (R[BASE (inst)] + IOFFSET (inst), R[RT (inst)]); break; case Y_SH_OP: SET_MEM_HALF (R[BASE (inst)] + IOFFSET (inst), R[RT (inst)]); break; case Y_SLL_OP: { int shamt = SHAMT (inst); if (shamt >= 0 && shamt < 32) R[RD (inst)] = R[RT (inst)] << shamt; else R[RD (inst)] = R[RT (inst)]; break; } case Y_SLLV_OP: { int shamt = (R[RS (inst)] & 0x1f); if (shamt >= 0 && shamt < 32) R[RD (inst)] = R[RT (inst)] << shamt; else R[RD (inst)] = R[RT (inst)]; break; } case Y_SLT_OP: if (R[RS (inst)] < R[RT (inst)]) R[RD (inst)] = 1; else R[RD (inst)] = 0; break; case Y_SLTI_OP: if (R[RS (inst)] < (short) IMM (inst)) R[RT (inst)] = 1; else R[RT (inst)] = 0; break; case Y_SLTIU_OP: { int x = (short) IMM (inst); if ((unsigned long) R[RS (inst)] < (unsigned long) x) R[RT (inst)] = 1; else R[RT (inst)] = 0; break; } case Y_SLTU_OP: if ((unsigned long) R[RS (inst)] < (unsigned long) R[RT (inst)]) R[RD (inst)] = 1; else R[RD (inst)] = 0; break; case Y_SRA_OP: { int shamt = SHAMT (inst); long val = R[RT (inst)]; if (shamt >= 0 && shamt < 32) R[RD (inst)] = val >> shamt; else R[RD (inst)] = val; break; } case Y_SRAV_OP: { int shamt = R[RS (inst)] & 0x1f; long val = R[RT (inst)]; if (shamt >= 0 && shamt < 32) R[RD (inst)] = val >> shamt; else R[RD (inst)] = val; break; } case Y_SRL_OP: { int shamt = SHAMT (inst); unsigned long val = R[RT (inst)]; if (shamt >= 0 && shamt < 32) R[RD (inst)] = val >> shamt; else R[RD (inst)] = val; break; } case Y_SRLV_OP: { int shamt = R[RS (inst)] & 0x1f; unsigned long val = R[RT (inst)]; if (shamt >= 0 && shamt < 32) R[RD (inst)] = val >> shamt; else R[RD (inst)] = val; break; } case Y_SUB_OP: { register reg_word vs = R[RS (inst)], vt = R[RT (inst)]; register reg_word diff = vs - vt; if (SIGN_BIT (vs) != SIGN_BIT (vt) && SIGN_BIT (vs) != SIGN_BIT (diff)) RAISE_EXCEPTION (OVF_EXCPT, break); R[RD (inst)] = diff; break; } case Y_SUBU_OP: R[RD (inst)] = (unsigned long) R[RS (inst)] - (unsigned long) R[RT (inst)]; break; case Y_SW_OP: SET_MEM_WORD (R[BASE (inst)] + IOFFSET (inst), R[RT (inst)]); break; case Y_SWC0_OP: case Y_SWC2_OP: case Y_SWC3_OP: SET_MEM_WORD (R[BASE (inst)] + IOFFSET (inst), CPR [OPCODE (inst) - Y_SWC0_OP] [RT (inst)]); break; case Y_SWL_OP: { register mem_addr addr = R[BASE (inst)] + IOFFSET (inst); mem_word data; reg_word reg = R[RT (inst)]; register int byte = addr & 0x3; READ_MEM_WORD (data, (addr & 0xfffffffc)); if (IS_BIG_ENDIAN) { switch (byte) { case 0: data = reg; break; case 1: data = (data & 0xff000000) | (reg >> 8 & 0xffffff); break; case 2: data = (data & 0xffff0000) | (reg >> 16 & 0xffff); break; case 3: data = (data & 0xffffff00) | (reg >> 24 & 0xff); break; } } else { switch (byte) { case 0: data = (data & 0xffffff00) | (reg >> 24 & 0xff); break; case 1: data = (data & 0xffff0000) | (reg >> 16 & 0xffff); break; case 2: data = (data & 0xff000000) | (reg >> 8 & 0xffffff); break; case 3: data = reg; break; } } SET_MEM_WORD (addr & 0xfffffffc, data); break; } case Y_SWR_OP: { register mem_addr addr = R[BASE (inst)] + IOFFSET (inst); mem_word data; reg_word reg = R[RT (inst)]; register int byte = addr & 0x3; READ_MEM_WORD (data, (addr & 0xfffffffc)); if (IS_BIG_ENDIAN) { switch (byte) { case 0: data = ((reg << 24) & 0xff000000) | (data & 0xffffff); break; case 1: data = ((reg << 16) & 0xffff0000) | (data & 0xffff); break; case 2: data = ((reg << 8) & 0xffffff00) | (data & 0xff) ; break; case 3: data = reg; break; } } else { switch (byte) { case 0: data = reg; break; case 1: data = ((reg << 8) & 0xffffff00) | (data & 0xff) ; break; case 2: data = ((reg << 16) & 0xffff0000) | (data & 0xffff); break; case 3: data = ((reg << 24) & 0xff000000) | (data & 0xffffff); break; } } SET_MEM_WORD (addr & 0xfffffffc, data); break; } case Y_SYSCALL_OP: if (memio) { if (R[REG_V0] == EXIT_SYSCALL) { PC = 0; return 0; } RAISE_EXCEPTION(SYSCALL_EXCPT, /* null stmt */ ); steps++; } else { /* It is a true pleasure to write an operating system in a high-level language! */ /* Dummy system calls for SPIMs assembly option */ switch (R[REG_V0]) { case PRINT_INT_SYSCALL: sprintf(mess_buff, "%d", R[REG_A0]); write_output (console_out, mess_buff); break; case PRINT_FLOAT_SYSCALL: { float val = FPR_S (REG_FA0); sprintf(mess_buff, "%f", val); write_output (console_out, mess_buff); break; } case PRINT_DOUBLE_SYSCALL: sprintf(mess_buff, "%f", FPR[REG_FA0/2]); write_output (console_out, mess_buff); break; case PRINT_STRING_SYSCALL: sprintf(mess_buff, "%s", MEM_ADDRESS (R[REG_A0])); write_output (console_out, mess_buff); break; case PRINT_CHARACTER_SYSCALL: sprintf(mess_buff, "%c", R[REG_A0]); write_output (console_out, mess_buff); break; case READ_INT_SYSCALL: { static char str [256]; read_input (str, 256); R[REG_RES] = atol (str); break; } case READ_FLOAT_SYSCALL: { static char str [256]; read_input (str, 256); FGR [REG_FRES] = (float) atof (str); break; } case READ_DOUBLE_SYSCALL: { static char str [256]; read_input (str, 256); FPR [REG_FRES] = atof (str); break; } case READ_STRING_SYSCALL: { read_input ((char *) MEM_ADDRESS (R[REG_A0]), R[REG_A1]); break; } case READ_CHARACTER_SYSCALL: { char str[2]; read_input (str , 2); if (*str == '\0') *str = '\n'; /* makes xspim = spim */ R[REG_V0] = (long)*str; break; } case SBRK_SYSCALL: { mem_addr x = data_top; expand_data (R[REG_A0]); R[REG_RES] = x; break; } case EXIT_SYSCALL: PC = 0; return (0); default: sprintf(mess_buff, "Unknown system call: %d\n", R[REG_V0]); run_error (mess_buff); break; } } break; case Y_TLBP_OP: case Y_TLBR_OP: case Y_TLBWI_OP: case Y_TLBWR_OP: fatal_error ("Unimplemented operation\n"); break; case Y_XOR_OP: R[RD (inst)] = R[RS (inst)] ^ R[RT (inst)]; break; case Y_XORI_OP: R[RT (inst)] = R[RS (inst)] ^ (0xffff & IMM (inst)); break; /* FPA Operations */ case Y_ABS_S_OP: SET_FPR_S (FD (inst), fabs (FPR_S (FS (inst)))); break; case Y_ABS_D_OP: SET_FPR_D (FD (inst), fabs (FPR_D (FS (inst)))); break; case Y_ADD_S_OP: SET_FPR_S (FD (inst), FPR_S (FS (inst)) + FPR_S (FT (inst))); /* Should trap on inexact/overflow/underflow */ break; case Y_ADD_D_OP: SET_FPR_D (FD (inst), FPR_D (FS (inst)) + FPR_D (FT (inst))); /* Should trap on inexact/overflow/underflow */ break; case Y_BC1F_OP: BRANCH_INST (FpCond == 0, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_BC1T_OP: BRANCH_INST (FpCond == 1, PC + (SIGN_EX (IOFFSET (inst)) << 2)); break; case Y_C_F_S_OP: case Y_C_UN_S_OP: case Y_C_EQ_S_OP: case Y_C_UEQ_S_OP: case Y_C_OLE_S_OP: case Y_C_ULE_S_OP: case Y_C_SF_S_OP: case Y_C_NGLE_S_OP: case Y_C_SEQ_S_OP: case Y_C_NGL_S_OP: case Y_C_LT_S_OP: case Y_C_NGE_S_OP: case Y_C_LE_S_OP: case Y_C_NGT_S_OP: { float v1 = FPR_S (FS (inst)), v2 = FPR_S (FT (inst)); double dv1 = v1, dv2 = v2; int less, equal, unordered; int cond = COND (inst); if (NaN (dv1) || NaN (dv2)) { less = 0; equal = 0; unordered = 1; if (cond & COND_IN) RAISE_EXCEPTION (INVALID_EXCEPT, break); } else { less = v1 < v2; equal = v1 == v2; unordered = 0; } FpCond = 0; if (cond & COND_LT) FpCond |= less; if (cond & COND_EQ) FpCond |= equal; if (cond & COND_UN) FpCond |= unordered; } break; case Y_C_F_D_OP: case Y_C_UN_D_OP: case Y_C_EQ_D_OP: case Y_C_UEQ_D_OP: case Y_C_OLE_D_OP: case Y_C_ULE_D_OP: case Y_C_SF_D_OP: case Y_C_NGLE_D_OP: case Y_C_SEQ_D_OP: case Y_C_NGL_D_OP: case Y_C_LT_D_OP: case Y_C_NGE_D_OP: case Y_C_LE_D_OP: case Y_C_NGT_D_OP: { double v1 = FPR_D (FS (inst)), v2 = FPR_D (FT (inst)); int less, equal, unordered; int cond = COND (inst); if (NaN (v1) || NaN (v2)) { less = 0; equal = 0; unordered = 1; if (cond & COND_IN) RAISE_EXCEPTION (INVALID_EXCEPT, break); } else { less = v1 < v2; equal = v1 == v2; unordered = 0; } FpCond = 0; if (cond & COND_LT) FpCond |= less; if (cond & COND_EQ) FpCond |= equal; if (cond & COND_UN) FpCond |= unordered; } break; case Y_CFC1_OP: R[RT (inst)] = FCR[RD (inst)]; /* RD not FS */ break; case Y_CTC1_OP: FCR[RD (inst)] = R[RT (inst)]; /* RD not FS */ break; case Y_CVT_D_S_OP: { double val = FPR_S (FS (inst)); SET_FPR_D (FD (inst), val); break; } case Y_CVT_D_W_OP: { double val = FPR_W (FS (inst)); SET_FPR_D (FD (inst), val); break; } case Y_CVT_S_D_OP: { float val = FPR_D (FS (inst)); SET_FPR_S (FD (inst), val); break; } case Y_CVT_S_W_OP: { float val = FPR_W (FS (inst)); SET_FPR_S (FD (inst), val); break; } case Y_CVT_W_D_OP: { int val = FPR_D (FS (inst)); SET_FPR_W (FD (inst), val); break; } case Y_CVT_W_S_OP: { int val = FPR_S (FS (inst)); SET_FPR_W (FD (inst), val); break; } case Y_DIV_S_OP: SET_FPR_S (FD (inst), FPR_S (FS (inst)) / FPR_S (FT (inst))); break; case Y_DIV_D_OP: SET_FPR_D (FD (inst), FPR_D (FS (inst)) / FPR_D (FT (inst))); break; case Y_LWC1_OP: { reg_word *wp = (reg_word *) &FGR [FT (inst)]; LOAD_INST (READ_MEM_WORD, R[BASE (inst)] + IOFFSET (inst), wp, 0xffffffff); break; } case Y_MFC1_OP: { float val = FGR [RD (inst)]; /* RD not FS */ reg_word *vp = (reg_word *) &val; R[RT (inst)] = *vp; /* Fool coercion */ break; } case Y_MOV_S_OP: SET_FPR_S (FD (inst), FPR_S (FS (inst))); break; case Y_MOV_D_OP: SET_FPR_D (FD (inst), FPR_D (FS (inst))); break; case Y_MTC1_OP: { reg_word word = R[RT (inst)]; float *wp = (float *) &word; FGR [RD (inst)] = *wp; /* RD not FS, fool coercion */ break; } case Y_MUL_S_OP: SET_FPR_S (FD (inst), FPR_S (FS (inst)) * FPR_S (FT (inst))); break; case Y_MUL_D_OP: SET_FPR_D (FD (inst), FPR_D (FS (inst)) * FPR_D (FT (inst))); break; case Y_NEG_S_OP: SET_FPR_S (FD (inst), -FPR_S (FS (inst))); break; case Y_NEG_D_OP: SET_FPR_D (FD (inst), -FPR_D (FS (inst))); break; case Y_SUB_S_OP: SET_FPR_S (FD (inst), FPR_S (FS (inst)) - FPR_S (FT (inst))); break; case Y_SUB_D_OP: SET_FPR_D (FD (inst), FPR_D (FS (inst)) - FPR_D (FT (inst))); break; case Y_SWC1_OP: { float val = FGR [RT (inst)]; reg_word *vp = (reg_word *) &val; SET_MEM_WORD (R[BASE (inst)] + IOFFSET (inst), *vp); break; } default: sprintf(mess_buff, "Unknown instruction type: %d\n", OPCODE (inst)); fatal_error (mess_buff); break; } if (!exception_occurred) { PC += BYTES_PER_WORD; if (memio) Update_IO(); /* Test for interrupts. */ if ((Status_Reg & 1) && (Cause & Status_Reg & 0xff00)) RAISE_EXCEPTION(INT_EXCPT, /* nada */); } if (exception_occurred) { exception_occurred = 0; EPC = PC & 0xfffffffc; /* Round down */ PC = EXCEPTION_ADDR; Status_Reg = Status_Reg & 0xffffffc0 | (Status_Reg & 0xf) << 2; if (!quiet) { sprintf(mess_buff, "Exception occurred at PC=0x%08x\n", EPC); error (mess_buff); switch ((Cause >> 2) & 0x1f) { case INT_EXCPT: error (" Interrupt exception\n"); break; case ADDRL_EXCPT: sprintf(mess_buff, " Unaligned address in inst/data fetch: 0x%08x\n", BadVAddr); error (mess_buff); break; case ADDRS_EXCPT: sprintf(mess_buff, " Unaligned address in store: 0x%08x\n", BadVAddr); error (mess_buff); break; case IBUS_EXCPT: sprintf(mess_buff, " Bad address in text read: 0x%08x\n", BadVAddr); error (mess_buff); break; case DBUS_EXCPT: sprintf(mess_buff, " Bad address in data/stack read: 0x%08x\n", BadVAddr); error (mess_buff); break; case BKPT_EXCPT: break; case SYSCALL_EXCPT: sprintf (mess_buff, " Syscall exception ($2 = 0x%08x)\n", R[2]); error (mess_buff); break; case RI_EXCPT: error (" Reserved instruction execution\n"); break; case OVF_EXCPT: error (" Arithmetic overflow\n"); break; case CLOCK_EXCPT: error (" Clock interrupt\n"); break; case IO_EXCPT: error (" IO interrupt\n"); break; default: sprintf(mess_buff, "Unknown exception: %d\n",(Cause >> 2) & 0x1f); error (mess_buff); break; } print_inst(EPC); } } /* This must come after all interrupt generators */ /* It makes the rfe instructions delayed. */ if (last_inst_was_rfe) { last_inst_was_rfe = 0; Status_Reg = (Status_Reg & 0xfffffff0) | ((Status_Reg & 0x3c) >> 2); } } /* End of while */ /* Executed enought steps, return, but are able to continue. */ return (1); } /* Multiply two 32-bit numbers, V1 and V2, to produce a 64 bit result in the HI/LO registers. The algorithm is high-school math: A B x C D ------ AD || BD AC || CB || 0 where A and B are the high and low short words of V1, C and D are the short words of V2, AD is the product of A and D, and X || Y is (X << 16) + Y. Since the algorithm is programmed in C, we need to be careful not to overflow. */ static void long_multiply (reg_word v1, reg_word v2) { register long a, b, c, d; register long x, y; a = (v1 >> 16) & 0xffff; b = v1 & 0xffff; c = (v2 >> 16) & 0xffff; d = v2 & 0xffff; LO = b * d; /* BD */ x = a * d + c * b; /* AD + CB */ y = ((LO >> 16) & 0xffff) + x; LO = (LO & 0xffff) | ((y & 0xffff) << 16); HI = (y >> 16) & 0xffff; HI += a * c; /* AC */ }