Index: src/mips/simulator-mips.cc |
diff --git a/src/mips/simulator-mips.cc b/src/mips/simulator-mips.cc |
index 7ea38419358a600f8c6202f2f30bcdd5c6e350bb..2e4921c71059eb799228c38bfa7bf5886b192d6b 100644 |
--- a/src/mips/simulator-mips.cc |
+++ b/src/mips/simulator-mips.cc |
@@ -2079,6 +2079,8 @@ void Simulator::ConfigureTypeRegister(Instruction* instr, |
case DIV: |
case DIVU: |
// div and divu never raise exceptions. |
+ case SELEQZ_S: |
+ case SELNEZ_S: |
break; |
default: |
UNREACHABLE(); |
@@ -2130,366 +2132,393 @@ void Simulator::ConfigureTypeRegister(Instruction* instr, |
} |
-void Simulator::DecodeTypeRegister(Instruction* instr) { |
- // Instruction fields. |
- const Opcode op = instr->OpcodeFieldRaw(); |
- const int32_t rs_reg = instr->RsValue(); |
- const int32_t rs = get_register(rs_reg); |
- const uint32_t rs_u = static_cast<uint32_t>(rs); |
- const int32_t rt_reg = instr->RtValue(); |
- const int32_t rt = get_register(rt_reg); |
- const uint32_t rt_u = static_cast<uint32_t>(rt); |
- const int32_t rd_reg = instr->RdValue(); |
+void Simulator::DecodeTypeRegisterDRsType(Instruction* instr, |
+ const int32_t& fr_reg, |
+ const int32_t& fs_reg, |
+ const int32_t& ft_reg, |
+ const int32_t& fd_reg) { |
+ double ft, fs; |
+ uint32_t cc, fcsr_cc; |
+ int64_t i64; |
+ fs = get_fpu_register_double(fs_reg); |
+ ft = get_fpu_register_double(ft_reg); |
+ int64_t ft_int = static_cast<int64_t>(ft); |
+ cc = instr->FCccValue(); |
+ fcsr_cc = get_fcsr_condition_bit(cc); |
+ switch (instr->FunctionFieldRaw()) { |
+ case SELEQZ_C: |
+ DCHECK(IsMipsArchVariant(kMips32r6)); |
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) == 0 ? fs : 0.0); |
+ break; |
+ case SELNEZ_C: |
+ DCHECK(IsMipsArchVariant(kMips32r6)); |
+ set_fpu_register_double(fd_reg, (ft_int & 0x1) != 0 ? fs : 0.0); |
+ break; |
+ case ADD_D: |
+ set_fpu_register_double(fd_reg, fs + ft); |
+ break; |
+ case SUB_D: |
+ set_fpu_register_double(fd_reg, fs - ft); |
+ break; |
+ case MUL_D: |
+ set_fpu_register_double(fd_reg, fs * ft); |
+ break; |
+ case DIV_D: |
+ set_fpu_register_double(fd_reg, fs / ft); |
+ break; |
+ case ABS_D: |
+ set_fpu_register_double(fd_reg, fabs(fs)); |
+ break; |
+ case MOV_D: |
+ set_fpu_register_double(fd_reg, fs); |
+ break; |
+ case NEG_D: |
+ set_fpu_register_double(fd_reg, -fs); |
+ break; |
+ case SQRT_D: |
+ set_fpu_register_double(fd_reg, fast_sqrt(fs)); |
+ break; |
+ case C_UN_D: |
+ set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
+ break; |
+ case C_EQ_D: |
+ set_fcsr_bit(fcsr_cc, (fs == ft)); |
+ break; |
+ case C_UEQ_D: |
+ set_fcsr_bit(fcsr_cc, (fs == ft) || (std::isnan(fs) || std::isnan(ft))); |
+ break; |
+ case C_OLT_D: |
+ set_fcsr_bit(fcsr_cc, (fs < ft)); |
+ break; |
+ case C_ULT_D: |
+ set_fcsr_bit(fcsr_cc, (fs < ft) || (std::isnan(fs) || std::isnan(ft))); |
+ break; |
+ case C_OLE_D: |
+ set_fcsr_bit(fcsr_cc, (fs <= ft)); |
+ break; |
+ case C_ULE_D: |
+ set_fcsr_bit(fcsr_cc, (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); |
+ break; |
+ case CVT_W_D: // Convert double to word. |
+ // Rounding modes are not yet supported. |
+ DCHECK((FCSR_ & 3) == 0); |
+ // In rounding mode 0 it should behave like ROUND. |
+ case ROUND_W_D: // Round double to word (round half to even). |
+ { |
+ double rounded = std::floor(fs + 0.5); |
+ int32_t result = static_cast<int32_t>(rounded); |
+ if ((result & 1) != 0 && result - fs == 0.5) { |
+ // If the number is halfway between two integers, |
+ // round to the even one. |
+ result--; |
+ } |
+ set_fpu_register_word(fd_reg, result); |
+ if (set_fcsr_round_error(fs, rounded)) { |
+ set_fpu_register_word(fd_reg, kFPUInvalidResult); |
+ } |
+ } break; |
+ case TRUNC_W_D: // Truncate double to word (round towards 0). |
+ { |
+ double rounded = trunc(fs); |
+ int32_t result = static_cast<int32_t>(rounded); |
+ set_fpu_register_word(fd_reg, result); |
+ if (set_fcsr_round_error(fs, rounded)) { |
+ set_fpu_register_word(fd_reg, kFPUInvalidResult); |
+ } |
+ } break; |
+ case FLOOR_W_D: // Round double to word towards negative infinity. |
+ { |
+ double rounded = std::floor(fs); |
+ int32_t result = static_cast<int32_t>(rounded); |
+ set_fpu_register_word(fd_reg, result); |
+ if (set_fcsr_round_error(fs, rounded)) { |
+ set_fpu_register_word(fd_reg, kFPUInvalidResult); |
+ } |
+ } break; |
+ case CEIL_W_D: // Round double to word towards positive infinity. |
+ { |
+ double rounded = std::ceil(fs); |
+ int32_t result = static_cast<int32_t>(rounded); |
+ set_fpu_register_word(fd_reg, result); |
+ if (set_fcsr_round_error(fs, rounded)) { |
+ set_fpu_register_word(fd_reg, kFPUInvalidResult); |
+ } |
+ } break; |
+ case CVT_S_D: // Convert double to float (single). |
+ set_fpu_register_float(fd_reg, static_cast<float>(fs)); |
+ break; |
+ case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word. |
+ double rounded = trunc(fs); |
+ i64 = static_cast<int64_t>(rounded); |
+ if (IsFp64Mode()) { |
+ set_fpu_register(fd_reg, i64); |
+ } else { |
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
+ set_fpu_register_word(fd_reg + 1, i64 >> 32); |
+ } |
+ break; |
+ } |
+ case TRUNC_L_D: { // Mips32r2 instruction. |
paul.l...
2015/03/31 04:02:01
This comment (and many other like it need to be ch
|
+ double rounded = trunc(fs); |
+ i64 = static_cast<int64_t>(rounded); |
+ if (IsFp64Mode()) { |
+ set_fpu_register(fd_reg, i64); |
+ } else { |
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
+ set_fpu_register_word(fd_reg + 1, i64 >> 32); |
+ } |
+ break; |
+ } |
+ case ROUND_L_D: { // Mips32r2 instruction. |
+ double rounded = fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5); |
+ i64 = static_cast<int64_t>(rounded); |
+ if (IsFp64Mode()) { |
+ set_fpu_register(fd_reg, i64); |
+ } else { |
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
+ set_fpu_register_word(fd_reg + 1, i64 >> 32); |
+ } |
+ break; |
+ } |
+ case FLOOR_L_D: // Mips32r2 instruction. |
+ i64 = static_cast<int64_t>(std::floor(fs)); |
+ if (IsFp64Mode()) { |
+ set_fpu_register(fd_reg, i64); |
+ } else { |
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
+ set_fpu_register_word(fd_reg + 1, i64 >> 32); |
+ } |
+ break; |
+ case CEIL_L_D: // Mips32r2 instruction. |
+ i64 = static_cast<int64_t>(std::ceil(fs)); |
+ if (IsFp64Mode()) { |
+ set_fpu_register(fd_reg, i64); |
+ } else { |
+ set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
+ set_fpu_register_word(fd_reg + 1, i64 >> 32); |
+ } |
+ break; |
+ case C_F_D: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
- const int32_t fr_reg = instr->FrValue(); |
- const int32_t fs_reg = instr->FsValue(); |
- const int32_t ft_reg = instr->FtValue(); |
- const int32_t fd_reg = instr->FdValue(); |
- int64_t i64hilo = 0; |
- uint64_t u64hilo = 0; |
- // ALU output. |
- // It should not be used as is. Instructions using it should always |
- // initialize it first. |
- int32_t alu_out = 0x12345678; |
+void Simulator::DecodeTypeRegisterWRsType(Instruction* instr, int32_t& alu_out, |
+ const int32_t& fd_reg, |
+ const int32_t& fs_reg) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case CVT_S_W: // Convert word to float (single). |
+ alu_out = get_fpu_register_signed_word(fs_reg); |
+ set_fpu_register_float(fd_reg, static_cast<float>(alu_out)); |
+ break; |
+ case CVT_D_W: // Convert word to double. |
+ alu_out = get_fpu_register_signed_word(fs_reg); |
+ set_fpu_register_double(fd_reg, static_cast<double>(alu_out)); |
+ break; |
+ default: // Mips64r6 CMP.S instructions unimplemented. |
+ UNREACHABLE(); |
+ } |
+} |
- // For break and trap instructions. |
- bool do_interrupt = false; |
- // For jr and jalr. |
- // Get current pc. |
- int32_t current_pc = get_pc(); |
- // Next pc |
- int32_t next_pc = 0; |
- int32_t return_addr_reg = 31; |
- |
- // Set up the variables if needed before executing the instruction. |
- ConfigureTypeRegister(instr, |
- &alu_out, |
- &i64hilo, |
- &u64hilo, |
- &next_pc, |
- &return_addr_reg, |
- &do_interrupt); |
+void Simulator::DecodeTypeRegisterSRsType(Instruction* instr, |
+ const int32_t& ft_reg, |
+ const int32_t& fs_reg, |
+ const int32_t& fd_reg) { |
+ float f; |
+ double ft = get_fpu_register_double(ft_reg); |
+ int64_t ft_int = static_cast<int64_t>(ft); |
+ switch (instr->FunctionFieldRaw()) { |
+ case CVT_D_S: |
+ f = get_fpu_register_float(fs_reg); |
+ set_fpu_register_double(fd_reg, static_cast<double>(f)); |
+ break; |
+ case SELEQZ_C: |
+ DCHECK(IsMipsArchVariant(kMips32r6)); |
+ set_fpu_register_double( |
+ fd_reg, (ft_int & 0x1) == 0 ? get_fpu_register_double(fs_reg) : 0.0); |
+ break; |
+ case SELNEZ_C: |
+ DCHECK(IsMipsArchVariant(kMips32r6)); |
+ set_fpu_register_double( |
+ fd_reg, (ft_int & 0x1) != 0 ? get_fpu_register_double(fs_reg) : 0.0); |
+ break; |
+ default: |
+ // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S |
+ // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented. |
+ UNREACHABLE(); |
+ } |
+} |
- // ---------- Raise exceptions triggered. |
- SignalExceptions(); |
- // ---------- Execution. |
- switch (op) { |
- case COP1: |
- switch (instr->RsFieldRaw()) { |
- case CFC1: |
- set_register(rt_reg, alu_out); |
- break; |
- case MFC1: |
- set_register(rt_reg, alu_out); |
- break; |
- case MFHC1: |
- set_register(rt_reg, alu_out); |
- break; |
- case CTC1: |
- // At the moment only FCSR is supported. |
- DCHECK(fs_reg == kFCSRRegister); |
- FCSR_ = registers_[rt_reg]; |
- break; |
- case MTC1: |
- // Hardware writes upper 32-bits to zero on mtc1. |
- set_fpu_register_hi_word(fs_reg, 0); |
- set_fpu_register_word(fs_reg, registers_[rt_reg]); |
- break; |
- case MTHC1: |
- set_fpu_register_hi_word(fs_reg, registers_[rt_reg]); |
- break; |
- case S: |
- float f; |
- switch (instr->FunctionFieldRaw()) { |
- case CVT_D_S: |
- f = get_fpu_register_float(fs_reg); |
- set_fpu_register_double(fd_reg, static_cast<double>(f)); |
- break; |
- default: |
- // CVT_W_S CVT_L_S TRUNC_W_S ROUND_W_S ROUND_L_S FLOOR_W_S FLOOR_L_S |
- // CEIL_W_S CEIL_L_S CVT_PS_S are unimplemented. |
- UNREACHABLE(); |
- } |
- break; |
- case D: |
- double ft, fs; |
- uint32_t cc, fcsr_cc; |
- int64_t i64; |
- fs = get_fpu_register_double(fs_reg); |
- ft = get_fpu_register_double(ft_reg); |
- cc = instr->FCccValue(); |
- fcsr_cc = get_fcsr_condition_bit(cc); |
- switch (instr->FunctionFieldRaw()) { |
- case ADD_D: |
- set_fpu_register_double(fd_reg, fs + ft); |
- break; |
- case SUB_D: |
- set_fpu_register_double(fd_reg, fs - ft); |
- break; |
- case MUL_D: |
- set_fpu_register_double(fd_reg, fs * ft); |
- break; |
- case DIV_D: |
- set_fpu_register_double(fd_reg, fs / ft); |
- break; |
- case ABS_D: |
- set_fpu_register_double(fd_reg, fabs(fs)); |
- break; |
- case MOV_D: |
- set_fpu_register_double(fd_reg, fs); |
- break; |
- case NEG_D: |
- set_fpu_register_double(fd_reg, -fs); |
- break; |
- case SQRT_D: |
- set_fpu_register_double(fd_reg, fast_sqrt(fs)); |
- break; |
- case C_UN_D: |
- set_fcsr_bit(fcsr_cc, std::isnan(fs) || std::isnan(ft)); |
- break; |
- case C_EQ_D: |
- set_fcsr_bit(fcsr_cc, (fs == ft)); |
- break; |
- case C_UEQ_D: |
- set_fcsr_bit(fcsr_cc, |
- (fs == ft) || (std::isnan(fs) || std::isnan(ft))); |
- break; |
- case C_OLT_D: |
- set_fcsr_bit(fcsr_cc, (fs < ft)); |
- break; |
- case C_ULT_D: |
- set_fcsr_bit(fcsr_cc, |
- (fs < ft) || (std::isnan(fs) || std::isnan(ft))); |
- break; |
- case C_OLE_D: |
- set_fcsr_bit(fcsr_cc, (fs <= ft)); |
- break; |
- case C_ULE_D: |
- set_fcsr_bit(fcsr_cc, |
- (fs <= ft) || (std::isnan(fs) || std::isnan(ft))); |
- break; |
- case CVT_W_D: // Convert double to word. |
- // Rounding modes are not yet supported. |
- DCHECK((FCSR_ & 3) == 0); |
- // In rounding mode 0 it should behave like ROUND. |
- case ROUND_W_D: // Round double to word (round half to even). |
- { |
- double rounded = std::floor(fs + 0.5); |
- int32_t result = static_cast<int32_t>(rounded); |
- if ((result & 1) != 0 && result - fs == 0.5) { |
- // If the number is halfway between two integers, |
- // round to the even one. |
- result--; |
- } |
- set_fpu_register_word(fd_reg, result); |
- if (set_fcsr_round_error(fs, rounded)) { |
- set_fpu_register_word(fd_reg, kFPUInvalidResult); |
- } |
- } |
- break; |
- case TRUNC_W_D: // Truncate double to word (round towards 0). |
- { |
- double rounded = trunc(fs); |
- int32_t result = static_cast<int32_t>(rounded); |
- set_fpu_register_word(fd_reg, result); |
- if (set_fcsr_round_error(fs, rounded)) { |
- set_fpu_register_word(fd_reg, kFPUInvalidResult); |
- } |
- } |
- break; |
- case FLOOR_W_D: // Round double to word towards negative infinity. |
- { |
- double rounded = std::floor(fs); |
- int32_t result = static_cast<int32_t>(rounded); |
- set_fpu_register_word(fd_reg, result); |
- if (set_fcsr_round_error(fs, rounded)) { |
- set_fpu_register_word(fd_reg, kFPUInvalidResult); |
- } |
- } |
- break; |
- case CEIL_W_D: // Round double to word towards positive infinity. |
- { |
- double rounded = std::ceil(fs); |
- int32_t result = static_cast<int32_t>(rounded); |
- set_fpu_register_word(fd_reg, result); |
- if (set_fcsr_round_error(fs, rounded)) { |
- set_fpu_register_word(fd_reg, kFPUInvalidResult); |
- } |
- } |
- break; |
- case CVT_S_D: // Convert double to float (single). |
- set_fpu_register_float(fd_reg, static_cast<float>(fs)); |
- break; |
- case CVT_L_D: { // Mips32r2: Truncate double to 64-bit long-word. |
- double rounded = trunc(fs); |
- i64 = static_cast<int64_t>(rounded); |
- if (IsFp64Mode()) { |
- set_fpu_register(fd_reg, i64); |
- } else { |
- set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
- set_fpu_register_word(fd_reg + 1, i64 >> 32); |
- } |
- break; |
- } |
- case TRUNC_L_D: { // Mips32r2 instruction. |
- double rounded = trunc(fs); |
- i64 = static_cast<int64_t>(rounded); |
- if (IsFp64Mode()) { |
- set_fpu_register(fd_reg, i64); |
- } else { |
- set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
- set_fpu_register_word(fd_reg + 1, i64 >> 32); |
- } |
- break; |
- } |
- case ROUND_L_D: { // Mips32r2 instruction. |
- double rounded = |
- fs > 0 ? std::floor(fs + 0.5) : std::ceil(fs - 0.5); |
- i64 = static_cast<int64_t>(rounded); |
- if (IsFp64Mode()) { |
- set_fpu_register(fd_reg, i64); |
- } else { |
- set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
- set_fpu_register_word(fd_reg + 1, i64 >> 32); |
- } |
- break; |
- } |
- case FLOOR_L_D: // Mips32r2 instruction. |
- i64 = static_cast<int64_t>(std::floor(fs)); |
- if (IsFp64Mode()) { |
- set_fpu_register(fd_reg, i64); |
- } else { |
- set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
- set_fpu_register_word(fd_reg + 1, i64 >> 32); |
- } |
- break; |
- case CEIL_L_D: // Mips32r2 instruction. |
- i64 = static_cast<int64_t>(std::ceil(fs)); |
- if (IsFp64Mode()) { |
- set_fpu_register(fd_reg, i64); |
- } else { |
- set_fpu_register_word(fd_reg, i64 & 0xffffffff); |
- set_fpu_register_word(fd_reg + 1, i64 >> 32); |
- } |
- break; |
- case C_F_D: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- default: |
- UNREACHABLE(); |
- } |
- break; |
- case W: |
- switch (instr->FunctionFieldRaw()) { |
- case CVT_S_W: // Convert word to float (single). |
- alu_out = get_fpu_register_signed_word(fs_reg); |
- set_fpu_register_float(fd_reg, static_cast<float>(alu_out)); |
- break; |
- case CVT_D_W: // Convert word to double. |
- alu_out = get_fpu_register_signed_word(fs_reg); |
- set_fpu_register_double(fd_reg, static_cast<double>(alu_out)); |
- break; |
- default: // Mips64r6 CMP.S instructions unimplemented. |
- UNREACHABLE(); |
- } |
- break; |
- case L: |
- fs = get_fpu_register_double(fs_reg); |
- ft = get_fpu_register_double(ft_reg); |
- switch (instr->FunctionFieldRaw()) { |
- case CVT_D_L: // Mips32r2 instruction. |
- // Watch the signs here, we want 2 32-bit vals |
- // to make a sign-64. |
- if (IsFp64Mode()) { |
- i64 = get_fpu_register(fs_reg); |
- } else { |
- i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg)); |
- i64 |= static_cast<int64_t>( |
- get_fpu_register_word(fs_reg + 1)) << 32; |
- } |
- set_fpu_register_double(fd_reg, static_cast<double>(i64)); |
- break; |
- case CVT_S_L: |
- UNIMPLEMENTED_MIPS(); |
- break; |
- case CMP_AF: // Mips64r6 CMP.D instructions. |
- UNIMPLEMENTED_MIPS(); |
- break; |
- case CMP_UN: |
- if (std::isnan(fs) || std::isnan(ft)) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_EQ: |
- if (fs == ft) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_UEQ: |
- if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_LT: |
- if (fs < ft) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_ULT: |
- if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_LE: |
- if (fs <= ft) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- case CMP_ULE: |
- if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { |
- set_fpu_register(fd_reg, -1); |
- } else { |
- set_fpu_register(fd_reg, 0); |
- } |
- break; |
- default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED. |
- UNREACHABLE(); |
- } |
- break; |
- default: |
- UNREACHABLE(); |
+void Simulator::DecodeTypeRegisterLRsType(Instruction* instr, |
+ const int32_t& ft_reg, |
+ const int32_t& fs_reg, |
+ const int32_t& fd_reg) { |
+ double fs = get_fpu_register_double(fs_reg); |
+ double ft = get_fpu_register_double(ft_reg); |
+ switch (instr->FunctionFieldRaw()) { |
+ case CVT_D_L: // Mips32r2 instruction. |
+ // Watch the signs here, we want 2 32-bit vals |
+ // to make a sign-64. |
+ int64_t i64; |
+ if (IsFp64Mode()) { |
+ i64 = get_fpu_register(fs_reg); |
+ } else { |
+ i64 = static_cast<uint32_t>(get_fpu_register_word(fs_reg)); |
+ i64 |= static_cast<int64_t>(get_fpu_register_word(fs_reg + 1)) << 32; |
} |
+ set_fpu_register_double(fd_reg, static_cast<double>(i64)); |
break; |
- case COP1X: |
- switch (instr->FunctionFieldRaw()) { |
- case MADD_D: |
- double fr, ft, fs; |
- fr = get_fpu_register_double(fr_reg); |
- fs = get_fpu_register_double(fs_reg); |
- ft = get_fpu_register_double(ft_reg); |
- set_fpu_register_double(fd_reg, fs * ft + fr); |
- break; |
- default: |
- UNREACHABLE(); |
+ case CVT_S_L: |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ case CMP_AF: // Mips64r6 CMP.D instructions. |
+ UNIMPLEMENTED_MIPS(); |
+ break; |
+ case CMP_UN: |
+ if (std::isnan(fs) || std::isnan(ft)) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
} |
break; |
- case SPECIAL: |
- switch (instr->FunctionFieldRaw()) { |
+ case CMP_EQ: |
+ if (fs == ft) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_UEQ: |
+ if ((fs == ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_LT: |
+ if (fs < ft) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_ULT: |
+ if ((fs < ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_LE: |
+ if (fs <= ft) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ case CMP_ULE: |
+ if ((fs <= ft) || (std::isnan(fs) || std::isnan(ft))) { |
+ set_fpu_register(fd_reg, -1); |
+ } else { |
+ set_fpu_register(fd_reg, 0); |
+ } |
+ break; |
+ default: // CMP_OR CMP_UNE CMP_NE UNIMPLEMENTED. |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterCOP1( |
+ Instruction* instr, const int32_t& rs_reg, const int32_t& rs, |
+ const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt, |
+ const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg, |
+ const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg, |
+ int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt, |
+ int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg) { |
+ switch (instr->RsFieldRaw()) { |
+ case CFC1: |
+ set_register(rt_reg, alu_out); |
+ break; |
+ case MFC1: |
+ set_register(rt_reg, alu_out); |
+ break; |
+ case MFHC1: |
+ set_register(rt_reg, alu_out); |
+ break; |
+ case CTC1: |
+ // At the moment only FCSR is supported. |
+ DCHECK(fs_reg == kFCSRRegister); |
+ FCSR_ = registers_[rt_reg]; |
+ break; |
+ case MTC1: |
+ // Hardware writes upper 32-bits to zero on mtc1. |
+ set_fpu_register_hi_word(fs_reg, 0); |
+ set_fpu_register_word(fs_reg, registers_[rt_reg]); |
+ break; |
+ case MTHC1: |
+ set_fpu_register_hi_word(fs_reg, registers_[rt_reg]); |
+ break; |
+ case S: { |
+ DecodeTypeRegisterSRsType(instr, ft_reg, fs_reg, fd_reg); |
+ break; |
+ } |
+ case D: |
+ DecodeTypeRegisterDRsType(instr, fr_reg, fs_reg, ft_reg, fd_reg); |
+ break; |
+ case W: |
+ DecodeTypeRegisterWRsType(instr, alu_out, fd_reg, fs_reg); |
+ break; |
+ case L: |
+ DecodeTypeRegisterLRsType(instr, ft_reg, fs_reg, fd_reg); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterCOP1X(Instruction* instr, |
+ const int32_t& fr_reg, |
+ const int32_t& fs_reg, |
+ const int32_t& ft_reg, |
+ const int32_t& fd_reg) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case MADD_D: |
+ double fr, ft, fs; |
+ fr = get_fpu_register_double(fr_reg); |
+ fs = get_fpu_register_double(fs_reg); |
+ ft = get_fpu_register_double(ft_reg); |
+ set_fpu_register_double(fd_reg, fs * ft + fr); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterSPECIAL( |
+ Instruction* instr, const int32_t& rs_reg, const int32_t& rs, |
+ const uint32_t& rs_u, const int32_t& rt_reg, const int32_t& rt, |
+ const uint32_t& rt_u, const int32_t& rd_reg, const int32_t& fr_reg, |
+ const int32_t& fs_reg, const int32_t& ft_reg, const int32_t& fd_reg, |
+ int64_t& i64hilo, uint64_t& u64hilo, int32_t& alu_out, bool& do_interrupt, |
+ int32_t& current_pc, int32_t& next_pc, int32_t& return_addr_reg) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case SELEQZ_S: |
+ DCHECK(IsMipsArchVariant(kMips32r6)); |
+ set_register(rd_reg, rt == 0 ? rs : 0); |
+ break; |
+ case SELNEZ_S: |
+ DCHECK(IsMipsArchVariant(kMips32r6)); |
+ set_register(rd_reg, rt != 0 ? rs : 0); |
+ break; |
case JR: { |
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>( |
current_pc+Instruction::kInstrSize); |
@@ -2638,32 +2667,105 @@ void Simulator::DecodeTypeRegister(Instruction* instr) { |
default: // For other special opcodes we do the default operation. |
set_register(rd_reg, alu_out); |
} |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterSPECIAL2(Instruction* instr, |
+ const int32_t& rd_reg, |
+ int32_t& alu_out) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case MUL: |
+ set_register(rd_reg, alu_out); |
+ // HI and LO are UNPREDICTABLE after the operation. |
+ set_register(LO, Unpredictable); |
+ set_register(HI, Unpredictable); |
+ break; |
+ default: // For other special2 opcodes we do the default operation. |
+ set_register(rd_reg, alu_out); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegisterSPECIAL3(Instruction* instr, |
+ const int32_t& rt_reg, |
+ int32_t& alu_out) { |
+ switch (instr->FunctionFieldRaw()) { |
+ case INS: |
+ // Ins instr leaves result in Rt, rather than Rd. |
+ set_register(rt_reg, alu_out); |
+ break; |
+ case EXT: |
+ // Ext instr leaves result in Rt, rather than Rd. |
+ set_register(rt_reg, alu_out); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void Simulator::DecodeTypeRegister(Instruction* instr) { |
+ // Instruction fields. |
+ const Opcode op = instr->OpcodeFieldRaw(); |
+ const int32_t rs_reg = instr->RsValue(); |
+ const int32_t rs = get_register(rs_reg); |
+ const uint32_t rs_u = static_cast<uint32_t>(rs); |
+ const int32_t rt_reg = instr->RtValue(); |
+ const int32_t rt = get_register(rt_reg); |
+ const uint32_t rt_u = static_cast<uint32_t>(rt); |
+ const int32_t rd_reg = instr->RdValue(); |
+ |
+ const int32_t fr_reg = instr->FrValue(); |
+ const int32_t fs_reg = instr->FsValue(); |
+ const int32_t ft_reg = instr->FtValue(); |
+ const int32_t fd_reg = instr->FdValue(); |
+ int64_t i64hilo = 0; |
+ uint64_t u64hilo = 0; |
+ |
+ // ALU output. |
+ // It should not be used as is. Instructions using it should always |
+ // initialize it first. |
+ int32_t alu_out = 0x12345678; |
+ |
+ // For break and trap instructions. |
+ bool do_interrupt = false; |
+ |
+ // For jr and jalr. |
+ // Get current pc. |
+ int32_t current_pc = get_pc(); |
+ // Next pc |
+ int32_t next_pc = 0; |
+ int32_t return_addr_reg = 31; |
+ |
+ // Set up the variables if needed before executing the instruction. |
+ ConfigureTypeRegister(instr, &alu_out, &i64hilo, &u64hilo, &next_pc, |
+ &return_addr_reg, &do_interrupt); |
+ |
+ // ---------- Raise exceptions triggered. |
+ SignalExceptions(); |
+ |
+ // ---------- Execution. |
+ switch (op) { |
+ case COP1: |
+ DecodeTypeRegisterCOP1(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, rd_reg, |
+ fr_reg, fs_reg, ft_reg, fd_reg, i64hilo, u64hilo, |
+ alu_out, do_interrupt, current_pc, next_pc, |
+ return_addr_reg); |
+ break; |
+ case COP1X: |
+ DecodeTypeRegisterCOP1X(instr, fr_reg, fs_reg, ft_reg, fd_reg); |
+ break; |
+ case SPECIAL: |
+ DecodeTypeRegisterSPECIAL(instr, rs_reg, rs, rs_u, rt_reg, rt, rt_u, |
+ rd_reg, fr_reg, fs_reg, ft_reg, fd_reg, i64hilo, |
+ u64hilo, alu_out, do_interrupt, current_pc, |
+ next_pc, return_addr_reg); |
break; |
case SPECIAL2: |
- switch (instr->FunctionFieldRaw()) { |
- case MUL: |
- set_register(rd_reg, alu_out); |
- // HI and LO are UNPREDICTABLE after the operation. |
- set_register(LO, Unpredictable); |
- set_register(HI, Unpredictable); |
- break; |
- default: // For other special2 opcodes we do the default operation. |
- set_register(rd_reg, alu_out); |
- } |
+ DecodeTypeRegisterSPECIAL2(instr, rd_reg, alu_out); |
break; |
case SPECIAL3: |
- switch (instr->FunctionFieldRaw()) { |
- case INS: |
- // Ins instr leaves result in Rt, rather than Rd. |
- set_register(rt_reg, alu_out); |
- break; |
- case EXT: |
- // Ext instr leaves result in Rt, rather than Rd. |
- set_register(rt_reg, alu_out); |
- break; |
- default: |
- UNREACHABLE(); |
- } |
+ DecodeTypeRegisterSPECIAL3(instr, rt_reg, alu_out); |
break; |
// Unimplemented opcodes raised an error in the configuration step before, |
// so we can use the default here to set the destination register in common |