CPU: Implement alignment (memory) exception
This commit is contained in:
parent
0726095f00
commit
32a36ef1bc
|
@ -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<MemoryAccessType::Read, MemoryAccessSize::Byte, false, true>(addr, value);
|
||||
return Truncate8(value);
|
||||
u32 temp = 0;
|
||||
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte, false, true>(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<MemoryAccessType::Read, MemoryAccessSize::HalfWord, false, true>(addr, value);
|
||||
return Truncate16(value);
|
||||
if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr))
|
||||
return false;
|
||||
|
||||
u32 temp = 0;
|
||||
const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord, false, true>(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<MemoryAccessType::Read, MemoryAccessSize::Word, false, true>(addr, value);
|
||||
return value;
|
||||
if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(addr))
|
||||
return false;
|
||||
|
||||
return DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word, false, true>(addr, *value);
|
||||
}
|
||||
|
||||
void Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value)
|
||||
bool Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value)
|
||||
{
|
||||
u32 value32 = ZeroExtend32(value);
|
||||
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte, false, true>(addr, value32);
|
||||
u32 temp = ZeroExtend32(value);
|
||||
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte, false, true>(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<MemoryAccessType::Write, MemoryAccessSize::HalfWord, false, true>(addr, value32);
|
||||
if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr))
|
||||
return false;
|
||||
|
||||
u32 temp = ZeroExtend32(value);
|
||||
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord, false, true>(addr, temp);
|
||||
}
|
||||
|
||||
void Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value)
|
||||
bool Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value)
|
||||
{
|
||||
Assert(Common::IsAlignedPow2(addr, 4));
|
||||
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word, false, true>(addr, value);
|
||||
if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::Word>(addr))
|
||||
return false;
|
||||
|
||||
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word, false, true>(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<u8>(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())
|
||||
{
|
||||
|
|
|
@ -45,15 +45,18 @@ private:
|
|||
template<MemoryAccessType type, MemoryAccessSize size, bool is_instruction_fetch, bool raise_exceptions>
|
||||
bool DoMemoryAccess(VirtualMemoryAddress address, u32& value);
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
bool DoAlignmentCheck(VirtualMemoryAddress address);
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
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;
|
||||
|
|
|
@ -106,6 +106,29 @@ bool Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
|
|||
}
|
||||
}
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
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<MemoryAccessType type, MemoryAccessSize size>
|
||||
void CPU::Core::DoScratchpadAccess(PhysicalMemoryAddress address, u32& value)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue