2019-09-09 07:01:26 +00:00
|
|
|
#include "cpu_core.h"
|
2020-07-02 14:57:22 +00:00
|
|
|
#include "common/align.h"
|
2020-08-01 03:34:07 +00:00
|
|
|
#include "common/file_system.h"
|
2020-01-10 03:31:12 +00:00
|
|
|
#include "common/log.h"
|
2019-09-09 07:01:26 +00:00
|
|
|
#include "common/state_wrapper.h"
|
2020-08-08 05:15:56 +00:00
|
|
|
#include "cpu_core_private.h"
|
2019-09-09 07:01:26 +00:00
|
|
|
#include "cpu_disasm.h"
|
2020-07-31 07:09:18 +00:00
|
|
|
#include "cpu_recompiler_thunks.h"
|
|
|
|
#include "gte.h"
|
2020-08-01 14:25:07 +00:00
|
|
|
#include "pgxp.h"
|
|
|
|
#include "settings.h"
|
2020-07-31 07:09:18 +00:00
|
|
|
#include "timing_event.h"
|
2019-09-09 07:01:26 +00:00
|
|
|
#include <cstdio>
|
|
|
|
Log_SetChannel(CPU::Core);
|
|
|
|
|
|
|
|
namespace CPU {
|
2019-11-03 14:55:07 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
static void SetPC(u32 new_pc);
|
|
|
|
static void UpdateLoadDelay();
|
|
|
|
static void Branch(u32 target);
|
|
|
|
static void FlushPipeline();
|
|
|
|
|
|
|
|
State g_state;
|
2019-10-03 06:45:54 +00:00
|
|
|
bool TRACE_EXECUTION = false;
|
2019-11-03 14:55:07 +00:00
|
|
|
bool LOG_EXECUTION = false;
|
|
|
|
|
|
|
|
void WriteToExecutionLog(const char* format, ...)
|
|
|
|
{
|
|
|
|
static std::FILE* log_file = nullptr;
|
|
|
|
static bool log_file_opened = false;
|
|
|
|
|
|
|
|
std::va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
|
|
|
if (!log_file_opened)
|
|
|
|
{
|
2020-08-01 03:34:07 +00:00
|
|
|
log_file = FileSystem::OpenCFile("cpu_log.txt", "wb");
|
2019-11-03 14:55:07 +00:00
|
|
|
log_file_opened = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (log_file)
|
|
|
|
{
|
|
|
|
std::vfprintf(log_file, format, ap);
|
|
|
|
std::fflush(log_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void Initialize()
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2019-09-14 03:31:44 +00:00
|
|
|
// From nocash spec.
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.PRID = UINT32_C(0x00000002);
|
2019-09-14 03:31:44 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
GTE::Initialize();
|
2020-08-01 14:25:07 +00:00
|
|
|
|
|
|
|
if (g_settings.gpu_pgxp_enable)
|
|
|
|
PGXP::Initialize();
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void Shutdown()
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
// GTE::Shutdown();
|
2020-09-02 12:44:52 +00:00
|
|
|
PGXP::Shutdown();
|
2020-07-31 07:09:18 +00:00
|
|
|
}
|
2019-09-17 04:25:25 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
g_state.pending_ticks = 0;
|
|
|
|
g_state.downcount = MAX_SLICE_SIZE;
|
2019-09-14 03:31:44 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs = {};
|
2019-09-14 03:31:44 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.BPC = 0;
|
|
|
|
g_state.cop0_regs.BDA = 0;
|
|
|
|
g_state.cop0_regs.TAR = 0;
|
|
|
|
g_state.cop0_regs.BadVaddr = 0;
|
|
|
|
g_state.cop0_regs.BDAM = 0;
|
|
|
|
g_state.cop0_regs.BPCM = 0;
|
|
|
|
g_state.cop0_regs.EPC = 0;
|
|
|
|
g_state.cop0_regs.sr.bits = 0;
|
|
|
|
g_state.cop0_regs.cause.bits = 0;
|
|
|
|
|
2020-08-29 12:07:33 +00:00
|
|
|
ClearICache();
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
GTE::Reset();
|
2019-09-17 13:35:17 +00:00
|
|
|
|
2019-09-14 03:31:44 +00:00
|
|
|
SetPC(RESET_VECTOR);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
|
|
|
if (g_settings.gpu_pgxp_enable)
|
|
|
|
PGXP::Initialize();
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
bool DoState(StateWrapper& sw)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
sw.Do(&g_state.pending_ticks);
|
|
|
|
sw.Do(&g_state.downcount);
|
|
|
|
sw.DoArray(g_state.regs.r, countof(g_state.regs.r));
|
|
|
|
sw.Do(&g_state.cop0_regs.BPC);
|
|
|
|
sw.Do(&g_state.cop0_regs.BDA);
|
|
|
|
sw.Do(&g_state.cop0_regs.TAR);
|
|
|
|
sw.Do(&g_state.cop0_regs.BadVaddr);
|
|
|
|
sw.Do(&g_state.cop0_regs.BDAM);
|
|
|
|
sw.Do(&g_state.cop0_regs.BPCM);
|
|
|
|
sw.Do(&g_state.cop0_regs.EPC);
|
|
|
|
sw.Do(&g_state.cop0_regs.PRID);
|
|
|
|
sw.Do(&g_state.cop0_regs.sr.bits);
|
|
|
|
sw.Do(&g_state.cop0_regs.cause.bits);
|
|
|
|
sw.Do(&g_state.cop0_regs.dcic.bits);
|
|
|
|
sw.Do(&g_state.next_instruction.bits);
|
|
|
|
sw.Do(&g_state.current_instruction.bits);
|
|
|
|
sw.Do(&g_state.current_instruction_pc);
|
|
|
|
sw.Do(&g_state.current_instruction_in_branch_delay_slot);
|
|
|
|
sw.Do(&g_state.current_instruction_was_branch_taken);
|
|
|
|
sw.Do(&g_state.next_instruction_is_branch_delay_slot);
|
|
|
|
sw.Do(&g_state.branch_was_taken);
|
|
|
|
sw.Do(&g_state.exception_raised);
|
|
|
|
sw.Do(&g_state.interrupt_delay);
|
|
|
|
sw.Do(&g_state.load_delay_reg);
|
|
|
|
sw.Do(&g_state.load_delay_value);
|
|
|
|
sw.Do(&g_state.next_load_delay_reg);
|
|
|
|
sw.Do(&g_state.next_load_delay_value);
|
2020-08-29 12:07:33 +00:00
|
|
|
sw.Do(&g_state.cache_control.bits);
|
2020-07-31 07:09:18 +00:00
|
|
|
sw.DoBytes(g_state.dcache.data(), g_state.dcache.size());
|
|
|
|
|
|
|
|
if (!GTE::DoState(sw))
|
2019-09-17 13:35:17 +00:00
|
|
|
return false;
|
|
|
|
|
2020-08-01 14:25:07 +00:00
|
|
|
if (sw.IsReading())
|
2020-08-29 12:07:33 +00:00
|
|
|
{
|
|
|
|
ClearICache();
|
2020-09-02 12:44:52 +00:00
|
|
|
if (g_settings.gpu_pgxp_enable)
|
|
|
|
PGXP::Initialize();
|
2020-08-29 12:07:33 +00:00
|
|
|
}
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2019-09-14 10:28:47 +00:00
|
|
|
return !sw.HasError();
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void SetPC(u32 new_pc)
|
2019-09-14 03:22:05 +00:00
|
|
|
{
|
2019-10-13 08:35:19 +00:00
|
|
|
DebugAssert(Common::IsAlignedPow2(new_pc, 4));
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.npc = new_pc;
|
2019-09-14 03:22:05 +00:00
|
|
|
FlushPipeline();
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void Branch(u32 target)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2019-10-13 08:35:19 +00:00
|
|
|
if (!Common::IsAlignedPow2(target, 4))
|
|
|
|
{
|
|
|
|
// The BadVaddr and EPC must be set to the fetching address, not the instruction about to execute.
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.BadVaddr = target;
|
2020-08-08 05:15:56 +00:00
|
|
|
RaiseException(Cop0Registers::CAUSE::MakeValueForException(Exception::AdEL, false, false, 0), target);
|
2019-10-13 08:35:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.npc = target;
|
|
|
|
g_state.branch_was_taken = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
ALWAYS_INLINE static u32 GetExceptionVector(Exception excode)
|
2019-09-15 02:43:54 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
const u32 base = g_state.cop0_regs.sr.BEV ? UINT32_C(0xbfc00100) : UINT32_C(0x80000000);
|
2019-09-20 10:14:00 +00:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
// apparently this isn't correct...
|
2019-09-15 02:43:54 +00:00
|
|
|
switch (excode)
|
|
|
|
{
|
|
|
|
case Exception::BP:
|
|
|
|
return base | UINT32_C(0x00000040);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return base | UINT32_C(0x00000080);
|
|
|
|
}
|
2019-09-20 10:14:00 +00:00
|
|
|
#else
|
|
|
|
return base | UINT32_C(0x00000080);
|
|
|
|
#endif
|
2019-09-15 02:43:54 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void RaiseException(Exception excode)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-08-08 05:15:56 +00:00
|
|
|
RaiseException(Cop0Registers::CAUSE::MakeValueForException(excode, g_state.current_instruction_in_branch_delay_slot,
|
|
|
|
g_state.current_instruction_was_branch_taken,
|
|
|
|
g_state.current_instruction.cop.cop_n),
|
|
|
|
g_state.current_instruction_pc);
|
2019-10-03 16:26:37 +00:00
|
|
|
}
|
|
|
|
|
2020-08-08 05:15:56 +00:00
|
|
|
void RaiseException(u32 CAUSE_bits, u32 EPC)
|
2019-10-03 16:26:37 +00:00
|
|
|
{
|
2020-08-08 05:15:56 +00:00
|
|
|
g_state.cop0_regs.EPC = EPC;
|
2020-08-14 13:52:34 +00:00
|
|
|
g_state.cop0_regs.cause.bits = (g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::EXCEPTION_WRITE_MASK) |
|
2020-08-08 05:15:56 +00:00
|
|
|
(CAUSE_bits & Cop0Registers::CAUSE::EXCEPTION_WRITE_MASK);
|
|
|
|
|
2020-05-15 15:02:18 +00:00
|
|
|
#ifdef _DEBUG
|
2020-08-08 05:15:56 +00:00
|
|
|
if (g_state.cop0_regs.cause.Excode != Exception::INT && g_state.cop0_regs.cause.Excode != Exception::Syscall &&
|
|
|
|
g_state.cop0_regs.cause.Excode != Exception::BP)
|
2019-10-04 10:23:47 +00:00
|
|
|
{
|
2020-08-08 05:15:56 +00:00
|
|
|
Log_DebugPrintf("Exception %u at 0x%08X (epc=0x%08X, BD=%s, CE=%u)",
|
|
|
|
static_cast<u8>(g_state.cop0_regs.cause.Excode.GetValue()), g_state.current_instruction_pc,
|
|
|
|
g_state.cop0_regs.EPC, g_state.cop0_regs.cause.BD ? "true" : "false",
|
|
|
|
g_state.cop0_regs.cause.CE.GetValue());
|
2020-07-31 07:09:18 +00:00
|
|
|
DisassembleAndPrint(g_state.current_instruction_pc, 4, 0);
|
2019-11-22 07:54:06 +00:00
|
|
|
if (LOG_EXECUTION)
|
|
|
|
{
|
2020-08-08 05:15:56 +00:00
|
|
|
CPU::WriteToExecutionLog("Exception %u at 0x%08X (epc=0x%08X, BD=%s, CE=%u)\n",
|
|
|
|
static_cast<u8>(g_state.cop0_regs.cause.Excode.GetValue()),
|
|
|
|
g_state.current_instruction_pc, g_state.cop0_regs.EPC,
|
|
|
|
g_state.cop0_regs.cause.BD ? "true" : "false", g_state.cop0_regs.cause.CE.GetValue());
|
2019-11-22 07:54:06 +00:00
|
|
|
}
|
2019-10-04 10:23:47 +00:00
|
|
|
}
|
2019-10-03 16:26:37 +00:00
|
|
|
#endif
|
|
|
|
|
2020-08-08 05:15:56 +00:00
|
|
|
if (g_state.cop0_regs.cause.BD)
|
2019-10-03 16:48:19 +00:00
|
|
|
{
|
|
|
|
// TAR is set to the address which was being fetched in this instruction, or the next instruction to execute if the
|
|
|
|
// exception hadn't occurred in the delay slot.
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.EPC -= UINT32_C(4);
|
|
|
|
g_state.cop0_regs.TAR = g_state.regs.pc;
|
2019-10-03 16:48:19 +00:00
|
|
|
}
|
|
|
|
|
2019-09-15 02:43:54 +00:00
|
|
|
// current -> previous, switch to kernel mode and disable interrupts
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.sr.mode_bits <<= 2;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
// flush the pipeline - we don't want to execute the previously fetched instruction
|
2020-08-08 05:15:56 +00:00
|
|
|
g_state.regs.npc = GetExceptionVector(g_state.cop0_regs.cause.Excode);
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.exception_raised = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
FlushPipeline();
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void SetExternalInterrupt(u8 bit)
|
2019-09-17 06:26:00 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.cause.Ip |= static_cast<u8>(1u << bit);
|
|
|
|
g_state.interrupt_delay = 1;
|
2019-09-17 06:26:00 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void ClearExternalInterrupt(u8 bit)
|
2019-09-17 06:26:00 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.cause.Ip &= static_cast<u8>(~(1u << bit));
|
2019-09-17 06:26:00 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE_RELEASE static void UpdateLoadDelay()
|
2020-07-31 07:09:18 +00:00
|
|
|
{
|
|
|
|
// the old value is needed in case the delay slot instruction overwrites the same register
|
|
|
|
if (g_state.load_delay_reg != Reg::count)
|
|
|
|
g_state.regs.r[static_cast<u8>(g_state.load_delay_reg)] = g_state.load_delay_value;
|
|
|
|
|
|
|
|
g_state.load_delay_reg = g_state.next_load_delay_reg;
|
|
|
|
g_state.load_delay_value = g_state.next_load_delay_value;
|
|
|
|
g_state.next_load_delay_reg = Reg::count;
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE_RELEASE static void FlushPipeline()
|
2019-09-15 02:16:51 +00:00
|
|
|
{
|
|
|
|
// loads are flushed
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_load_delay_reg = Reg::count;
|
|
|
|
if (g_state.load_delay_reg != Reg::count)
|
2019-11-19 14:15:14 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.r[static_cast<u8>(g_state.load_delay_reg)] = g_state.load_delay_value;
|
|
|
|
g_state.load_delay_reg = Reg::count;
|
2019-11-19 14:15:14 +00:00
|
|
|
}
|
2019-11-21 13:33:58 +00:00
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
// not in a branch delay slot
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.branch_was_taken = false;
|
|
|
|
g_state.next_instruction_is_branch_delay_slot = false;
|
|
|
|
g_state.current_instruction_pc = g_state.regs.pc;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
// prefetch the next instruction
|
|
|
|
FetchInstruction();
|
2019-11-19 10:30:04 +00:00
|
|
|
|
|
|
|
// and set it as the next one to execute
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.current_instruction.bits = g_state.next_instruction.bits;
|
|
|
|
g_state.current_instruction_in_branch_delay_slot = false;
|
|
|
|
g_state.current_instruction_was_branch_taken = false;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE static u32 ReadReg(Reg rs)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.regs.r[static_cast<u8>(rs)];
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE static void WriteReg(Reg rd, u32 value)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
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;
|
2019-11-19 14:15:14 +00:00
|
|
|
|
|
|
|
// prevent writes to $zero from going through - better than branching/cmov
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.zero = 0;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE_RELEASE static void WriteRegDelayed(Reg rd, u32 value)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
Assert(g_state.next_load_delay_reg == Reg::count);
|
2019-09-09 07:01:26 +00:00
|
|
|
if (rd == Reg::zero)
|
|
|
|
return;
|
|
|
|
|
2019-11-19 14:15:14 +00:00
|
|
|
// double load delays ignore the first value
|
2020-07-31 07:09:18 +00:00
|
|
|
if (g_state.load_delay_reg == rd)
|
|
|
|
g_state.load_delay_reg = Reg::count;
|
2019-11-19 14:15:14 +00:00
|
|
|
|
|
|
|
// save the old value, if something else overwrites this reg we want to preserve it
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_load_delay_reg = rd;
|
|
|
|
g_state.next_load_delay_value = value;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE_RELEASE static std::optional<u32> ReadCop0Reg(Cop0Reg reg)
|
2019-09-21 16:06:47 +00:00
|
|
|
{
|
|
|
|
switch (reg)
|
|
|
|
{
|
|
|
|
case Cop0Reg::BPC:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.BPC;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::BPCM:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.BPCM;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::BDA:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.BDA;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::BDAM:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.BDAM;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::DCIC:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.dcic.bits;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::JUMPDEST:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.TAR;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::BadVaddr:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.BadVaddr;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::SR:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.sr.bits;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::CAUSE:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.cause.bits;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::EPC:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.EPC;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
case Cop0Reg::PRID:
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.cop0_regs.PRID;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
|
|
|
default:
|
2019-10-04 03:41:12 +00:00
|
|
|
Log_DevPrintf("Unknown COP0 reg %u", ZeroExtend32(static_cast<u8>(reg)));
|
|
|
|
return std::nullopt;
|
2019-09-21 16:06:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value)
|
2019-09-21 16:06:47 +00:00
|
|
|
{
|
|
|
|
switch (reg)
|
|
|
|
{
|
|
|
|
case Cop0Reg::BPC:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.BPC = value;
|
2019-09-21 16:06:47 +00:00
|
|
|
Log_WarningPrintf("COP0 BPC <- %08X", value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Cop0Reg::BPCM:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.BPCM = value;
|
2019-09-21 16:06:47 +00:00
|
|
|
Log_WarningPrintf("COP0 BPCM <- %08X", value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Cop0Reg::BDA:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.BDA = value;
|
2019-09-21 16:06:47 +00:00
|
|
|
Log_WarningPrintf("COP0 BDA <- %08X", value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Cop0Reg::BDAM:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.BDAM = value;
|
2019-09-21 16:06:47 +00:00
|
|
|
Log_WarningPrintf("COP0 BDAM <- %08X", value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Cop0Reg::JUMPDEST:
|
|
|
|
{
|
|
|
|
Log_WarningPrintf("Ignoring write to Cop0 JUMPDEST");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Cop0Reg::DCIC:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.dcic.bits =
|
|
|
|
(g_state.cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK);
|
|
|
|
Log_WarningPrintf("COP0 DCIC <- %08X (now %08X)", value, g_state.cop0_regs.dcic.bits);
|
2019-09-21 16:06:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Cop0Reg::SR:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.sr.bits =
|
|
|
|
(g_state.cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) | (value & Cop0Registers::SR::WRITE_MASK);
|
|
|
|
Log_DebugPrintf("COP0 SR <- %08X (now %08X)", value, g_state.cop0_regs.sr.bits);
|
2019-09-21 16:06:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Cop0Reg::CAUSE:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.cop0_regs.cause.bits =
|
|
|
|
(g_state.cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) | (value & Cop0Registers::CAUSE::WRITE_MASK);
|
|
|
|
Log_DebugPrintf("COP0 CAUSE <- %08X (now %08X)", value, g_state.cop0_regs.cause.bits);
|
2019-09-21 16:06:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2019-10-04 03:41:12 +00:00
|
|
|
Log_DevPrintf("Unknown COP0 reg %u", ZeroExtend32(static_cast<u8>(reg)));
|
2019-09-21 16:06:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
static void PrintInstruction(u32 bits, u32 pc, Registers* regs)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
TinyString instr;
|
2020-07-31 07:09:18 +00:00
|
|
|
DisassembleInstruction(&instr, pc, bits, regs);
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
std::printf("%08x: %08x %s\n", pc, bits, instr.GetCharArray());
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
static void LogInstruction(u32 bits, u32 pc, Registers* regs)
|
2019-11-03 14:55:07 +00:00
|
|
|
{
|
|
|
|
TinyString instr;
|
2020-07-31 07:09:18 +00:00
|
|
|
DisassembleInstruction(&instr, pc, bits, regs);
|
2019-11-03 14:55:07 +00:00
|
|
|
|
|
|
|
WriteToExecutionLog("%08x: %08x %s\n", pc, bits, instr.GetCharArray());
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE static constexpr bool AddOverflow(u32 old_value, u32 add_value, u32 new_value)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
return (((new_value ^ old_value) & (new_value ^ add_value)) & UINT32_C(0x80000000)) != 0;
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
ALWAYS_INLINE static constexpr bool SubOverflow(u32 old_value, u32 sub_value, u32 new_value)
|
2019-09-14 03:39:36 +00:00
|
|
|
{
|
|
|
|
return (((new_value ^ old_value) & (old_value ^ sub_value)) & UINT32_C(0x80000000)) != 0;
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void DisassembleAndPrint(u32 addr)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2019-09-24 14:36:24 +00:00
|
|
|
u32 bits = 0;
|
2020-07-31 07:09:18 +00:00
|
|
|
SafeReadMemoryWord(addr, &bits);
|
|
|
|
PrintInstruction(bits, addr, &g_state.regs);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instructions_after /* = 0 */)
|
2019-10-03 16:26:37 +00:00
|
|
|
{
|
|
|
|
u32 disasm_addr = addr - (instructions_before * sizeof(u32));
|
|
|
|
for (u32 i = 0; i < instructions_before; i++)
|
|
|
|
{
|
|
|
|
DisassembleAndPrint(disasm_addr);
|
|
|
|
disasm_addr += sizeof(u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::printf("----> ");
|
|
|
|
|
|
|
|
// <= to include the instruction itself
|
|
|
|
for (u32 i = 0; i <= instructions_after; i++)
|
|
|
|
{
|
|
|
|
DisassembleAndPrint(disasm_addr);
|
|
|
|
disasm_addr += sizeof(u32);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
template<PGXPMode pgxp_mode>
|
|
|
|
ALWAYS_INLINE_RELEASE static void ExecuteInstruction()
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-09-19 15:36:44 +00:00
|
|
|
restart_instruction:
|
2020-07-31 07:09:18 +00:00
|
|
|
const Instruction inst = g_state.current_instruction;
|
2019-10-03 16:26:37 +00:00
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
#if 0
|
2020-07-31 07:09:18 +00:00
|
|
|
if (g_state.m_current_instruction_pc == 0x80010000)
|
2019-09-14 03:22:05 +00:00
|
|
|
{
|
2019-11-19 10:30:04 +00:00
|
|
|
LOG_EXECUTION = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
__debugbreak();
|
2019-09-14 03:22:05 +00:00
|
|
|
}
|
2019-09-09 07:01:26 +00:00
|
|
|
#endif
|
|
|
|
|
2019-11-23 09:49:43 +00:00
|
|
|
#ifdef _DEBUG
|
2019-10-03 06:45:54 +00:00
|
|
|
if (TRACE_EXECUTION)
|
2020-07-31 07:09:18 +00:00
|
|
|
PrintInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
|
2019-11-03 14:55:07 +00:00
|
|
|
if (LOG_EXECUTION)
|
2020-07-31 07:09:18 +00:00
|
|
|
LogInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
|
2019-11-23 09:49:43 +00:00
|
|
|
#endif
|
2019-09-14 03:22:05 +00:00
|
|
|
|
2020-08-19 13:38:05 +00:00
|
|
|
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
|
|
|
|
if (inst.bits == 0)
|
|
|
|
return;
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
switch (inst.op)
|
|
|
|
{
|
|
|
|
case InstructionOp::funct:
|
|
|
|
{
|
|
|
|
switch (inst.r.funct)
|
|
|
|
{
|
|
|
|
case InstructionFunct::sll:
|
|
|
|
{
|
|
|
|
const u32 new_value = ReadReg(inst.r.rt) << inst.r.shamt;
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SLL(inst.bits, new_value, ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::srl:
|
|
|
|
{
|
|
|
|
const u32 new_value = ReadReg(inst.r.rt) >> inst.r.shamt;
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SRL(inst.bits, new_value, ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::sra:
|
|
|
|
{
|
|
|
|
const u32 new_value = static_cast<u32>(static_cast<s32>(ReadReg(inst.r.rt)) >> inst.r.shamt);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SRA(inst.bits, new_value, ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::sllv:
|
|
|
|
{
|
|
|
|
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
|
|
|
const u32 new_value = ReadReg(inst.r.rt) << shift_amount;
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SLLV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::srlv:
|
|
|
|
{
|
|
|
|
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
|
|
|
const u32 new_value = ReadReg(inst.r.rt) >> shift_amount;
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SRLV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::srav:
|
|
|
|
{
|
|
|
|
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);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SRAV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::and_:
|
|
|
|
{
|
|
|
|
const u32 new_value = ReadReg(inst.r.rs) & ReadReg(inst.r.rt);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
2020-08-19 15:05:03 +00:00
|
|
|
PGXP::CPU_AND_(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::or_:
|
|
|
|
{
|
|
|
|
const u32 new_value = ReadReg(inst.r.rs) | ReadReg(inst.r.rt);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
2020-08-19 15:05:03 +00:00
|
|
|
PGXP::CPU_OR_(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::xor_:
|
|
|
|
{
|
|
|
|
const u32 new_value = ReadReg(inst.r.rs) ^ ReadReg(inst.r.rt);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
2020-08-19 15:05:03 +00:00
|
|
|
PGXP::CPU_XOR_(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::nor:
|
|
|
|
{
|
|
|
|
const u32 new_value = ~(ReadReg(inst.r.rs) | ReadReg(inst.r.rt));
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_NOR(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::add:
|
|
|
|
{
|
|
|
|
const u32 old_value = ReadReg(inst.r.rs);
|
|
|
|
const u32 add_value = ReadReg(inst.r.rt);
|
|
|
|
const u32 new_value = old_value + add_value;
|
|
|
|
if (AddOverflow(old_value, add_value, new_value))
|
2019-09-14 03:33:29 +00:00
|
|
|
{
|
2019-09-14 04:29:23 +00:00
|
|
|
RaiseException(Exception::Ov);
|
2019-09-14 03:33:29 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode == PGXPMode::CPU)
|
|
|
|
PGXP::CPU_ADD(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::addu:
|
|
|
|
{
|
|
|
|
const u32 new_value = ReadReg(inst.r.rs) + ReadReg(inst.r.rt);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_ADDU(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
2019-09-14 03:39:36 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::sub:
|
|
|
|
{
|
|
|
|
const u32 old_value = ReadReg(inst.r.rs);
|
|
|
|
const u32 sub_value = ReadReg(inst.r.rt);
|
|
|
|
const u32 new_value = old_value - sub_value;
|
|
|
|
if (SubOverflow(old_value, sub_value, new_value))
|
|
|
|
{
|
2019-09-14 04:29:23 +00:00
|
|
|
RaiseException(Exception::Ov);
|
2019-09-14 03:39:36 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SUB(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-14 03:39:36 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::subu:
|
|
|
|
{
|
|
|
|
const u32 new_value = ReadReg(inst.r.rs) - ReadReg(inst.r.rt);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SUBU(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::slt:
|
|
|
|
{
|
|
|
|
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.r.rs)) < static_cast<s32>(ReadReg(inst.r.rt)));
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SLT(inst.bits, result, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, result);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::sltu:
|
|
|
|
{
|
|
|
|
const u32 result = BoolToUInt32(ReadReg(inst.r.rs) < ReadReg(inst.r.rt));
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SLTU(inst.bits, result, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.r.rd, result);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::mfhi:
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_MFHI(inst.bits, ReadReg(inst.r.rd), g_state.regs.hi);
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
WriteReg(inst.r.rd, g_state.regs.hi);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::mthi:
|
|
|
|
{
|
|
|
|
const u32 value = ReadReg(inst.r.rs);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_MTHI(inst.bits, g_state.regs.hi, value);
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.hi = value;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::mflo:
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_MFLO(inst.bits, ReadReg(inst.r.rd), g_state.regs.lo);
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
WriteReg(inst.r.rd, g_state.regs.lo);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::mtlo:
|
|
|
|
{
|
|
|
|
const u32 value = ReadReg(inst.r.rs);
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode == PGXPMode::CPU)
|
|
|
|
PGXP::CPU_MTLO(inst.bits, g_state.regs.lo, value);
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.lo = value;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::mult:
|
|
|
|
{
|
|
|
|
const u32 lhs = ReadReg(inst.r.rs);
|
|
|
|
const u32 rhs = ReadReg(inst.r.rt);
|
|
|
|
const u64 result =
|
|
|
|
static_cast<u64>(static_cast<s64>(SignExtend64(lhs)) * static_cast<s64>(SignExtend64(rhs)));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.hi = Truncate32(result >> 32);
|
|
|
|
g_state.regs.lo = Truncate32(result);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_MULT(inst.bits, g_state.regs.hi, g_state.regs.lo, lhs, rhs);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::multu:
|
|
|
|
{
|
|
|
|
const u32 lhs = ReadReg(inst.r.rs);
|
|
|
|
const u32 rhs = ReadReg(inst.r.rt);
|
|
|
|
const u64 result = ZeroExtend64(lhs) * ZeroExtend64(rhs);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_MULTU(inst.bits, g_state.regs.hi, g_state.regs.lo, lhs, rhs);
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.hi = Truncate32(result >> 32);
|
|
|
|
g_state.regs.lo = Truncate32(result);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::div:
|
|
|
|
{
|
|
|
|
const s32 num = static_cast<s32>(ReadReg(inst.r.rs));
|
|
|
|
const s32 denom = static_cast<s32>(ReadReg(inst.r.rt));
|
|
|
|
|
|
|
|
if (denom == 0)
|
|
|
|
{
|
|
|
|
// divide by zero
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1);
|
|
|
|
g_state.regs.hi = static_cast<u32>(num);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
else if (static_cast<u32>(num) == UINT32_C(0x80000000) && denom == -1)
|
|
|
|
{
|
|
|
|
// unrepresentable
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.lo = UINT32_C(0x80000000);
|
|
|
|
g_state.regs.hi = 0;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.lo = static_cast<u32>(num / denom);
|
|
|
|
g_state.regs.hi = static_cast<u32>(num % denom);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_DIV(inst.bits, g_state.regs.hi, g_state.regs.lo, num, denom);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::divu:
|
|
|
|
{
|
|
|
|
const u32 num = ReadReg(inst.r.rs);
|
|
|
|
const u32 denom = ReadReg(inst.r.rt);
|
|
|
|
|
|
|
|
if (denom == 0)
|
|
|
|
{
|
|
|
|
// divide by zero
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.lo = UINT32_C(0xFFFFFFFF);
|
|
|
|
g_state.regs.hi = static_cast<u32>(num);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.regs.lo = num / denom;
|
|
|
|
g_state.regs.hi = num % denom;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_DIVU(inst.bits, g_state.regs.hi, g_state.regs.lo, num, denom);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::jr:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
const u32 target = ReadReg(inst.r.rs);
|
|
|
|
Branch(target);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::jalr:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
const u32 target = ReadReg(inst.r.rs);
|
2020-07-31 07:09:18 +00:00
|
|
|
WriteReg(inst.r.rd, g_state.regs.npc);
|
2019-09-09 07:01:26 +00:00
|
|
|
Branch(target);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionFunct::syscall:
|
|
|
|
{
|
2019-09-14 04:29:23 +00:00
|
|
|
RaiseException(Exception::Syscall);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-14 04:41:41 +00:00
|
|
|
case InstructionFunct::break_:
|
|
|
|
{
|
|
|
|
RaiseException(Exception::BP);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
default:
|
2019-10-04 03:41:12 +00:00
|
|
|
{
|
|
|
|
RaiseException(Exception::RI);
|
2019-09-09 07:01:26 +00:00
|
|
|
break;
|
2019-10-04 03:41:12 +00:00
|
|
|
}
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::lui:
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
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);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::andi:
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
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);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::ori:
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
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);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-14 03:31:44 +00:00
|
|
|
case InstructionOp::xori:
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
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);
|
2019-09-14 03:31:44 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
case InstructionOp::addi:
|
|
|
|
{
|
|
|
|
const u32 old_value = ReadReg(inst.i.rs);
|
|
|
|
const u32 add_value = inst.i.imm_sext32();
|
|
|
|
const u32 new_value = old_value + add_value;
|
|
|
|
if (AddOverflow(old_value, add_value, new_value))
|
2019-09-14 03:33:29 +00:00
|
|
|
{
|
2019-09-14 04:29:23 +00:00
|
|
|
RaiseException(Exception::Ov);
|
2019-09-14 03:33:29 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_ANDI(inst.bits, new_value, ReadReg(inst.i.rs));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.i.rt, new_value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::addiu:
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
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);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::slti:
|
|
|
|
{
|
|
|
|
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.i.rs)) < static_cast<s32>(inst.i.imm_sext32()));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SLTI(inst.bits, result, ReadReg(inst.i.rs));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.i.rt, result);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::sltiu:
|
|
|
|
{
|
|
|
|
const u32 result = BoolToUInt32(ReadReg(inst.i.rs) < inst.i.imm_sext32());
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
|
|
|
PGXP::CPU_SLTIU(inst.bits, result, ReadReg(inst.i.rs));
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteReg(inst.i.rt, result);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::lb:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
2019-09-14 04:29:23 +00:00
|
|
|
u8 value;
|
|
|
|
if (!ReadMemoryByte(addr, &value))
|
|
|
|
return;
|
|
|
|
|
2020-08-01 14:25:07 +00:00
|
|
|
const u32 sxvalue = SignExtend32(value);
|
|
|
|
|
|
|
|
WriteRegDelayed(inst.i.rt, sxvalue);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_LBx(inst.bits, sxvalue, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::lh:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
2019-09-14 04:29:23 +00:00
|
|
|
u16 value;
|
|
|
|
if (!ReadMemoryHalfWord(addr, &value))
|
|
|
|
return;
|
|
|
|
|
2020-08-01 14:25:07 +00:00
|
|
|
const u32 sxvalue = SignExtend32(value);
|
|
|
|
WriteRegDelayed(inst.i.rt, sxvalue);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_LHx(inst.bits, sxvalue, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::lw:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
2019-09-14 04:29:23 +00:00
|
|
|
u32 value;
|
|
|
|
if (!ReadMemoryWord(addr, &value))
|
|
|
|
return;
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
WriteRegDelayed(inst.i.rt, value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_LW(inst.bits, value, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::lbu:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
2019-09-14 04:29:23 +00:00
|
|
|
u8 value;
|
|
|
|
if (!ReadMemoryByte(addr, &value))
|
|
|
|
return;
|
|
|
|
|
2020-08-01 14:25:07 +00:00
|
|
|
const u32 zxvalue = ZeroExtend32(value);
|
|
|
|
WriteRegDelayed(inst.i.rt, zxvalue);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_LBx(inst.bits, zxvalue, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::lhu:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
2019-09-14 04:29:23 +00:00
|
|
|
u16 value;
|
|
|
|
if (!ReadMemoryHalfWord(addr, &value))
|
|
|
|
return;
|
|
|
|
|
2020-08-01 14:25:07 +00:00
|
|
|
const u32 zxvalue = ZeroExtend32(value);
|
|
|
|
WriteRegDelayed(inst.i.rt, zxvalue);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_LHx(inst.bits, zxvalue, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-11 04:01:19 +00:00
|
|
|
case InstructionOp::lwl:
|
|
|
|
case InstructionOp::lwr:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
|
|
|
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
|
2019-09-14 04:29:23 +00:00
|
|
|
u32 aligned_value;
|
|
|
|
if (!ReadMemoryWord(aligned_addr, &aligned_value))
|
|
|
|
return;
|
2019-09-11 04:01:19 +00:00
|
|
|
|
2019-11-19 14:15:14 +00:00
|
|
|
// Bypasses load delay. No need to check the old value since this is the delay slot or it's not relevant.
|
2020-07-31 07:09:18 +00:00
|
|
|
const u32 existing_value = (inst.i.rt == g_state.load_delay_reg) ? g_state.load_delay_value : ReadReg(inst.i.rt);
|
2019-09-11 04:01:19 +00:00
|
|
|
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
|
|
|
|
u32 new_value;
|
|
|
|
if (inst.op == InstructionOp::lwl)
|
|
|
|
{
|
|
|
|
const u32 mask = UINT32_C(0x00FFFFFF) >> shift;
|
|
|
|
new_value = (existing_value & mask) | (aligned_value << (24 - shift));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const u32 mask = UINT32_C(0xFFFFFF00) << (24 - shift);
|
|
|
|
new_value = (existing_value & mask) | (aligned_value >> shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteRegDelayed(inst.i.rt, new_value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_LW(inst.bits, new_value, addr);
|
2019-09-11 04:01:19 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
case InstructionOp::sb:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
|
|
|
const u8 value = Truncate8(ReadReg(inst.i.rt));
|
|
|
|
WriteMemoryByte(addr, value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_SB(inst.bits, value, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::sh:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
|
|
|
const u16 value = Truncate16(ReadReg(inst.i.rt));
|
|
|
|
WriteMemoryHalfWord(addr, value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_SH(inst.bits, value, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::sw:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
|
|
|
const u32 value = ReadReg(inst.i.rt);
|
|
|
|
WriteMemoryWord(addr, value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_SW(inst.bits, value, addr);
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-11 04:01:19 +00:00
|
|
|
case InstructionOp::swl:
|
|
|
|
case InstructionOp::swr:
|
|
|
|
{
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
|
|
|
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
|
|
|
|
const u32 reg_value = ReadReg(inst.i.rt);
|
|
|
|
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
|
2019-09-14 04:29:23 +00:00
|
|
|
u32 mem_value;
|
|
|
|
if (!ReadMemoryWord(aligned_addr, &mem_value))
|
|
|
|
return;
|
2019-09-11 04:01:19 +00:00
|
|
|
|
|
|
|
u32 new_value;
|
|
|
|
if (inst.op == InstructionOp::swl)
|
|
|
|
{
|
|
|
|
const u32 mem_mask = UINT32_C(0xFFFFFF00) << shift;
|
|
|
|
new_value = (mem_value & mem_mask) | (reg_value >> (24 - shift));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const u32 mem_mask = UINT32_C(0x00FFFFFF) >> (24 - shift);
|
|
|
|
new_value = (mem_value & mem_mask) | (reg_value << shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteMemoryWord(aligned_addr, new_value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_SW(inst.bits, new_value, addr);
|
2019-09-11 04:01:19 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
case InstructionOp::j:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
|
|
|
Branch((g_state.regs.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::jal:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
WriteReg(Reg::ra, g_state.regs.npc);
|
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
|
|
|
Branch((g_state.regs.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::beq:
|
|
|
|
{
|
2019-10-03 16:48:19 +00:00
|
|
|
// We're still flagged as a branch delay slot even if the branch isn't taken.
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
const bool branch = (ReadReg(inst.i.rs) == ReadReg(inst.i.rt));
|
|
|
|
if (branch)
|
2020-07-31 07:09:18 +00:00
|
|
|
Branch(g_state.regs.pc + (inst.i.imm_sext32() << 2));
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::bne:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
const bool branch = (ReadReg(inst.i.rs) != ReadReg(inst.i.rt));
|
|
|
|
if (branch)
|
2020-07-31 07:09:18 +00:00
|
|
|
Branch(g_state.regs.pc + (inst.i.imm_sext32() << 2));
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::bgtz:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) > 0);
|
|
|
|
if (branch)
|
2020-07-31 07:09:18 +00:00
|
|
|
Branch(g_state.regs.pc + (inst.i.imm_sext32() << 2));
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::blez:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) <= 0);
|
|
|
|
if (branch)
|
2020-07-31 07:09:18 +00:00
|
|
|
Branch(g_state.regs.pc + (inst.i.imm_sext32() << 2));
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::b:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
g_state.next_instruction_is_branch_delay_slot = true;
|
2019-09-09 07:01:26 +00:00
|
|
|
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
|
|
|
|
|
|
|
|
// bgez is the inverse of bltz, so simply do ltz and xor the result
|
|
|
|
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
|
|
|
|
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) < 0) ^ bgez;
|
|
|
|
|
2019-09-14 15:02:35 +00:00
|
|
|
// register is still linked even if the branch isn't taken
|
|
|
|
const bool link = (rt & u8(0x1E)) == u8(0x10);
|
|
|
|
if (link)
|
2020-07-31 07:09:18 +00:00
|
|
|
WriteReg(Reg::ra, g_state.regs.npc);
|
2019-09-14 15:02:35 +00:00
|
|
|
|
|
|
|
if (branch)
|
2020-07-31 07:09:18 +00:00
|
|
|
Branch(g_state.regs.pc + (inst.i.imm_sext32() << 2));
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::cop0:
|
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
if (InUserMode() && !g_state.cop0_regs.sr.CU0)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
Log_WarningPrintf("Coprocessor 0 not present in user mode");
|
2019-10-03 16:26:37 +00:00
|
|
|
RaiseException(Exception::CpU);
|
2019-09-09 07:01:26 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::cop2:
|
|
|
|
{
|
2020-08-07 07:05:01 +00:00
|
|
|
if (!g_state.cop0_regs.sr.CE2)
|
2019-09-17 13:35:17 +00:00
|
|
|
{
|
2020-08-07 07:05:01 +00:00
|
|
|
Log_WarningPrintf("Coprocessor 2 not enabled");
|
2019-10-03 16:26:37 +00:00
|
|
|
RaiseException(Exception::CpU);
|
2019-09-17 13:35:17 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-09-21 16:06:47 +00:00
|
|
|
case InstructionOp::lwc2:
|
|
|
|
{
|
2020-08-07 07:05:01 +00:00
|
|
|
if (!g_state.cop0_regs.sr.CE2)
|
2019-09-21 16:06:47 +00:00
|
|
|
{
|
2020-08-07 07:05:01 +00:00
|
|
|
Log_WarningPrintf("Coprocessor 2 not enabled");
|
2019-10-03 16:26:37 +00:00
|
|
|
RaiseException(Exception::CpU);
|
2019-09-21 16:06:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
|
|
|
u32 value;
|
|
|
|
if (!ReadMemoryWord(addr, &value))
|
|
|
|
return;
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
GTE::WriteRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())), value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_LWC2(inst.bits, value, addr);
|
2019-09-21 16:06:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case InstructionOp::swc2:
|
|
|
|
{
|
2020-08-07 07:05:01 +00:00
|
|
|
if (!g_state.cop0_regs.sr.CE2)
|
2019-09-21 16:06:47 +00:00
|
|
|
{
|
2020-08-07 07:05:01 +00:00
|
|
|
Log_WarningPrintf("Coprocessor 2 not enabled");
|
2019-10-03 16:26:37 +00:00
|
|
|
RaiseException(Exception::CpU);
|
2019-09-21 16:06:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
2020-07-31 07:09:18 +00:00
|
|
|
const u32 value = GTE::ReadRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())));
|
2019-09-21 16:06:47 +00:00
|
|
|
WriteMemoryWord(addr, value);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP::CPU_SWC2(inst.bits, value, addr);
|
2019-09-21 16:06:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// swc0/lwc0/cop1/cop3 are essentially no-ops
|
2019-09-17 13:35:17 +00:00
|
|
|
case InstructionOp::cop1:
|
2019-09-09 07:01:26 +00:00
|
|
|
case InstructionOp::cop3:
|
2019-10-04 03:41:12 +00:00
|
|
|
case InstructionOp::lwc0:
|
2019-09-21 16:06:47 +00:00
|
|
|
case InstructionOp::lwc1:
|
|
|
|
case InstructionOp::lwc3:
|
2019-10-04 03:41:12 +00:00
|
|
|
case InstructionOp::swc0:
|
|
|
|
case InstructionOp::swc1:
|
2019-09-21 16:06:47 +00:00
|
|
|
case InstructionOp::swc3:
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// everything else is reserved/invalid
|
2019-09-09 07:01:26 +00:00
|
|
|
default:
|
2019-10-04 03:41:12 +00:00
|
|
|
{
|
2020-09-19 15:36:44 +00:00
|
|
|
u32 ram_value;
|
|
|
|
if (SafeReadInstruction(g_state.current_instruction_pc, &ram_value) &&
|
|
|
|
ram_value != g_state.current_instruction.bits)
|
|
|
|
{
|
|
|
|
Log_ErrorPrintf("Stale icache at 0x%08X - ICache: %08X RAM: %08X", g_state.current_instruction_pc,
|
|
|
|
g_state.current_instruction.bits, ram_value);
|
|
|
|
g_state.current_instruction.bits = ram_value;
|
|
|
|
goto restart_instruction;
|
|
|
|
}
|
|
|
|
|
2019-10-04 03:41:12 +00:00
|
|
|
RaiseException(Exception::RI);
|
|
|
|
}
|
|
|
|
break;
|
2019-09-09 07:01:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
template<PGXPMode pgxp_mode>
|
|
|
|
static void ExecuteImpl()
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
g_state.frame_done = false;
|
|
|
|
while (!g_state.frame_done)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
TimingEvents::UpdateCPUDowncount();
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-08-22 15:29:07 +00:00
|
|
|
while (g_state.pending_ticks < g_state.downcount)
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
if (HasPendingInterrupt())
|
|
|
|
DispatchInterrupt();
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
g_state.pending_ticks++;
|
2019-10-03 16:26:37 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// 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;
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// fetch the next instruction
|
|
|
|
if (!FetchInstruction())
|
|
|
|
continue;
|
2019-09-21 16:06:47 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
#if 0 // GTE flag test debugging
|
|
|
|
if (g_state.m_current_instruction_pc == 0x8002cdf4)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
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);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
#endif
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// execute the instruction we previously fetched
|
|
|
|
ExecuteInstruction<pgxp_mode>();
|
2019-09-21 16:06:47 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// next load delay
|
|
|
|
UpdateLoadDelay();
|
|
|
|
}
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
TimingEvents::RunEvents();
|
|
|
|
}
|
|
|
|
}
|
2019-09-17 13:35:17 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
void Execute()
|
|
|
|
{
|
|
|
|
if (g_settings.gpu_pgxp_enable)
|
|
|
|
{
|
|
|
|
if (g_settings.gpu_pgxp_cpu)
|
|
|
|
ExecuteImpl<PGXPMode::CPU>();
|
|
|
|
else
|
|
|
|
ExecuteImpl<PGXPMode::Memory>();
|
2019-09-17 13:35:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
ExecuteImpl<PGXPMode::Disabled>();
|
2020-07-31 07:09:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace CodeCache {
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
template<PGXPMode pgxp_mode>
|
2020-07-31 07:09:18 +00:00
|
|
|
void InterpretCachedBlock(const CodeBlock& block)
|
|
|
|
{
|
|
|
|
// set up the state so we've already fetched the instruction
|
|
|
|
DebugAssert(g_state.regs.pc == block.GetPC());
|
|
|
|
g_state.regs.npc = block.GetPC() + 4;
|
|
|
|
|
|
|
|
for (const CodeBlockInstruction& cbi : block.instructions)
|
|
|
|
{
|
|
|
|
g_state.pending_ticks++;
|
|
|
|
|
|
|
|
// now executing the instruction we previously fetched
|
|
|
|
g_state.current_instruction.bits = cbi.instruction.bits;
|
|
|
|
g_state.current_instruction_pc = cbi.pc;
|
|
|
|
g_state.current_instruction_in_branch_delay_slot = cbi.is_branch_delay_slot;
|
|
|
|
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
|
|
|
|
g_state.branch_was_taken = false;
|
|
|
|
g_state.exception_raised = false;
|
|
|
|
|
|
|
|
// update pc
|
|
|
|
g_state.regs.pc = g_state.regs.npc;
|
|
|
|
g_state.regs.npc += 4;
|
|
|
|
|
|
|
|
// execute the instruction we previously fetched
|
2020-08-19 13:26:57 +00:00
|
|
|
ExecuteInstruction<pgxp_mode>();
|
2020-07-31 07:09:18 +00:00
|
|
|
|
|
|
|
// next load delay
|
|
|
|
UpdateLoadDelay();
|
|
|
|
|
|
|
|
if (g_state.exception_raised)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanup so the interpreter can kick in if needed
|
|
|
|
g_state.next_instruction_is_branch_delay_slot = false;
|
|
|
|
}
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
template void InterpretCachedBlock<PGXPMode::Disabled>(const CodeBlock& block);
|
|
|
|
template void InterpretCachedBlock<PGXPMode::Memory>(const CodeBlock& block);
|
|
|
|
template void InterpretCachedBlock<PGXPMode::CPU>(const CodeBlock& block);
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void InterpretUncachedBlock()
|
|
|
|
{
|
2020-09-19 11:21:29 +00:00
|
|
|
g_state.regs.npc = g_state.regs.pc;
|
|
|
|
FetchInstruction();
|
2020-07-31 07:09:18 +00:00
|
|
|
|
|
|
|
// At this point, pc contains the last address executed (in the previous block). The instruction has not been fetched
|
|
|
|
// yet. pc shouldn't be updated until the fetch occurs, that way the exception occurs in the delay slot.
|
|
|
|
bool in_branch_delay_slot = false;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
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, except if we're in a branch delay slot. The "fetch" is done in the next block.
|
2020-09-19 11:21:29 +00:00
|
|
|
if (!g_state.current_instruction_in_branch_delay_slot)
|
|
|
|
{
|
|
|
|
if (!FetchInstruction())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_state.regs.pc = g_state.regs.npc;
|
|
|
|
}
|
2020-07-31 07:09:18 +00:00
|
|
|
|
|
|
|
// execute the instruction we previously fetched
|
2020-08-19 13:26:57 +00:00
|
|
|
ExecuteInstruction<PGXPMode::Disabled>();
|
2020-07-31 07:09:18 +00:00
|
|
|
|
|
|
|
// next load delay
|
|
|
|
UpdateLoadDelay();
|
|
|
|
|
|
|
|
const bool branch = IsBranchInstruction(g_state.current_instruction);
|
|
|
|
if (g_state.exception_raised || (!branch && in_branch_delay_slot) ||
|
|
|
|
IsExitBlockInstruction(g_state.current_instruction))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
in_branch_delay_slot = branch;
|
2019-09-17 13:35:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
} // namespace CodeCache
|
|
|
|
|
|
|
|
namespace Recompiler::Thunks {
|
|
|
|
|
|
|
|
bool InterpretInstruction()
|
|
|
|
{
|
2020-08-19 13:26:57 +00:00
|
|
|
ExecuteInstruction<PGXPMode::Disabled>();
|
|
|
|
return g_state.exception_raised;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InterpretInstructionPGXP()
|
|
|
|
{
|
|
|
|
ExecuteInstruction<PGXPMode::Memory>();
|
2020-07-31 07:09:18 +00:00
|
|
|
return g_state.exception_raised;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Recompiler::Thunks
|
|
|
|
|
|
|
|
} // namespace CPU
|