CPU: Implement PGXP CPU Mode
This is *very* slow. You don't want to enable it if you don't need it. It is also incompatible with the recompiler and will disable it if the option is enabled.
This commit is contained in:
parent
db6b9e3bf4
commit
2e9f656546
|
@ -11,6 +11,7 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c
|
|||
|
||||
## Latest News
|
||||
|
||||
- 2020/08/19: CPU PGXP mode added. It is very slow and incompatible with the recompiler, only use for games which need it.
|
||||
- 2020/08/15: Playlist support/single memcard for multi-disc games in Qt frontend added.
|
||||
- 2020/08/07: Automatic updater for standalone Windows builds.
|
||||
- 2020/08/01: Initial PGXP (geometry/perspective correction) support.
|
||||
|
|
|
@ -15,6 +15,13 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
// Force inline in non-debug helper
|
||||
#ifdef _DEBUG
|
||||
#define ALWAYS_INLINE_RELEASE
|
||||
#else
|
||||
#define ALWAYS_INLINE_RELEASE ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
// unreferenced parameter macro
|
||||
#ifndef UNREFERENCED_VARIABLE
|
||||
#if defined(_MSC_VER)
|
||||
|
|
|
@ -110,7 +110,8 @@ void Shutdown()
|
|||
#endif
|
||||
}
|
||||
|
||||
void Execute()
|
||||
template<PGXPMode pgxp_mode>
|
||||
static void ExecuteImpl()
|
||||
{
|
||||
CodeBlockKey next_block_key;
|
||||
|
||||
|
@ -157,7 +158,7 @@ void Execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
InterpretCachedBlock(*block);
|
||||
InterpretCachedBlock<pgxp_mode>(*block);
|
||||
}
|
||||
|
||||
if (g_state.pending_ticks >= g_state.downcount)
|
||||
|
@ -212,6 +213,21 @@ void Execute()
|
|||
g_state.regs.npc = g_state.regs.pc;
|
||||
}
|
||||
|
||||
void Execute()
|
||||
{
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
if (g_settings.gpu_pgxp_cpu)
|
||||
ExecuteImpl<PGXPMode::CPU>();
|
||||
else
|
||||
ExecuteImpl<PGXPMode::Memory>();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteImpl<PGXPMode::Disabled>();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_RECOMPILER
|
||||
|
||||
void ExecuteRecompiler()
|
||||
|
|
|
@ -96,6 +96,7 @@ void SetUseRecompiler(bool enable);
|
|||
/// Invalidates all blocks which are in the range of the specified code page.
|
||||
void InvalidateBlocksWithPageIndex(u32 page_index);
|
||||
|
||||
template<PGXPMode pgxp_mode>
|
||||
void InterpretCachedBlock(const CodeBlock& block);
|
||||
void InterpretUncachedBlock();
|
||||
|
||||
|
|
|
@ -15,19 +15,9 @@ Log_SetChannel(CPU::Core);
|
|||
|
||||
namespace CPU {
|
||||
|
||||
/// Sets the PC and flushes the pipeline.
|
||||
static void SetPC(u32 new_pc);
|
||||
|
||||
// Updates load delays - call after each instruction
|
||||
static void UpdateLoadDelay();
|
||||
|
||||
// Fetches the instruction at m_regs.npc
|
||||
static void ExecuteInstruction();
|
||||
static void ExecuteCop0Instruction();
|
||||
static void ExecuteCop2Instruction();
|
||||
static void Branch(u32 target);
|
||||
|
||||
// clears pipeline of load/branch delays
|
||||
static void FlushPipeline();
|
||||
|
||||
State g_state;
|
||||
|
@ -139,14 +129,14 @@ bool DoState(StateWrapper& sw)
|
|||
return !sw.HasError();
|
||||
}
|
||||
|
||||
void SetPC(u32 new_pc)
|
||||
ALWAYS_INLINE_RELEASE void SetPC(u32 new_pc)
|
||||
{
|
||||
DebugAssert(Common::IsAlignedPow2(new_pc, 4));
|
||||
g_state.regs.npc = new_pc;
|
||||
FlushPipeline();
|
||||
}
|
||||
|
||||
void Branch(u32 target)
|
||||
ALWAYS_INLINE_RELEASE void Branch(u32 target)
|
||||
{
|
||||
if (!Common::IsAlignedPow2(target, 4))
|
||||
{
|
||||
|
@ -240,7 +230,7 @@ void ClearExternalInterrupt(u8 bit)
|
|||
g_state.cop0_regs.cause.Ip &= static_cast<u8>(~(1u << bit));
|
||||
}
|
||||
|
||||
void UpdateLoadDelay()
|
||||
ALWAYS_INLINE_RELEASE static void UpdateLoadDelay()
|
||||
{
|
||||
// the old value is needed in case the delay slot instruction overwrites the same register
|
||||
if (g_state.load_delay_reg != Reg::count)
|
||||
|
@ -251,7 +241,7 @@ void UpdateLoadDelay()
|
|||
g_state.next_load_delay_reg = Reg::count;
|
||||
}
|
||||
|
||||
void FlushPipeline()
|
||||
ALWAYS_INLINE_RELEASE static void FlushPipeline()
|
||||
{
|
||||
// loads are flushed
|
||||
g_state.next_load_delay_reg = Reg::count;
|
||||
|
@ -275,12 +265,12 @@ void FlushPipeline()
|
|||
g_state.current_instruction_was_branch_taken = false;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 ReadReg(Reg rs)
|
||||
ALWAYS_INLINE static u32 ReadReg(Reg rs)
|
||||
{
|
||||
return g_state.regs.r[static_cast<u8>(rs)];
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void WriteReg(Reg rd, u32 value)
|
||||
ALWAYS_INLINE static void WriteReg(Reg rd, u32 value)
|
||||
{
|
||||
g_state.regs.r[static_cast<u8>(rd)] = value;
|
||||
g_state.load_delay_reg = (rd == g_state.load_delay_reg) ? Reg::count : g_state.load_delay_reg;
|
||||
|
@ -289,7 +279,7 @@ ALWAYS_INLINE void WriteReg(Reg rd, u32 value)
|
|||
g_state.regs.zero = 0;
|
||||
}
|
||||
|
||||
static void WriteRegDelayed(Reg rd, u32 value)
|
||||
ALWAYS_INLINE_RELEASE static void WriteRegDelayed(Reg rd, u32 value)
|
||||
{
|
||||
Assert(g_state.next_load_delay_reg == Reg::count);
|
||||
if (rd == Reg::zero)
|
||||
|
@ -304,7 +294,7 @@ static void WriteRegDelayed(Reg rd, u32 value)
|
|||
g_state.next_load_delay_value = value;
|
||||
}
|
||||
|
||||
static std::optional<u32> ReadCop0Reg(Cop0Reg reg)
|
||||
ALWAYS_INLINE_RELEASE static std::optional<u32> ReadCop0Reg(Cop0Reg reg)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
|
@ -347,7 +337,7 @@ static std::optional<u32> ReadCop0Reg(Cop0Reg reg)
|
|||
}
|
||||
}
|
||||
|
||||
static void WriteCop0Reg(Cop0Reg reg, u32 value)
|
||||
ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
|
@ -431,12 +421,12 @@ static void LogInstruction(u32 bits, u32 pc, Registers* regs)
|
|||
WriteToExecutionLog("%08x: %08x %s\n", pc, bits, instr.GetCharArray());
|
||||
}
|
||||
|
||||
static constexpr bool AddOverflow(u32 old_value, u32 add_value, u32 new_value)
|
||||
ALWAYS_INLINE static constexpr bool AddOverflow(u32 old_value, u32 add_value, u32 new_value)
|
||||
{
|
||||
return (((new_value ^ old_value) & (new_value ^ add_value)) & UINT32_C(0x80000000)) != 0;
|
||||
}
|
||||
|
||||
static constexpr bool SubOverflow(u32 old_value, u32 sub_value, u32 new_value)
|
||||
ALWAYS_INLINE static constexpr bool SubOverflow(u32 old_value, u32 sub_value, u32 new_value)
|
||||
{
|
||||
return (((new_value ^ old_value) & (old_value ^ sub_value)) & UINT32_C(0x80000000)) != 0;
|
||||
}
|
||||
|
@ -467,53 +457,8 @@ void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instru
|
|||
}
|
||||
}
|
||||
|
||||
void Execute()
|
||||
{
|
||||
g_state.frame_done = false;
|
||||
while (!g_state.frame_done)
|
||||
{
|
||||
TimingEvents::UpdateCPUDowncount();
|
||||
|
||||
while (g_state.pending_ticks <= g_state.downcount)
|
||||
{
|
||||
if (HasPendingInterrupt())
|
||||
DispatchInterrupt();
|
||||
|
||||
g_state.pending_ticks++;
|
||||
|
||||
// now executing the instruction we previously fetched
|
||||
g_state.current_instruction.bits = g_state.next_instruction.bits;
|
||||
g_state.current_instruction_pc = g_state.regs.pc;
|
||||
g_state.current_instruction_in_branch_delay_slot = g_state.next_instruction_is_branch_delay_slot;
|
||||
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
|
||||
g_state.next_instruction_is_branch_delay_slot = false;
|
||||
g_state.branch_was_taken = false;
|
||||
g_state.exception_raised = false;
|
||||
|
||||
// fetch the next instruction
|
||||
if (!FetchInstruction())
|
||||
continue;
|
||||
|
||||
#if 0 // GTE flag test debugging
|
||||
if (g_state.m_current_instruction_pc == 0x8002cdf4)
|
||||
{
|
||||
if (g_state.m_regs.v1 != g_state.m_regs.v0)
|
||||
printf("Got %08X Expected? %08X\n", g_state.m_regs.v1, g_state.m_regs.v0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
}
|
||||
|
||||
TimingEvents::RunEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteInstruction()
|
||||
template<PGXPMode pgxp_mode>
|
||||
ALWAYS_INLINE_RELEASE static void ExecuteInstruction()
|
||||
{
|
||||
const Instruction inst = g_state.current_instruction;
|
||||
|
||||
|
@ -525,14 +470,6 @@ void ExecuteInstruction()
|
|||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (g_state.m_current_instruction_pc == 0x8002bf50)
|
||||
{
|
||||
TRACE_EXECUTION = true;
|
||||
__debugbreak();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (TRACE_EXECUTION)
|
||||
PrintInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
|
||||
|
@ -549,6 +486,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::sll:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rt) << inst.r.shamt;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLL(inst.bits, new_value, ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -556,6 +496,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::srl:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rt) >> inst.r.shamt;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRL(inst.bits, new_value, ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -563,6 +506,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::sra:
|
||||
{
|
||||
const u32 new_value = static_cast<u32>(static_cast<s32>(ReadReg(inst.r.rt)) >> inst.r.shamt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRA(inst.bits, new_value, ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -571,6 +517,9 @@ void ExecuteInstruction()
|
|||
{
|
||||
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
||||
const u32 new_value = ReadReg(inst.r.rt) << shift_amount;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLLV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -579,6 +528,9 @@ void ExecuteInstruction()
|
|||
{
|
||||
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
||||
const u32 new_value = ReadReg(inst.r.rt) >> shift_amount;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRLV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -587,6 +539,9 @@ void ExecuteInstruction()
|
|||
{
|
||||
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
||||
const u32 new_value = static_cast<u32>(static_cast<s32>(ReadReg(inst.r.rt)) >> shift_amount);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRAV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -594,6 +549,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::and_:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) & ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_AND(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -601,6 +559,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::or_:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) | ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_OR(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -608,6 +569,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::xor_:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) ^ ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_XOR(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -615,6 +579,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::nor:
|
||||
{
|
||||
const u32 new_value = ~(ReadReg(inst.r.rs) | ReadReg(inst.r.rt));
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_NOR(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -630,6 +597,9 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
PGXP::CPU_ADD(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -637,6 +607,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::addu:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) + ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ADDU(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -652,6 +625,9 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SUB(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -659,6 +635,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::subu:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) - ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SUBU(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -666,6 +645,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::slt:
|
||||
{
|
||||
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.r.rs)) < static_cast<s32>(ReadReg(inst.r.rt)));
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLT(inst.bits, result, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, result);
|
||||
}
|
||||
break;
|
||||
|
@ -673,12 +655,18 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::sltu:
|
||||
{
|
||||
const u32 result = BoolToUInt32(ReadReg(inst.r.rs) < ReadReg(inst.r.rt));
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLTU(inst.bits, result, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, result);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionFunct::mfhi:
|
||||
{
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MFHI(inst.bits, ReadReg(inst.r.rd), g_state.regs.hi);
|
||||
|
||||
WriteReg(inst.r.rd, g_state.regs.hi);
|
||||
}
|
||||
break;
|
||||
|
@ -686,12 +674,18 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::mthi:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rs);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MTHI(inst.bits, g_state.regs.hi, value);
|
||||
|
||||
g_state.regs.hi = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionFunct::mflo:
|
||||
{
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MFLO(inst.bits, ReadReg(inst.r.rd), g_state.regs.lo);
|
||||
|
||||
WriteReg(inst.r.rd, g_state.regs.lo);
|
||||
}
|
||||
break;
|
||||
|
@ -699,6 +693,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::mtlo:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rs);
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
PGXP::CPU_MTLO(inst.bits, g_state.regs.lo, value);
|
||||
|
||||
g_state.regs.lo = value;
|
||||
}
|
||||
break;
|
||||
|
@ -709,8 +706,12 @@ void ExecuteInstruction()
|
|||
const u32 rhs = ReadReg(inst.r.rt);
|
||||
const u64 result =
|
||||
static_cast<u64>(static_cast<s64>(SignExtend64(lhs)) * static_cast<s64>(SignExtend64(rhs)));
|
||||
|
||||
g_state.regs.hi = Truncate32(result >> 32);
|
||||
g_state.regs.lo = Truncate32(result);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MULT(inst.bits, g_state.regs.hi, g_state.regs.lo, lhs, rhs);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -719,6 +720,10 @@ void ExecuteInstruction()
|
|||
const u32 lhs = ReadReg(inst.r.rs);
|
||||
const u32 rhs = ReadReg(inst.r.rt);
|
||||
const u64 result = ZeroExtend64(lhs) * ZeroExtend64(rhs);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MULTU(inst.bits, g_state.regs.hi, g_state.regs.lo, lhs, rhs);
|
||||
|
||||
g_state.regs.hi = Truncate32(result >> 32);
|
||||
g_state.regs.lo = Truncate32(result);
|
||||
}
|
||||
|
@ -746,6 +751,9 @@ void ExecuteInstruction()
|
|||
g_state.regs.lo = static_cast<u32>(num / denom);
|
||||
g_state.regs.hi = static_cast<u32>(num % denom);
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_DIV(inst.bits, g_state.regs.hi, g_state.regs.lo, num, denom);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -765,6 +773,9 @@ void ExecuteInstruction()
|
|||
g_state.regs.lo = num / denom;
|
||||
g_state.regs.hi = num % denom;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_DIVU(inst.bits, g_state.regs.hi, g_state.regs.lo, num, denom);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -808,25 +819,44 @@ void ExecuteInstruction()
|
|||
|
||||
case InstructionOp::lui:
|
||||
{
|
||||
WriteReg(inst.i.rt, inst.i.imm_zext32() << 16);
|
||||
const u32 value = inst.i.imm_zext32() << 16;
|
||||
WriteReg(inst.i.rt, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_LUI(inst.bits, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::andi:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) & inst.i.imm_zext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) & inst.i.imm_zext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ANDI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::ori:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) | inst.i.imm_zext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) | inst.i.imm_zext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ORI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::xori:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) ^ inst.i.imm_zext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) ^ inst.i.imm_zext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_XORI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -841,19 +871,31 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ANDI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::addiu:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) + inst.i.imm_sext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ADDIU(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::slti:
|
||||
{
|
||||
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.i.rs)) < static_cast<s32>(inst.i.imm_sext32()));
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLTI(inst.bits, result, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, result);
|
||||
}
|
||||
break;
|
||||
|
@ -861,6 +903,10 @@ void ExecuteInstruction()
|
|||
case InstructionOp::sltiu:
|
||||
{
|
||||
const u32 result = BoolToUInt32(ReadReg(inst.i.rs) < inst.i.imm_sext32());
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLTIU(inst.bits, result, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, result);
|
||||
}
|
||||
break;
|
||||
|
@ -876,7 +922,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteRegDelayed(inst.i.rt, sxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LBx(inst.bits, sxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -891,7 +937,7 @@ void ExecuteInstruction()
|
|||
const u32 sxvalue = SignExtend32(value);
|
||||
WriteRegDelayed(inst.i.rt, sxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LHx(inst.bits, sxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -905,7 +951,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteRegDelayed(inst.i.rt, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LW(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -920,7 +966,7 @@ void ExecuteInstruction()
|
|||
const u32 zxvalue = ZeroExtend32(value);
|
||||
WriteRegDelayed(inst.i.rt, zxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LBx(inst.bits, zxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -935,7 +981,7 @@ void ExecuteInstruction()
|
|||
const u32 zxvalue = ZeroExtend32(value);
|
||||
WriteRegDelayed(inst.i.rt, zxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LHx(inst.bits, zxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -966,7 +1012,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteRegDelayed(inst.i.rt, new_value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LW(inst.bits, new_value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -977,7 +1023,7 @@ void ExecuteInstruction()
|
|||
const u8 value = Truncate8(ReadReg(inst.i.rt));
|
||||
WriteMemoryByte(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SB(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -988,7 +1034,7 @@ void ExecuteInstruction()
|
|||
const u16 value = Truncate16(ReadReg(inst.i.rt));
|
||||
WriteMemoryHalfWord(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SH(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -999,7 +1045,7 @@ void ExecuteInstruction()
|
|||
const u32 value = ReadReg(inst.i.rt);
|
||||
WriteMemoryWord(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SW(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -1029,7 +1075,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteMemoryWord(aligned_addr, new_value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SW(inst.bits, new_value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -1114,7 +1160,58 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
ExecuteCop0Instruction();
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
{
|
||||
switch (inst.cop.CommonOp())
|
||||
{
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const std::optional<u32> value = ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()));
|
||||
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
PGXP::CPU_MFC0(inst.bits, value.value_or(0), ReadReg(inst.i.rs));
|
||||
|
||||
if (value)
|
||||
WriteRegDelayed(inst.r.rt, value.value());
|
||||
else
|
||||
RaiseException(Exception::RI);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
WriteCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()), ReadReg(inst.r.rt));
|
||||
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
{
|
||||
PGXP::CPU_MTC0(inst.bits, ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue())).value_or(0),
|
||||
ReadReg(inst.i.rs));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (inst.cop.Cop0Op())
|
||||
{
|
||||
case Cop0Instruction::rfe:
|
||||
{
|
||||
// restore mode
|
||||
g_state.cop0_regs.sr.mode_bits =
|
||||
(g_state.cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (g_state.cop0_regs.sr.mode_bits >> 2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1127,7 +1224,61 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
ExecuteCop2Instruction();
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
{
|
||||
// TODO: Combine with cop0.
|
||||
switch (inst.cop.CommonOp())
|
||||
{
|
||||
case CopCommonInstruction::cfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32);
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_CFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::ctcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_CTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()));
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_MFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()), value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_MTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::bcnc:
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GTE::ExecuteInstruction(inst.bits);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1147,7 +1298,7 @@ void ExecuteInstruction()
|
|||
|
||||
GTE::WriteRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())), value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LWC2(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -1165,12 +1316,12 @@ void ExecuteInstruction()
|
|||
const u32 value = GTE::ReadRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())));
|
||||
WriteMemoryWord(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SWC2(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
||||
// swc0/lwc0/cop1/cop3 are essentially no-ops
|
||||
// swc0/lwc0/cop1/cop3 are essentially no-ops
|
||||
case InstructionOp::cop1:
|
||||
case InstructionOp::cop3:
|
||||
case InstructionOp::lwc0:
|
||||
|
@ -1183,7 +1334,7 @@ void ExecuteInstruction()
|
|||
}
|
||||
break;
|
||||
|
||||
// everything else is reserved/invalid
|
||||
// everything else is reserved/invalid
|
||||
default:
|
||||
{
|
||||
RaiseException(Exception::RI);
|
||||
|
@ -1192,117 +1343,71 @@ void ExecuteInstruction()
|
|||
}
|
||||
}
|
||||
|
||||
void ExecuteCop0Instruction()
|
||||
template<PGXPMode pgxp_mode>
|
||||
static void ExecuteImpl()
|
||||
{
|
||||
const Instruction inst = g_state.current_instruction;
|
||||
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
g_state.frame_done = false;
|
||||
while (!g_state.frame_done)
|
||||
{
|
||||
switch (inst.cop.CommonOp())
|
||||
TimingEvents::UpdateCPUDowncount();
|
||||
|
||||
while (g_state.pending_ticks <= g_state.downcount)
|
||||
{
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const std::optional<u32> value = ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()));
|
||||
if (value)
|
||||
WriteRegDelayed(inst.r.rt, value.value());
|
||||
else
|
||||
RaiseException(Exception::RI);
|
||||
}
|
||||
break;
|
||||
if (HasPendingInterrupt())
|
||||
DispatchInterrupt();
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
WriteCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()), ReadReg(inst.r.rt));
|
||||
}
|
||||
break;
|
||||
g_state.pending_ticks++;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
// now executing the instruction we previously fetched
|
||||
g_state.current_instruction.bits = g_state.next_instruction.bits;
|
||||
g_state.current_instruction_pc = g_state.regs.pc;
|
||||
g_state.current_instruction_in_branch_delay_slot = g_state.next_instruction_is_branch_delay_slot;
|
||||
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
|
||||
g_state.next_instruction_is_branch_delay_slot = false;
|
||||
g_state.branch_was_taken = false;
|
||||
g_state.exception_raised = false;
|
||||
|
||||
// fetch the next instruction
|
||||
if (!FetchInstruction())
|
||||
continue;
|
||||
|
||||
#if 0 // GTE flag test debugging
|
||||
if (g_state.m_current_instruction_pc == 0x8002cdf4)
|
||||
{
|
||||
if (g_state.m_regs.v1 != g_state.m_regs.v0)
|
||||
printf("Got %08X Expected? %08X\n", g_state.m_regs.v1, g_state.m_regs.v0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction<pgxp_mode>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (inst.cop.Cop0Op())
|
||||
{
|
||||
case Cop0Instruction::rfe:
|
||||
{
|
||||
// restore mode
|
||||
g_state.cop0_regs.sr.mode_bits =
|
||||
(g_state.cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (g_state.cop0_regs.sr.mode_bits >> 2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
TimingEvents::RunEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteCop2Instruction()
|
||||
void Execute()
|
||||
{
|
||||
const Instruction inst = g_state.current_instruction;
|
||||
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
// TODO: Combine with cop0.
|
||||
switch (inst.cop.CommonOp())
|
||||
{
|
||||
case CopCommonInstruction::cfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32);
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_CFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::ctcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_CTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()));
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_MFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()), value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_MTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::bcnc:
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
if (g_settings.gpu_pgxp_cpu)
|
||||
ExecuteImpl<PGXPMode::CPU>();
|
||||
else
|
||||
ExecuteImpl<PGXPMode::Memory>();
|
||||
}
|
||||
else
|
||||
{
|
||||
GTE::ExecuteInstruction(inst.bits);
|
||||
ExecuteImpl<PGXPMode::Disabled>();
|
||||
}
|
||||
}
|
||||
|
||||
namespace CodeCache {
|
||||
|
||||
template<PGXPMode pgxp_mode>
|
||||
void InterpretCachedBlock(const CodeBlock& block)
|
||||
{
|
||||
// set up the state so we've already fetched the instruction
|
||||
|
@ -1327,7 +1432,7 @@ void InterpretCachedBlock(const CodeBlock& block)
|
|||
g_state.regs.npc += 4;
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction();
|
||||
ExecuteInstruction<pgxp_mode>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
|
@ -1340,6 +1445,10 @@ void InterpretCachedBlock(const CodeBlock& block)
|
|||
g_state.next_instruction_is_branch_delay_slot = false;
|
||||
}
|
||||
|
||||
template void InterpretCachedBlock<PGXPMode::Disabled>(const CodeBlock& block);
|
||||
template void InterpretCachedBlock<PGXPMode::Memory>(const CodeBlock& block);
|
||||
template void InterpretCachedBlock<PGXPMode::CPU>(const CodeBlock& block);
|
||||
|
||||
void InterpretUncachedBlock()
|
||||
{
|
||||
Panic("Fixme with regards to re-fetching PC");
|
||||
|
@ -1365,7 +1474,7 @@ void InterpretUncachedBlock()
|
|||
break;
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction();
|
||||
ExecuteInstruction<PGXPMode::Disabled>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
|
@ -1387,7 +1496,13 @@ namespace Recompiler::Thunks {
|
|||
|
||||
bool InterpretInstruction()
|
||||
{
|
||||
ExecuteInstruction();
|
||||
ExecuteInstruction<PGXPMode::Disabled>();
|
||||
return g_state.exception_raised;
|
||||
}
|
||||
|
||||
bool InterpretInstructionPGXP()
|
||||
{
|
||||
ExecuteInstruction<PGXPMode::Memory>();
|
||||
return g_state.exception_raised;
|
||||
}
|
||||
|
||||
|
|
|
@ -1014,12 +1014,14 @@ bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
|
|||
{
|
||||
// TODO: Use carry flag or something here too
|
||||
Value return_value = m_register_cache.AllocateScratch(RegSize_8);
|
||||
EmitFunctionCall(&return_value, &Thunks::InterpretInstruction);
|
||||
EmitFunctionCall(&return_value,
|
||||
g_settings.gpu_pgxp_enable ? &Thunks::InterpretInstructionPGXP : &Thunks::InterpretInstruction);
|
||||
EmitExceptionExitOnBool(return_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFunctionCall(nullptr, &Thunks::InterpretInstruction);
|
||||
EmitFunctionCall(nullptr,
|
||||
g_settings.gpu_pgxp_enable ? &Thunks::InterpretInstructionPGXP : &Thunks::InterpretInstruction);
|
||||
}
|
||||
|
||||
m_current_instruction_in_branch_delay_slot_dirty = cbi.is_branch_instruction;
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Recompiler::Thunks {
|
|||
// TODO: Abuse carry flag or something else for exception
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool InterpretInstruction();
|
||||
bool InterpretInstructionPGXP();
|
||||
|
||||
// Memory access functions for the JIT - MSB is set on exception.
|
||||
u64 ReadMemoryByte(u32 address);
|
||||
|
|
|
@ -135,7 +135,6 @@ enum class InstructionFunct : u8
|
|||
or_ = 37,
|
||||
xor_ = 38,
|
||||
nor = 39,
|
||||
sh = 41,
|
||||
slt = 42,
|
||||
sltu = 43
|
||||
};
|
||||
|
|
|
@ -375,6 +375,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetBoolValue("GPU", "PGXPCulling", true);
|
||||
si.SetBoolValue("GPU", "PGXPTextureCorrection", true);
|
||||
si.SetBoolValue("GPU", "PGXPVertexCache", false);
|
||||
si.SetBoolValue("GPU", "PGXPCPU", false);
|
||||
|
||||
si.SetStringValue("Display", "CropMode", Settings::GetDisplayCropModeName(Settings::DEFAULT_DISPLAY_CROP_MODE));
|
||||
si.SetStringValue("Display", "AspectRatio",
|
||||
|
@ -438,6 +439,25 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
void HostInterface::LoadSettings(SettingsInterface& si)
|
||||
{
|
||||
g_settings.Load(si);
|
||||
|
||||
FixIncompatibleSettings();
|
||||
}
|
||||
|
||||
void HostInterface::FixIncompatibleSettings()
|
||||
{
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
if (g_settings.gpu_renderer == GPURenderer::Software)
|
||||
{
|
||||
Log_WarningPrintf("PGXP enabled with software renderer, disabling");
|
||||
g_settings.gpu_pgxp_enable = false;
|
||||
}
|
||||
else if (g_settings.gpu_pgxp_cpu && g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler)
|
||||
{
|
||||
Log_WarningPrintf("Recompiler selected with PGXP CPU mode, falling back to cached interpreter");
|
||||
g_settings.cpu_execution_mode = CPUExecutionMode::CachedInterpreter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HostInterface::SaveSettings(SettingsInterface& si)
|
||||
|
|
|
@ -134,6 +134,9 @@ protected:
|
|||
/// Saves current settings variables to ini.
|
||||
virtual void SaveSettings(SettingsInterface& si);
|
||||
|
||||
/// Checks and fixes up any incompatible settings.
|
||||
virtual void FixIncompatibleSettings();
|
||||
|
||||
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
|
||||
virtual void CheckForSettingsChanges(const Settings& old_settings);
|
||||
|
||||
|
|
1039
src/core/pgxp.cpp
1039
src/core/pgxp.cpp
File diff suppressed because it is too large
Load Diff
|
@ -51,4 +51,56 @@ void CPU_SB(u32 instr, u8 rtVal, u32 addr);
|
|||
void CPU_SH(u32 instr, u16 rtVal, u32 addr);
|
||||
void CPU_SW(u32 instr, u32 rtVal, u32 addr);
|
||||
|
||||
// Arithmetic with immediate value
|
||||
void CPU_ADDI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_ADDIU(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_ANDI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_ORI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_XORI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_SLTI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_SLTIU(u32 instr, u32 rtVal, u32 rsVal);
|
||||
|
||||
// Load Upper
|
||||
void CPU_LUI(u32 instr, u32 rtVal);
|
||||
|
||||
// Register Arithmetic
|
||||
void CPU_ADD(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_ADDU(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SUB(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SUBU(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_AND(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_OR(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_XOR(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_NOR(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SLT(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SLTU(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
|
||||
// Register mult/div
|
||||
void CPU_MULT(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_MULTU(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_DIV(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_DIVU(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
|
||||
// Shift operations (sa)
|
||||
void CPU_SLL(u32 instr, u32 rdVal, u32 rtVal);
|
||||
void CPU_SRL(u32 instr, u32 rdVal, u32 rtVal);
|
||||
void CPU_SRA(u32 instr, u32 rdVal, u32 rtVal);
|
||||
|
||||
// Shift operations variable
|
||||
void CPU_SLLV(u32 instr, u32 rdVal, u32 rtVal, u32 rsVal);
|
||||
void CPU_SRLV(u32 instr, u32 rdVal, u32 rtVal, u32 rsVal);
|
||||
void CPU_SRAV(u32 instr, u32 rdVal, u32 rtVal, u32 rsVal);
|
||||
|
||||
// Move registers
|
||||
void CPU_MFHI(u32 instr, u32 rdVal, u32 hiVal);
|
||||
void CPU_MTHI(u32 instr, u32 hiVal, u32 rdVal);
|
||||
void CPU_MFLO(u32 instr, u32 rdVal, u32 loVal);
|
||||
void CPU_MTLO(u32 instr, u32 loVal, u32 rdVal);
|
||||
|
||||
// CP0 Data transfer tracking
|
||||
void CPU_MFC0(u32 instr, u32 rtVal, u32 rdVal);
|
||||
void CPU_MTC0(u32 instr, u32 rdVal, u32 rtVal);
|
||||
void CPU_CFC0(u32 instr, u32 rtVal, u32 rdVal);
|
||||
void CPU_CTC0(u32 instr, u32 rdVal, u32 rtVal);
|
||||
|
||||
} // namespace PGXP
|
|
@ -106,6 +106,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
gpu_pgxp_culling = si.GetBoolValue("GPU", "PGXPCulling", true);
|
||||
gpu_pgxp_texture_correction = si.GetBoolValue("GPU", "PGXPTextureCorrection", true);
|
||||
gpu_pgxp_vertex_cache = si.GetBoolValue("GPU", "PGXPVertexCache", false);
|
||||
gpu_pgxp_cpu = si.GetBoolValue("GPU", "PGXPCPU", false);
|
||||
|
||||
display_crop_mode =
|
||||
ParseDisplayCropMode(
|
||||
|
@ -215,6 +216,7 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("GPU", "PGXPCulling", gpu_pgxp_culling);
|
||||
si.SetBoolValue("GPU", "PGXPTextureCorrection", gpu_pgxp_texture_correction);
|
||||
si.SetBoolValue("GPU", "PGXPVertexCache", gpu_pgxp_vertex_cache);
|
||||
si.SetBoolValue("GPU", "PGXPCPU", gpu_pgxp_cpu);
|
||||
|
||||
si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
|
||||
si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio));
|
||||
|
|
|
@ -93,6 +93,7 @@ struct Settings
|
|||
bool gpu_pgxp_culling = true;
|
||||
bool gpu_pgxp_texture_correction = true;
|
||||
bool gpu_pgxp_vertex_cache = false;
|
||||
bool gpu_pgxp_cpu = false;
|
||||
DisplayCropMode display_crop_mode = DisplayCropMode::None;
|
||||
DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::R4_3;
|
||||
bool display_linear_filtering = true;
|
||||
|
@ -157,6 +158,11 @@ struct Settings
|
|||
ALWAYS_INLINE bool IsUsingRecompiler() const { return (cpu_execution_mode == CPUExecutionMode::Recompiler); }
|
||||
ALWAYS_INLINE bool IsUsingSoftwareRenderer() const { return (gpu_renderer == GPURenderer::Software); }
|
||||
|
||||
ALWAYS_INLINE PGXPMode GetPGXPMode()
|
||||
{
|
||||
return gpu_pgxp_enable ? (gpu_pgxp_cpu ? PGXPMode::CPU : PGXPMode::Memory) : PGXPMode::Disabled;
|
||||
}
|
||||
|
||||
bool HasAnyPerGameMemoryCards() const;
|
||||
|
||||
enum : u32
|
||||
|
|
|
@ -48,6 +48,13 @@ enum class CPUExecutionMode : u8
|
|||
Count
|
||||
};
|
||||
|
||||
enum class PGXPMode : u8
|
||||
{
|
||||
Disabled,
|
||||
Memory,
|
||||
CPU
|
||||
};
|
||||
|
||||
enum class GPURenderer : u8
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
|
|
@ -369,7 +369,7 @@ void LibretroHostInterface::OnSystemDestroyed()
|
|||
m_using_hardware_renderer = false;
|
||||
}
|
||||
|
||||
static std::array<retro_core_option_definition, 30> s_option_definitions = {{
|
||||
static std::array<retro_core_option_definition, 31> s_option_definitions = {{
|
||||
{"duckstation_Console.Region",
|
||||
"Console Region",
|
||||
"Determines which region/hardware to emulate. Auto-Detect will use the region of the disc inserted.",
|
||||
|
@ -500,6 +500,12 @@ static std::array<retro_core_option_definition, 30> s_option_definitions = {{
|
|||
"Uses screen coordinates as a fallback when tracking vertices through memory fails. May improve PGXP compatibility.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{"duckstation_GPU.PGXPCPU",
|
||||
"PGXP CPU Mode",
|
||||
"Tries to track vertex manipulation through the CPU. Some games require this option for PGXP to be effective. "
|
||||
"Very slow, and incompatible with the recompiler.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{"duckstation_Display.CropMode",
|
||||
"Crop Mode",
|
||||
"Changes how much of the image is cropped. Some games display garbage in the overscan area which is typically "
|
||||
|
@ -607,7 +613,7 @@ bool LibretroHostInterface::HasCoreVariablesChanged()
|
|||
void LibretroHostInterface::LoadSettings()
|
||||
{
|
||||
LibretroSettingsInterface si;
|
||||
g_settings.Load(si);
|
||||
HostInterface::LoadSettings(si);
|
||||
|
||||
// Assume BIOS files are located in system directory.
|
||||
const char* system_directory = nullptr;
|
||||
|
|
|
@ -44,6 +44,7 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pgxpTextureCorrection, "GPU",
|
||||
"PGXPTextureCorrection", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pgxpVertexCache, "GPU", "PGXPVertexCache", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pgxpCPUMode, "GPU", "PGXPCPUMode", false);
|
||||
|
||||
connect(m_ui.resolutionScale, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&GPUSettingsWidget::updateScaledDitheringEnabled);
|
||||
|
@ -142,6 +143,9 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
|
|||
dialog->registerWidgetHelp(m_ui.pgxpVertexCache, tr("Vertex Cache"), tr("Unchecked"),
|
||||
tr("Uses screen coordinates as a fallback when tracking vertices through memory fails. "
|
||||
"May improve PGXP compatibility."));
|
||||
dialog->registerWidgetHelp(m_ui.pgxpCPUMode, tr("CPU Mode"), tr("Unchecked"),
|
||||
tr("Tries to track vertex manipulation through the CPU. Some games require this option for PGXP to be effective. "
|
||||
"Very slow, and incompatible with the recompiler."));
|
||||
}
|
||||
|
||||
GPUSettingsWidget::~GPUSettingsWidget() = default;
|
||||
|
@ -255,4 +259,5 @@ void GPUSettingsWidget::updatePGXPSettingsEnabled()
|
|||
m_ui.pgxpCulling->setEnabled(enabled);
|
||||
m_ui.pgxpTextureCorrection->setEnabled(enabled);
|
||||
m_ui.pgxpVertexCache->setEnabled(enabled);
|
||||
m_ui.pgxpCPUMode->setEnabled(enabled);
|
||||
}
|
||||
|
|
|
@ -215,6 +215,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="pgxpCPUMode">
|
||||
<property name="text">
|
||||
<string>CPU Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -874,6 +874,8 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
&m_settings_copy.gpu_pgxp_texture_correction, m_settings_copy.gpu_pgxp_enable);
|
||||
settings_changed |= ImGui::MenuItem("PGXP Vertex Cache", nullptr, &m_settings_copy.gpu_pgxp_vertex_cache,
|
||||
m_settings_copy.gpu_pgxp_enable);
|
||||
settings_changed |=
|
||||
ImGui::MenuItem("PGXP CPU Instructions", nullptr, &m_settings_copy.gpu_pgxp_cpu, m_settings_copy.gpu_pgxp_enable);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
@ -1347,6 +1349,7 @@ void SDLHostInterface::DrawSettingsWindow()
|
|||
settings_changed |= ImGui::Checkbox("PGXP Culling", &m_settings_copy.gpu_pgxp_culling);
|
||||
settings_changed |= ImGui::Checkbox("PGXP Texture Correction", &m_settings_copy.gpu_pgxp_texture_correction);
|
||||
settings_changed |= ImGui::Checkbox("PGXP Vertex Cache", &m_settings_copy.gpu_pgxp_vertex_cache);
|
||||
settings_changed |= ImGui::Checkbox("PGXP CPU", &m_settings_copy.gpu_pgxp_cpu);
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
|
|
Loading…
Reference in New Issue