diff --git a/src/pse/cpu_core.cpp b/src/pse/cpu_core.cpp index 7d660e142..25606f444 100644 --- a/src/pse/cpu_core.cpp +++ b/src/pse/cpu_core.cpp @@ -48,46 +48,54 @@ void Core::SetPC(u32 new_pc) FlushPipeline(); } -u8 Core::ReadMemoryByte(VirtualMemoryAddress addr) +bool Core::ReadMemoryByte(VirtualMemoryAddress addr, u8* value) { - u32 value; - DoMemoryAccess(addr, value); - return Truncate8(value); + u32 temp = 0; + const bool result = DoMemoryAccess(addr, temp); + *value = Truncate8(temp); + return result; } -u16 Core::ReadMemoryHalfWord(VirtualMemoryAddress addr) +bool Core::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value) { - Assert(Common::IsAlignedPow2(addr, 2)); - u32 value; - DoMemoryAccess(addr, value); - return Truncate16(value); + if (!DoAlignmentCheck(addr)) + return false; + + u32 temp = 0; + const bool result = DoMemoryAccess(addr, temp); + *value = Truncate16(temp); + return result; } -u32 Core::ReadMemoryWord(VirtualMemoryAddress addr) +bool Core::ReadMemoryWord(VirtualMemoryAddress addr, u32* value) { - Assert(Common::IsAlignedPow2(addr, 4)); - u32 value; - DoMemoryAccess(addr, value); - return value; + if (!DoAlignmentCheck(addr)) + return false; + + return DoMemoryAccess(addr, *value); } -void Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value) +bool Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value) { - u32 value32 = ZeroExtend32(value); - DoMemoryAccess(addr, value32); + u32 temp = ZeroExtend32(value); + return DoMemoryAccess(addr, temp); } -void Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value) +bool Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value) { - Assert(Common::IsAlignedPow2(addr, 2)); - u32 value32 = ZeroExtend32(value); - DoMemoryAccess(addr, value32); + if (!DoAlignmentCheck(addr)) + return false; + + u32 temp = ZeroExtend32(value); + return DoMemoryAccess(addr, temp); } -void Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value) +bool Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value) { - Assert(Common::IsAlignedPow2(addr, 4)); - DoMemoryAccess(addr, value); + if (!DoAlignmentCheck(addr)) + return false; + + return DoMemoryAccess(addr, value); } bool Core::SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value) @@ -134,9 +142,9 @@ void Core::Branch(u32 target) m_branched = true; } -void Core::RaiseException(u32 inst_pc, Exception excode) +void Core::RaiseException(Exception excode) { - m_cop0_regs.EPC = m_in_branch_delay_slot ? (inst_pc - UINT32_C(4)) : inst_pc; + m_cop0_regs.EPC = m_in_branch_delay_slot ? (m_current_instruction_pc - UINT32_C(4)) : m_current_instruction_pc; m_cop0_regs.cause.Excode = excode; m_cop0_regs.cause.BD = m_in_branch_delay_slot; @@ -222,7 +230,7 @@ void Core::Execute() { // now executing the instruction we previously fetched const Instruction inst = m_next_instruction; - const u32 inst_pc = m_regs.pc; + m_current_instruction_pc = m_regs.pc; // fetch the next instruction FetchInstruction(); @@ -232,7 +240,7 @@ void Core::Execute() m_branched = false; // execute the instruction we previously fetched - ExecuteInstruction(inst, inst_pc); + ExecuteInstruction(inst); // next load delay m_load_delay_reg = m_next_load_delay_reg; @@ -249,7 +257,7 @@ void Core::FetchInstruction() m_regs.npc += sizeof(m_next_instruction.bits); } -void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) +void Core::ExecuteInstruction(Instruction inst) { #if 0 if (inst_pc == 0xBFC06FF0) @@ -260,7 +268,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) #endif if (TRACE_EXECUTION) - PrintInstruction(inst.bits, inst_pc); + PrintInstruction(inst.bits, m_current_instruction_pc); switch (inst.op) { @@ -348,7 +356,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) const u32 new_value = old_value + add_value; if (AddOverflow(old_value, add_value, new_value)) { - RaiseException(inst_pc, Exception::Ov); + RaiseException(Exception::Ov); return; } @@ -370,7 +378,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) const u32 new_value = old_value - sub_value; if (SubOverflow(old_value, sub_value, new_value)) { - RaiseException(inst_pc, Exception::Ov); + RaiseException(Exception::Ov); return; } @@ -507,7 +515,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) case InstructionFunct::syscall: { - RaiseException(inst_pc, Exception::Syscall); + RaiseException(Exception::Syscall); } break; @@ -549,7 +557,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) const u32 new_value = old_value + add_value; if (AddOverflow(old_value, add_value, new_value)) { - RaiseException(inst_pc, Exception::Ov); + RaiseException(Exception::Ov); return; } @@ -580,7 +588,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) case InstructionOp::lb: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - const u8 value = ReadMemoryByte(addr); + u8 value; + if (!ReadMemoryByte(addr, &value)) + return; + WriteRegDelayed(inst.i.rt, SignExtend32(value)); } break; @@ -588,7 +599,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) case InstructionOp::lh: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - const u16 value = ReadMemoryHalfWord(addr); + u16 value; + if (!ReadMemoryHalfWord(addr, &value)) + return; + WriteRegDelayed(inst.i.rt, SignExtend32(value)); } break; @@ -596,7 +610,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) case InstructionOp::lw: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - const u32 value = ReadMemoryWord(addr); + u32 value; + if (!ReadMemoryWord(addr, &value)) + return; + WriteRegDelayed(inst.i.rt, value); } break; @@ -604,7 +621,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) case InstructionOp::lbu: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - const u8 value = ReadMemoryByte(addr); + u8 value; + if (!ReadMemoryByte(addr, &value)) + return; + WriteRegDelayed(inst.i.rt, ZeroExtend32(value)); } break; @@ -612,7 +632,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) case InstructionOp::lhu: { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); - const u16 value = ReadMemoryHalfWord(addr); + u16 value; + if (!ReadMemoryHalfWord(addr, &value)) + return; + WriteRegDelayed(inst.i.rt, ZeroExtend32(value)); } break; @@ -622,7 +645,9 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); - const u32 aligned_value = ReadMemoryWord(aligned_addr); + u32 aligned_value; + 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())]; @@ -672,9 +697,11 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) { const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); - const u32 mem_value = ReadMemoryWord(aligned_addr); const u32 reg_value = ReadReg(inst.i.rt); const u8 shift = (Truncate8(addr) & u8(3)) * u8(8); + u32 mem_value; + if (!ReadMemoryWord(aligned_addr, &mem_value)) + return; u32 new_value; if (inst.op == InstructionOp::swl) @@ -760,11 +787,11 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) if (!m_cop0_regs.sr.CU0 && InUserMode()) { Log_WarningPrintf("Coprocessor 0 not present in user mode"); - RaiseException(inst_pc, Exception::CpU); + RaiseException(Exception::CpU); return; } - ExecuteCop0Instruction(inst, inst_pc); + ExecuteCop0Instruction(inst); } break; @@ -772,7 +799,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) case InstructionOp::cop1: case InstructionOp::cop2: { - RaiseException(inst_pc, Exception::CpU); + RaiseException(Exception::CpU); } break; @@ -788,7 +815,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) } } -void Core::ExecuteCop0Instruction(Instruction inst, u32 inst_pc) +void Core::ExecuteCop0Instruction(Instruction inst) { switch (inst.cop.cop0_op()) { diff --git a/src/pse/cpu_core.h b/src/pse/cpu_core.h index d5b781875..e5804f0fd 100644 --- a/src/pse/cpu_core.h +++ b/src/pse/cpu_core.h @@ -45,15 +45,18 @@ private: template bool DoMemoryAccess(VirtualMemoryAddress address, u32& value); + template + bool DoAlignmentCheck(VirtualMemoryAddress address); + template void DoScratchpadAccess(PhysicalMemoryAddress address, u32& value); - u8 ReadMemoryByte(VirtualMemoryAddress addr); - u16 ReadMemoryHalfWord(VirtualMemoryAddress addr); - u32 ReadMemoryWord(VirtualMemoryAddress addr); - void WriteMemoryByte(VirtualMemoryAddress addr, u8 value); - void WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value); - void WriteMemoryWord(VirtualMemoryAddress addr, u32 value); + bool ReadMemoryByte(VirtualMemoryAddress addr, u8* value); + bool ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value); + bool ReadMemoryWord(VirtualMemoryAddress addr, u32* value); + bool WriteMemoryByte(VirtualMemoryAddress addr, u8 value); + bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value); + bool WriteMemoryWord(VirtualMemoryAddress addr, u32 value); // state helpers bool InUserMode() const { return m_cop0_regs.sr.KUc; } @@ -63,10 +66,10 @@ private: // Fetches the instruction at m_regs.npc void FetchInstruction(); - void ExecuteInstruction(Instruction inst, u32 inst_pc); - void ExecuteCop0Instruction(Instruction inst, u32 inst_pc); + void ExecuteInstruction(Instruction inst); + void ExecuteCop0Instruction(Instruction inst); void Branch(u32 target); - void RaiseException(u32 inst_pc, Exception excode); + void RaiseException(Exception excode); // clears pipeline of load/branch delays void FlushPipeline(); @@ -87,6 +90,9 @@ private: bool m_in_branch_delay_slot = false; bool m_branched = false; + // address of the instruction currently being executed + u32 m_current_instruction_pc = 0; + // load delays Reg m_load_delay_reg = Reg::count; u32 m_load_delay_old_value = 0; diff --git a/src/pse/cpu_core.inl b/src/pse/cpu_core.inl index 69cd3d954..c3269e4ca 100644 --- a/src/pse/cpu_core.inl +++ b/src/pse/cpu_core.inl @@ -106,6 +106,29 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value) } } +template +bool CPU::Core::DoAlignmentCheck(VirtualMemoryAddress address) +{ + if constexpr (size == MemoryAccessSize::HalfWord) + { + if ((address & UINT32_C(1)) == 0) + return true; + } + else if constexpr (size == MemoryAccessSize::Word) + { + if ((address & UINT32_C(3)) == 0) + return true; + } + else + { + return true; + } + + m_cop0_regs.BadVaddr = address; + RaiseException(type == MemoryAccessType::Read ? Exception::AdEL : Exception::AdES); + return false; +} + template void CPU::Core::DoScratchpadAccess(PhysicalMemoryAddress address, u32& value) {