Merge pull request #7620 from lioncash/paired

Interpreter: Handle HID2.PSE and HID2.LSQE bits
This commit is contained in:
Mat M 2018-12-28 06:30:37 -05:00 committed by GitHub
commit 8c9f553734
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 55 deletions

View File

@ -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

View File

@ -282,6 +282,8 @@ public:
static u32 Helper_Carry(u32 value1, u32 value2);
private:
void CheckExceptions();
static void InitializeInstructionTables();
static bool HandleFunctionHooking(u32 address);

View File

@ -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: