diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index e42edb53a..bfb0f4b1e 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -4,6 +4,9 @@ #include "cpu_disasm.h" #include "cpu_recompiler_code_generator.h" #include "cpu_recompiler_thunks.h" +#include "system.h" +#include +#include Log_SetChannel(CPU::CodeCache); namespace CPU { @@ -15,8 +18,9 @@ CodeCache::CodeCache() = default; CodeCache::~CodeCache() = default; -void CodeCache::Initialize(Core* core, Bus* bus) +void CodeCache::Initialize(System* system, Core* core, Bus* bus) { + m_system = system; m_core = core; m_bus = bus; @@ -122,9 +126,12 @@ bool CodeCache::CompileBlock(CodeBlock* block) } cbi.pc = pc; - cbi.is_branch = IsBranchInstruction(cbi.instruction); cbi.is_branch_delay_slot = is_branch_delay_slot; cbi.is_load_delay_slot = is_load_delay_slot; + cbi.is_branch_instruction = IsBranchInstruction(cbi.instruction); + cbi.is_load_instruction = IsMemoryLoadInstruction(cbi.instruction); + cbi.is_store_instruction = IsMemoryStoreInstruction(cbi.instruction); + cbi.has_load_delay = InstructionHasLoadDelay(cbi.instruction); cbi.can_trap = CanInstructionTrap(cbi.instruction, m_core->InUserMode()); // instruction is decoded now @@ -133,11 +140,14 @@ bool CodeCache::CompileBlock(CodeBlock* block) // if we're in a branch delay slot, the block is now done // except if this is a branch in a branch delay slot, then we grab the one after that, and so on... - if (is_branch_delay_slot && !cbi.is_branch) + if (is_branch_delay_slot && !cbi.is_branch_instruction) break; // if this is a branch, we grab the next instruction (delay slot), and then exit - is_branch_delay_slot = cbi.is_branch; + is_branch_delay_slot = cbi.is_branch_instruction; + + // same for load delay + is_load_delay_slot = cbi.has_load_delay; // is this a non-branchy exit? (e.g. syscall) if (IsExitBlockInstruction(cbi.instruction)) @@ -254,10 +264,7 @@ void CodeCache::InterpretCachedBlock(const CodeBlock& block) m_core->ExecuteInstruction(); // next load delay - m_core->m_load_delay_reg = m_core->m_next_load_delay_reg; - m_core->m_next_load_delay_reg = Reg::count; - m_core->m_load_delay_old_value = m_core->m_next_load_delay_old_value; - m_core->m_next_load_delay_old_value = 0; + m_core->UpdateLoadDelay(); if (m_core->m_exception_raised) break; @@ -294,10 +301,7 @@ void CodeCache::InterpretUncachedBlock() m_core->ExecuteInstruction(); // next load delay - m_core->m_load_delay_reg = m_core->m_next_load_delay_reg; - m_core->m_next_load_delay_reg = Reg::count; - m_core->m_load_delay_old_value = m_core->m_next_load_delay_old_value; - m_core->m_next_load_delay_old_value = 0; + m_core->UpdateLoadDelay(); const bool branch = IsBranchInstruction(m_core->m_current_instruction); if (m_core->m_exception_raised || (!branch && in_branch_delay_slot) || diff --git a/src/core/cpu_code_cache.h b/src/core/cpu_code_cache.h index f68ea2bb1..5b9882032 100644 --- a/src/core/cpu_code_cache.h +++ b/src/core/cpu_code_cache.h @@ -9,6 +9,7 @@ class JitCodeBuffer; class Bus; +class System; namespace CPU { class Core; @@ -23,7 +24,7 @@ public: CodeCache(); ~CodeCache(); - void Initialize(Core* core, Bus* bus); + void Initialize(System* system, Core* core, Bus* bus); void Reset(); void Execute(); @@ -39,6 +40,7 @@ private: void InterpretCachedBlock(const CodeBlock& block); void InterpretUncachedBlock(); + System* m_system; Core* m_core; Bus* m_bus; diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 79625bbe1..5416edd48 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -97,8 +97,10 @@ bool Core::DoState(StateWrapper& sw) sw.Do(&m_next_instruction_is_branch_delay_slot); sw.Do(&m_branch_was_taken); sw.Do(&m_load_delay_reg); + sw.Do(&m_load_delay_value); sw.Do(&m_load_delay_old_value); sw.Do(&m_next_load_delay_reg); + sw.Do(&m_next_load_delay_value); sw.Do(&m_next_load_delay_old_value); sw.Do(&m_cache_control); sw.DoBytes(m_dcache.data(), m_dcache.size()); @@ -338,7 +340,7 @@ bool Core::HasPendingInterrupt() // const bool do_interrupt = m_cop0_regs.sr.IEc && ((m_cop0_regs.cause.Ip & m_cop0_regs.sr.Im) != 0); const bool do_interrupt = m_cop0_regs.sr.IEc && (((m_cop0_regs.cause.bits & m_cop0_regs.sr.bits) & (UINT32_C(0xFF) << 8)) != 0); - + return do_interrupt; } @@ -354,19 +356,16 @@ void Core::DispatchInterrupt() m_next_instruction.cop.cop_n); } -void Core::FlushLoadDelay() -{ - m_load_delay_reg = Reg::count; - m_load_delay_old_value = 0; - m_next_load_delay_reg = Reg::count; - m_next_load_delay_old_value = 0; -} - void Core::FlushPipeline() { // loads are flushed - FlushLoadDelay(); - + m_next_load_delay_reg = Reg::count; + if (m_load_delay_reg != Reg::count) + { + m_regs.r[static_cast(m_load_delay_reg)] = m_load_delay_value; + m_load_delay_reg = Reg::count; + } + // not in a branch delay slot m_branch_was_taken = false; m_next_instruction_is_branch_delay_slot = false; @@ -383,13 +382,15 @@ void Core::FlushPipeline() u32 Core::ReadReg(Reg rs) { - return rs == m_load_delay_reg ? m_load_delay_old_value : m_regs.r[static_cast(rs)]; + return m_regs.r[static_cast(rs)]; } void Core::WriteReg(Reg rd, u32 value) { - if (rd != Reg::zero) - m_regs.r[static_cast(rd)] = value; + m_regs.r[static_cast(rd)] = value; + + // prevent writes to $zero from going through - better than branching/cmov + m_regs.zero = 0; } void Core::WriteRegDelayed(Reg rd, u32 value) @@ -398,10 +399,14 @@ void Core::WriteRegDelayed(Reg rd, u32 value) if (rd == Reg::zero) return; - // save the old value, this will be returned if the register is read in the next instruction + // double load delays ignore the first value + if (m_load_delay_reg == rd) + m_load_delay_reg = Reg::count; + + // save the old value, if something else overwrites this reg we want to preserve it m_next_load_delay_reg = rd; - m_next_load_delay_old_value = ReadReg(rd); - m_regs.r[static_cast(rd)] = value; + m_next_load_delay_value = value; + m_next_load_delay_old_value = m_regs.r[static_cast(rd)]; } std::optional Core::ReadCop0Reg(Cop0Reg reg) @@ -608,10 +613,7 @@ void Core::Execute() ExecuteInstruction(); // next load delay - m_load_delay_reg = m_next_load_delay_reg; - m_next_load_delay_reg = Reg::count; - m_load_delay_old_value = m_next_load_delay_old_value; - m_next_load_delay_old_value = 0; + UpdateLoadDelay(); } } @@ -643,12 +645,12 @@ void Core::ExecuteInstruction() } #endif -//#ifdef _DEBUG + //#ifdef _DEBUG if (TRACE_EXECUTION) PrintInstruction(inst.bits, m_current_instruction_pc, this); if (LOG_EXECUTION) LogInstruction(inst.bits, m_current_instruction_pc, this); -//#endif + //#endif switch (inst.op) { @@ -1039,8 +1041,8 @@ void Core::ExecuteInstruction() if (!ReadMemoryWord(aligned_addr, &aligned_value)) return; - // note: bypasses load delay on the read - const u32 existing_value = m_regs.r[static_cast(inst.i.rt.GetValue())]; + // Bypasses load delay. No need to check the old value since this is the delay slot or it's not relevant. + const u32 existing_value = (inst.i.rt == m_load_delay_reg) ? m_load_delay_value : ReadReg(inst.i.rt); const u8 shift = (Truncate8(addr) & u8(3)) * u8(8); u32 new_value; if (inst.op == InstructionOp::lwl) diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index c0469025b..cd84b7ce6 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -14,11 +14,10 @@ namespace CPU { class CodeCache; -namespace Recompiler -{ +namespace Recompiler { class CodeGenerator; class Thunks; -} +} // namespace Recompiler class Core { @@ -28,7 +27,7 @@ public: static constexpr PhysicalMemoryAddress DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00); static constexpr PhysicalMemoryAddress DCACHE_OFFSET_MASK = UINT32_C(0x000003FF); static constexpr PhysicalMemoryAddress DCACHE_SIZE = UINT32_C(0x00000400); - + friend CodeCache; friend Recompiler::CodeGenerator; friend Recompiler::Thunks; @@ -103,6 +102,19 @@ private: void DisassembleAndLog(u32 addr); void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after); + // Updates load delays - call after each instruction + ALWAYS_INLINE void UpdateLoadDelay() + { + // the old value is needed in case the delay slot instruction overwrites the same register + if (m_load_delay_reg != Reg::count && m_regs.r[static_cast(m_load_delay_reg)] == m_load_delay_old_value) + m_regs.r[static_cast(m_load_delay_reg)] = m_load_delay_value; + + m_load_delay_reg = m_next_load_delay_reg; + m_load_delay_value = m_next_load_delay_value; + m_load_delay_old_value = m_next_load_delay_old_value; + m_next_load_delay_reg = Reg::count; + } + // Fetches the instruction at m_regs.npc bool FetchInstruction(); void ExecuteInstruction(); @@ -117,9 +129,6 @@ private: bool HasPendingInterrupt(); void DispatchInterrupt(); - // flushes any load delays if present - void FlushLoadDelay(); - // clears pipeline of load/branch delays void FlushPipeline(); @@ -158,8 +167,10 @@ private: // load delays Reg m_load_delay_reg = Reg::count; + u32 m_load_delay_value = 0; u32 m_load_delay_old_value = 0; Reg m_next_load_delay_reg = Reg::count; + u32 m_next_load_delay_value = 0; u32 m_next_load_delay_old_value = 0; u32 m_cache_control = 0; diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 13da8d3b5..e9f821169 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -28,11 +28,6 @@ bool CodeGenerator::CompileBlock(const CodeBlock* block, CodeBlock::HostCodePoin m_block_start = block->instructions.data(); m_block_end = block->instructions.data() + block->instructions.size(); - m_current_instruction_in_branch_delay_slot_dirty = true; - m_branch_was_taken_dirty = true; - m_current_instruction_was_branch_taken_dirty = false; - m_load_delay_dirty = true; - EmitBeginBlock(); BlockPrologue(); @@ -340,7 +335,11 @@ void CodeGenerator::BlockPrologue() { EmitStoreCPUStructField(offsetof(Core, m_exception_raised), Value::FromConstantU8(0)); - // fetching of the first instruction... + // we don't know the state of the last block, so assume load delays might be in progress + m_current_instruction_in_branch_delay_slot_dirty = true; + m_branch_was_taken_dirty = true; + m_current_instruction_was_branch_taken_dirty = false; + m_load_delay_dirty = true; // sync m_current_instruction_pc so we can simply add to it SyncCurrentInstructionPC(); @@ -435,7 +434,7 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou m_delayed_pc_add = 0; } - if (!cbi.is_branch) + if (!cbi.is_branch_instruction) m_delayed_pc_add = INSTRUCTION_SIZE; m_delayed_cycles_add += cycles; @@ -445,38 +444,18 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou void CodeGenerator::InstructionEpilogue(const CodeBlockInstruction& cbi) { // copy if the previous instruction was a load, reset the current value on the next instruction - if (m_load_delay_dirty) + if (m_next_load_delay_dirty) { - // cpu->m_load_delay_reg = cpu->m_next_load_delay_reg; - // cpu->m_next_load_delay_reg = Reg::count; - { - Value temp = m_register_cache.AllocateScratch(RegSize_8); - EmitLoadCPUStructField(temp.host_reg, RegSize_8, offsetof(Core, m_next_load_delay_reg)); - EmitStoreCPUStructField(offsetof(Core, m_next_load_delay_reg), - Value::FromConstantU8(static_cast(Reg::count))); - EmitStoreCPUStructField(offsetof(Core, m_load_delay_reg), temp); - } - - // cpu->m_load_delay_old_value = cpu->m_next_load_delay_old_value; - // cpu->m_next_load_delay_old_value = 0; - { - Value temp = m_register_cache.AllocateScratch(RegSize_32); - EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(Core, m_next_load_delay_old_value)); - EmitStoreCPUStructField(offsetof(Core, m_next_load_delay_old_value), Value::FromConstantU32(0)); - EmitStoreCPUStructField(offsetof(Core, m_load_delay_old_value), temp); - } - - m_load_delay_dirty = false; - m_next_load_delay_dirty = true; - } - else if (m_next_load_delay_dirty) - { - // cpu->m_load_delay_reg = Reg::count; - // cpu->m_load_delay_old_value = 0; - EmitStoreCPUStructField(offsetof(Core, m_load_delay_reg), Value::FromConstantU8(static_cast(Reg::count))); - EmitStoreCPUStructField(offsetof(Core, m_load_delay_old_value), Value::FromConstantU32(0)); - + Log_DebugPrint("Emitting delay slot flush (with move next)"); + EmitDelaySlotUpdate(false, false, true); m_next_load_delay_dirty = false; + m_load_delay_dirty = true; + } + else if (m_load_delay_dirty) + { + Log_DebugPrint("Emitting delay slot flush"); + EmitDelaySlotUpdate(true, false, false); + m_load_delay_dirty = false; } } @@ -528,9 +507,9 @@ bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi) EmitFunctionCall(nullptr, &Thunks::InterpretInstruction, m_register_cache.GetCPUPtr()); } - m_current_instruction_in_branch_delay_slot_dirty = cbi.is_branch; - m_branch_was_taken_dirty = cbi.is_branch; - m_load_delay_dirty = true; + m_current_instruction_in_branch_delay_slot_dirty = cbi.is_branch_instruction; + m_branch_was_taken_dirty = cbi.is_branch_instruction; + m_next_load_delay_dirty = cbi.has_load_delay; InstructionEpilogue(cbi); return true; } diff --git a/src/core/cpu_recompiler_code_generator.h b/src/core/cpu_recompiler_code_generator.h index effff6b0a..2321db856 100644 --- a/src/core/cpu_recompiler_code_generator.h +++ b/src/core/cpu_recompiler_code_generator.h @@ -147,6 +147,7 @@ private: void SyncCurrentInstructionPC(); void SyncPC(); void AddPendingCycles(); + void EmitDelaySlotUpdate(bool skip_check_for_delay, bool skip_check_old_value, bool move_next); ////////////////////////////////////////////////////////////////////////// // Instruction Code Generators @@ -172,14 +173,12 @@ private: std::array m_operand_memory_addresses{}; - Xbyak::Label m_block_exit_label; - // whether various flags need to be reset. bool m_current_instruction_in_branch_delay_slot_dirty = false; bool m_branch_was_taken_dirty = false; bool m_current_instruction_was_branch_taken_dirty = false; - bool m_next_load_delay_dirty = false; bool m_load_delay_dirty = false; + bool m_next_load_delay_dirty = false; }; } // namespace CPU_X86::Recompiler diff --git a/src/core/cpu_recompiler_code_generator_x64.cpp b/src/core/cpu_recompiler_code_generator_x64.cpp index 4f47d8f68..749c86c9d 100644 --- a/src/core/cpu_recompiler_code_generator_x64.cpp +++ b/src/core/cpu_recompiler_code_generator_x64.cpp @@ -151,9 +151,8 @@ void CodeGenerator::EmitBeginBlock() void CodeGenerator::EmitEndBlock() { - m_emit.L(m_block_exit_label); m_register_cache.FreeHostReg(RCPUPTR); - m_register_cache.PopCalleeSavedRegisters(); + m_register_cache.PopCalleeSavedRegisters(true); m_emit.ret(); } @@ -166,9 +165,10 @@ void CodeGenerator::EmitBlockExitOnBool(const Value& value) m_emit.test(GetHostReg8(value), GetHostReg8(value)); m_emit.jz(continue_label); - // flush current state + // flush current state and return m_register_cache.FlushAllGuestRegisters(false, false); - m_emit.jmp(m_block_exit_label, Xbyak::CodeGenerator::T_NEAR); + m_register_cache.PopCalleeSavedRegisters(false); + m_emit.ret(); m_emit.L(continue_label); } @@ -1303,6 +1303,62 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) } } +void CodeGenerator::EmitDelaySlotUpdate(bool skip_check_for_delay, bool skip_check_old_value, bool move_next) +{ + Value reg = m_register_cache.AllocateScratch(RegSize_8); + Value value = m_register_cache.AllocateScratch(RegSize_32); + + Xbyak::Label skip_flush; + + auto load_delay_reg = m_emit.byte[GetCPUPtrReg() + offsetof(Core, m_load_delay_reg)]; + auto load_delay_old_value = m_emit.dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_old_value)]; + auto load_delay_value = m_emit.dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_value)]; + auto reg_ptr = m_emit.dword[GetCPUPtrReg() + offsetof(Core, m_regs.r[0]) + GetHostReg64(reg.host_reg) * 4]; + + // reg = load_delay_reg + m_emit.movzx(GetHostReg32(reg.host_reg), load_delay_reg); + if (!skip_check_old_value) + m_emit.mov(GetHostReg32(value), load_delay_old_value); + + if (!skip_check_for_delay) + { + // if load_delay_reg == Reg::count goto skip_flush + m_emit.cmp(GetHostReg32(reg.host_reg), static_cast(Reg::count)); + m_emit.je(skip_flush); + } + + if (!skip_check_old_value) + { + // if r[reg] != load_delay_old_value goto skip_flush + m_emit.cmp(GetHostReg32(value), reg_ptr); + m_emit.jne(skip_flush); + } + + // r[reg] = load_delay_value + m_emit.mov(GetHostReg32(value), load_delay_value); + m_emit.mov(reg_ptr, GetHostReg32(value)); + + // if !move_next load_delay_reg = Reg::count + if (!move_next) + m_emit.mov(load_delay_reg, static_cast(Reg::count)); + + m_emit.L(skip_flush); + + if (move_next) + { + auto next_load_delay_reg = m_emit.byte[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_reg)]; + auto next_load_delay_old_value = m_emit.dword[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_old_value)]; + auto next_load_delay_value = m_emit.dword[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_value)]; + m_emit.mov(GetHostReg32(value), next_load_delay_value); + m_emit.mov(GetHostReg8(reg), next_load_delay_reg); + m_emit.mov(load_delay_value, GetHostReg32(value)); + m_emit.mov(GetHostReg32(value), next_load_delay_old_value); + m_emit.mov(load_delay_reg, GetHostReg8(reg)); + m_emit.mov(load_delay_old_value, GetHostReg32(value)); + m_emit.mov(next_load_delay_reg, static_cast(Reg::count)); + } +} + #if 0 class ThunkGenerator { diff --git a/src/core/cpu_recompiler_register_cache.cpp b/src/core/cpu_recompiler_register_cache.cpp index 3d8ae9f0f..e540c4b77 100644 --- a/src/core/cpu_recompiler_register_cache.cpp +++ b/src/core/cpu_recompiler_register_cache.cpp @@ -307,7 +307,7 @@ u32 RegisterCache::PopCallerSavedRegisters() const return count; } -u32 RegisterCache::PopCalleeSavedRegisters() +u32 RegisterCache::PopCalleeSavedRegisters(bool commit) { if (m_host_register_callee_saved_order_count == 0) return 0; @@ -321,7 +321,8 @@ u32 RegisterCache::PopCalleeSavedRegisters() (HostRegState::CalleeSaved | HostRegState::CalleeSavedAllocated)); m_code_generator.EmitPopHostReg(reg); - m_host_register_state[reg] &= ~HostRegState::CalleeSavedAllocated; + if (commit) + m_host_register_state[reg] &= ~HostRegState::CalleeSavedAllocated; count++; i--; } while (i > 0); diff --git a/src/core/cpu_recompiler_register_cache.h b/src/core/cpu_recompiler_register_cache.h index b01637d25..a090c733f 100644 --- a/src/core/cpu_recompiler_register_cache.h +++ b/src/core/cpu_recompiler_register_cache.h @@ -176,7 +176,7 @@ public: u32 PopCallerSavedRegisters() const; /// Restore callee-saved registers. Call at the end of the function. - u32 PopCalleeSavedRegisters(); + u32 PopCalleeSavedRegisters(bool commit); ////////////////////////////////////////////////////////////////////////// // Scratch Register Allocation diff --git a/src/core/cpu_recompiler_thunks.cpp b/src/core/cpu_recompiler_thunks.cpp index cfdfa1e61..9d968da97 100644 --- a/src/core/cpu_recompiler_thunks.cpp +++ b/src/core/cpu_recompiler_thunks.cpp @@ -40,4 +40,9 @@ bool Thunks::InterpretInstruction(Core* cpu) return cpu->m_exception_raised; } +void Thunks::UpdateLoadDelay(Core* cpu) +{ + cpu->UpdateLoadDelay(); +} + } // namespace CPU::Recompiler \ No newline at end of file diff --git a/src/core/cpu_recompiler_thunks.h b/src/core/cpu_recompiler_thunks.h index 4104d2cca..3cfbfdb14 100644 --- a/src/core/cpu_recompiler_thunks.h +++ b/src/core/cpu_recompiler_thunks.h @@ -20,6 +20,7 @@ public: static bool WriteMemoryHalfWord(Core* cpu, u32 address, u16 value); static bool WriteMemoryWord(Core* cpu, u32 address, u32 value); static bool InterpretInstruction(Core* cpu); + static void UpdateLoadDelay(Core* cpu); }; class ASMFunctions diff --git a/src/core/cpu_types.cpp b/src/core/cpu_types.cpp index 927f83572..57d651c77 100644 --- a/src/core/cpu_types.cpp +++ b/src/core/cpu_types.cpp @@ -44,6 +44,76 @@ bool IsBranchInstruction(const Instruction& instruction) } } +bool IsMemoryLoadInstruction(const Instruction& instruction) +{ + switch (instruction.op) + { + case InstructionOp::lb: + case InstructionOp::lh: + case InstructionOp::lw: + case InstructionOp::lbu: + case InstructionOp::lhu: + case InstructionOp::lwl: + case InstructionOp::lwr: + return true; + + case InstructionOp::lwc2: + return true; + + default: + return false; + } +} + +bool IsMemoryStoreInstruction(const Instruction& instruction) +{ + switch (instruction.op) + { + case InstructionOp::sb: + case InstructionOp::sh: + case InstructionOp::sw: + case InstructionOp::swl: + case InstructionOp::swr: + return true; + + case InstructionOp::swc2: + return true; + + default: + return false; + } +} + +bool InstructionHasLoadDelay(const Instruction& instruction) +{ + switch (instruction.op) + { + case InstructionOp::lb: + case InstructionOp::lh: + case InstructionOp::lw: + case InstructionOp::lbu: + case InstructionOp::lhu: + case InstructionOp::lwl: + case InstructionOp::lwr: + return true; + + case InstructionOp::cop0: + case InstructionOp::cop2: + { + if (instruction.cop.IsCommonInstruction()) + { + const CopCommonInstruction common_op = instruction.cop.CommonOp(); + return (common_op == CopCommonInstruction::cfcn || common_op == CopCommonInstruction::mfcn); + } + + return false; + } + + default: + return false; + } +} + bool IsExitBlockInstruction(const Instruction& instruction) { switch (instruction.op) @@ -167,26 +237,6 @@ bool CanInstructionTrap(const Instruction& instruction, bool in_user_mode) } } -bool IsLoadDelayingInstruction(const Instruction& instruction) -{ - switch (instruction.op) - { - case InstructionOp::lb: - case InstructionOp::lh: - case InstructionOp::lw: - case InstructionOp::lbu: - case InstructionOp::lhu: - return true; - - case InstructionOp::lwl: - case InstructionOp::lwr: - return false; - - default: - return false; - } -} - bool IsInvalidInstruction(const Instruction& instruction) { // TODO diff --git a/src/core/cpu_types.h b/src/core/cpu_types.h index e7a74ef97..61395dd2b 100644 --- a/src/core/cpu_types.h +++ b/src/core/cpu_types.h @@ -208,9 +208,11 @@ union Instruction // Instruction helpers. bool IsBranchInstruction(const Instruction& instruction); +bool IsMemoryLoadInstruction(const Instruction& instruction); +bool IsMemoryStoreInstruction(const Instruction& instruction); +bool InstructionHasLoadDelay(const Instruction& instruction); bool IsExitBlockInstruction(const Instruction& instruction); bool CanInstructionTrap(const Instruction& instruction, bool in_user_mode); -bool IsLoadDelayingInstruction(const Instruction& instruction); bool IsInvalidInstruction(const Instruction& instruction); struct Registers @@ -396,10 +398,13 @@ struct CodeBlockInstruction Instruction instruction; u32 pc; - bool is_branch : 1; + bool is_branch_instruction : 1; bool is_branch_delay_slot : 1; + bool is_load_instruction : 1; + bool is_store_instruction : 1; bool is_load_delay_slot : 1; bool is_last_instruction : 1; + bool has_load_delay : 1; bool can_trap : 1; }; diff --git a/src/core/system.cpp b/src/core/system.cpp index c0877df77..64f89ae07 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -171,7 +171,7 @@ bool System::Boot(const char* filename) void System::InitializeComponents() { m_cpu->Initialize(m_bus.get()); - m_cpu_code_cache->Initialize(m_cpu.get(), m_bus.get()); + m_cpu_code_cache->Initialize(this, m_cpu.get(), m_bus.get()); m_bus->Initialize(m_cpu.get(), m_cpu_code_cache.get(), m_dma.get(), m_interrupt_controller.get(), m_gpu.get(), m_cdrom.get(), m_pad.get(), m_timers.get(), m_spu.get(), m_mdec.get());