diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp index 8f912cd73b..10bed94201 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp @@ -20,6 +20,7 @@ #include "Core/HLE/HLE.h" #include "Core/HW/CPU.h" #include "Core/Host.h" +#include "Core/PowerPC/Interpreter/ExceptionUtils.h" #include "Core/PowerPC/MMU.h" #include "Core/PowerPC/PPCTables.h" #include "Core/PowerPC/PowerPC.h" @@ -43,6 +44,42 @@ std::array Interpreter::m_op_table31; std::array Interpreter::m_op_table59; std::array Interpreter::m_op_table63; +namespace +{ +// Determines whether or not the given instruction is one where its execution +// validity is determined by whether or not HID2's LSQE bit is set. +// In other words, if the instruction is psq_l, psq_lu, psq_st, or psq_stu +bool IsPairedSingleQuantizedNonIndexedInstruction(UGeckoInstruction inst) +{ + const u32 opcode = inst.OPCD; + return opcode == 0x38 || opcode == 0x39 || opcode == 0x3C || opcode == 0x3D; +} + +bool IsPairedSingleInstruction(UGeckoInstruction inst) +{ + return inst.OPCD == 4 || IsPairedSingleQuantizedNonIndexedInstruction(inst); +} + +// Checks if a given instruction would be illegal to execute if it's a paired single instruction. +// +// Paired single instructions are illegal to execute if HID2.PSE is not set. +// It's also illegal to execute psq_l, psq_lu, psq_st, and psq_stu if HID2.PSE is enabled, +// but HID2.LSQE is not set. +bool IsInvalidPairedSingleExecution(UGeckoInstruction inst) +{ + if (!HID2.PSE && IsPairedSingleInstruction(inst)) + return true; + + return HID2.PSE && !HID2.LSQE && IsPairedSingleQuantizedNonIndexedInstruction(inst); +} + +void UpdatePC() +{ + last_pc = PC; + PC = NPC; +} +} // Anonymous namespace + void Interpreter::RunTable4(UGeckoInstruction inst) { m_op_table4[inst.SUBOP10](inst); @@ -110,75 +147,77 @@ bool Interpreter::HandleFunctionHooking(u32 address) int Interpreter::SingleStepInner() { - if (!HandleFunctionHooking(PC)) + if (HandleFunctionHooking(PC)) { -#ifdef USE_GDBSTUB - if (gdb_active() && gdb_bp_x(PC)) - { - Host_UpdateDisasmDialog(); + UpdatePC(); + return PPCTables::GetOpInfo(m_prev_inst)->numCycles; + } - gdb_signal(GDB_SIGTRAP); - gdb_handle_exception(); - } +#ifdef USE_GDBSTUB + if (gdb_active() && gdb_bp_x(PC)) + { + Host_UpdateDisasmDialog(); + + gdb_signal(GDB_SIGTRAP); + gdb_handle_exception(); + } #endif - NPC = PC + sizeof(UGeckoInstruction); - m_prev_inst.hex = PowerPC::Read_Opcode(PC); + NPC = PC + sizeof(UGeckoInstruction); + m_prev_inst.hex = PowerPC::Read_Opcode(PC); - // Uncomment to trace the interpreter - // if ((PC & 0xffffff)>=0x0ab54c && (PC & 0xffffff)<=0x0ab624) - // startTrace = 1; - // else - // startTrace = 0; + // Uncomment to trace the interpreter + // if ((PC & 0xffffff)>=0x0ab54c && (PC & 0xffffff)<=0x0ab624) + // startTrace = 1; + // else + // startTrace = 0; - if (startTrace) + if (startTrace) + { + Trace(m_prev_inst); + } + + if (m_prev_inst.hex != 0) + { + if (IsInvalidPairedSingleExecution(m_prev_inst)) { - Trace(m_prev_inst); + GenerateProgramException(); + CheckExceptions(); } - - if (m_prev_inst.hex != 0) + else if (MSR.FP) { - if (MSR.FP) // If FPU is enabled, just execute + m_op_table[m_prev_inst.OPCD](m_prev_inst); + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { - m_op_table[m_prev_inst.OPCD](m_prev_inst); - if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) - { - PowerPC::CheckExceptions(); - m_end_block = true; - } - } - else - { - // check if we have to generate a FPU unavailable exception - if (!PPCTables::UsesFPU(m_prev_inst)) - { - m_op_table[m_prev_inst.OPCD](m_prev_inst); - if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) - { - PowerPC::CheckExceptions(); - m_end_block = true; - } - } - else - { - PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; - PowerPC::CheckExceptions(); - m_end_block = true; - } + CheckExceptions(); } } else { - // Memory exception on instruction fetch - PowerPC::CheckExceptions(); - m_end_block = true; + // check if we have to generate a FPU unavailable exception or a program exception. + if (PPCTables::UsesFPU(m_prev_inst)) + { + PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; + CheckExceptions(); + } + else + { + m_op_table[m_prev_inst.OPCD](m_prev_inst); + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) + { + CheckExceptions(); + } + } } } - last_pc = PC; - PC = NPC; + else + { + // Memory exception on instruction fetch + CheckExceptions(); + } - const GekkoOPInfo* opinfo = PPCTables::GetOpInfo(m_prev_inst); - return opinfo->numCycles; + UpdatePC(); + return PPCTables::GetOpInfo(m_prev_inst)->numCycles; } void Interpreter::SingleStep() @@ -317,6 +356,12 @@ void Interpreter::ClearCache() // Do nothing. } +void Interpreter::CheckExceptions() +{ + PowerPC::CheckExceptions(); + m_end_block = true; +} + const char* Interpreter::GetName() const { #ifdef _ARCH_64 diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter.h index 8c76b6de6f..c823e88606 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter.h @@ -282,6 +282,8 @@ public: static u32 Helper_Carry(u32 value1, u32 value2); private: + void CheckExceptions(); + static void InitializeInstructionTables(); static bool HandleFunctionHooking(u32 address); diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp index 6de8588307..7a5e9bcd70 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp @@ -341,11 +341,12 @@ void Interpreter::mtspr(UGeckoInstruction inst) rSPR(index) &= 0xF8000000; break; - case SPR_HID2: // HID2 - // TODO: generate illegal instruction for paired inst if PSE or LSQE - // not set. + case SPR_HID2: // TODO: disable write gather pipe if WPE not set // TODO: emulate locked cache and DMA bits. + // Only the lower half of the register (upper half from a little endian perspective) + // is modifiable, except for the DMAQL field. + rSPR(index) = (rSPR(index) & 0xF0FF0000) | (old_value & 0x0F000000); break; case SPR_HID4: