Interpreter: Handle paired-single HID2.PSE and HID2.LSQE bits
These bits enable or disable paired-single execution based on how they're set. If PSE isn't set, then all paired-single instructions are illegal. If PSE is set, but LSQE isn't set, then psq_l, psq_lu, psq_st and psq_stu are illegal to execute. Also thanks go out to my roommate @Veegie for letting me use his Wii as a blasting ground for tests, since mine isn't on hand right now. It only caught on fire twice and only burned down half of the house through the process; what a team player.
This commit is contained in:
parent
c87a2f57b4
commit
52cae18b01
|
@ -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::Instruction, 1024> Interpreter::m_op_table31;
|
|||
std::array<Interpreter::Instruction, 32> Interpreter::m_op_table59;
|
||||
std::array<Interpreter::Instruction, 1024> 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
|
||||
|
|
|
@ -282,6 +282,8 @@ public:
|
|||
static u32 Helper_Carry(u32 value1, u32 value2);
|
||||
|
||||
private:
|
||||
void CheckExceptions();
|
||||
|
||||
static void InitializeInstructionTables();
|
||||
|
||||
static bool HandleFunctionHooking(u32 address);
|
||||
|
|
|
@ -341,9 +341,7 @@ 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)
|
||||
|
|
Loading…
Reference in New Issue