CPU: Implement alignment (memory) exception

This commit is contained in:
Connor McLaughlin 2019-09-14 14:29:23 +10:00
parent 0726095f00
commit 32a36ef1bc
3 changed files with 110 additions and 54 deletions

View File

@ -48,46 +48,54 @@ void Core::SetPC(u32 new_pc)
FlushPipeline(); FlushPipeline();
} }
u8 Core::ReadMemoryByte(VirtualMemoryAddress addr) bool Core::ReadMemoryByte(VirtualMemoryAddress addr, u8* value)
{ {
u32 value; u32 temp = 0;
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte, false, true>(addr, value); const bool result = DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte, false, true>(addr, temp);
return Truncate8(value); *value = Truncate8(temp);
return result;
} }
u16 Core::ReadMemoryHalfWord(VirtualMemoryAddress addr) bool Core::ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value)
{ {
Assert(Common::IsAlignedPow2(addr, 2)); if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(addr))
u32 value; return false;
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord, false, true>(addr, value);
return Truncate16(value); 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)); if (!DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(addr))
u32 value; return false;
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word, false, true>(addr, value);
return value; 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); u32 temp = ZeroExtend32(value);
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte, false, true>(addr, value32); 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)); if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(addr))
u32 value32 = ZeroExtend32(value); return false;
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord, false, true>(addr, value32);
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)); if (!DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::Word>(addr))
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word, false, true>(addr, value); return false;
return DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word, false, true>(addr, value);
} }
bool Core::SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value) bool Core::SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value)
@ -134,9 +142,9 @@ void Core::Branch(u32 target)
m_branched = true; 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.Excode = excode;
m_cop0_regs.cause.BD = m_in_branch_delay_slot; m_cop0_regs.cause.BD = m_in_branch_delay_slot;
@ -222,7 +230,7 @@ void Core::Execute()
{ {
// now executing the instruction we previously fetched // now executing the instruction we previously fetched
const Instruction inst = m_next_instruction; const Instruction inst = m_next_instruction;
const u32 inst_pc = m_regs.pc; m_current_instruction_pc = m_regs.pc;
// fetch the next instruction // fetch the next instruction
FetchInstruction(); FetchInstruction();
@ -232,7 +240,7 @@ void Core::Execute()
m_branched = false; m_branched = false;
// execute the instruction we previously fetched // execute the instruction we previously fetched
ExecuteInstruction(inst, inst_pc); ExecuteInstruction(inst);
// next load delay // next load delay
m_load_delay_reg = m_next_load_delay_reg; m_load_delay_reg = m_next_load_delay_reg;
@ -249,7 +257,7 @@ void Core::FetchInstruction()
m_regs.npc += sizeof(m_next_instruction.bits); m_regs.npc += sizeof(m_next_instruction.bits);
} }
void Core::ExecuteInstruction(Instruction inst, u32 inst_pc) void Core::ExecuteInstruction(Instruction inst)
{ {
#if 0 #if 0
if (inst_pc == 0xBFC06FF0) if (inst_pc == 0xBFC06FF0)
@ -260,7 +268,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
#endif #endif
if (TRACE_EXECUTION) if (TRACE_EXECUTION)
PrintInstruction(inst.bits, inst_pc); PrintInstruction(inst.bits, m_current_instruction_pc);
switch (inst.op) switch (inst.op)
{ {
@ -348,7 +356,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
const u32 new_value = old_value + add_value; const u32 new_value = old_value + add_value;
if (AddOverflow(old_value, add_value, new_value)) if (AddOverflow(old_value, add_value, new_value))
{ {
RaiseException(inst_pc, Exception::Ov); RaiseException(Exception::Ov);
return; return;
} }
@ -370,7 +378,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
const u32 new_value = old_value - sub_value; const u32 new_value = old_value - sub_value;
if (SubOverflow(old_value, sub_value, new_value)) if (SubOverflow(old_value, sub_value, new_value))
{ {
RaiseException(inst_pc, Exception::Ov); RaiseException(Exception::Ov);
return; return;
} }
@ -507,7 +515,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
case InstructionFunct::syscall: case InstructionFunct::syscall:
{ {
RaiseException(inst_pc, Exception::Syscall); RaiseException(Exception::Syscall);
} }
break; break;
@ -549,7 +557,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
const u32 new_value = old_value + add_value; const u32 new_value = old_value + add_value;
if (AddOverflow(old_value, add_value, new_value)) if (AddOverflow(old_value, add_value, new_value))
{ {
RaiseException(inst_pc, Exception::Ov); RaiseException(Exception::Ov);
return; return;
} }
@ -580,7 +588,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
case InstructionOp::lb: case InstructionOp::lb:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); 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)); WriteRegDelayed(inst.i.rt, SignExtend32(value));
} }
break; break;
@ -588,7 +599,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
case InstructionOp::lh: case InstructionOp::lh:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); 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)); WriteRegDelayed(inst.i.rt, SignExtend32(value));
} }
break; break;
@ -596,7 +610,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
case InstructionOp::lw: case InstructionOp::lw:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); 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); WriteRegDelayed(inst.i.rt, value);
} }
break; break;
@ -604,7 +621,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
case InstructionOp::lbu: case InstructionOp::lbu:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); 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)); WriteRegDelayed(inst.i.rt, ZeroExtend32(value));
} }
break; break;
@ -612,7 +632,10 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
case InstructionOp::lhu: case InstructionOp::lhu:
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); 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)); WriteRegDelayed(inst.i.rt, ZeroExtend32(value));
} }
break; 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 addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); 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 // note: bypasses load delay on the read
const u32 existing_value = m_regs.r[static_cast<u8>(inst.i.rt.GetValue())]; 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 addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3); const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
const u32 mem_value = ReadMemoryWord(aligned_addr);
const u32 reg_value = ReadReg(inst.i.rt); const u32 reg_value = ReadReg(inst.i.rt);
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8); const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
u32 mem_value;
if (!ReadMemoryWord(aligned_addr, &mem_value))
return;
u32 new_value; u32 new_value;
if (inst.op == InstructionOp::swl) if (inst.op == InstructionOp::swl)
@ -760,11 +787,11 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
if (!m_cop0_regs.sr.CU0 && InUserMode()) if (!m_cop0_regs.sr.CU0 && InUserMode())
{ {
Log_WarningPrintf("Coprocessor 0 not present in user mode"); Log_WarningPrintf("Coprocessor 0 not present in user mode");
RaiseException(inst_pc, Exception::CpU); RaiseException(Exception::CpU);
return; return;
} }
ExecuteCop0Instruction(inst, inst_pc); ExecuteCop0Instruction(inst);
} }
break; break;
@ -772,7 +799,7 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
case InstructionOp::cop1: case InstructionOp::cop1:
case InstructionOp::cop2: case InstructionOp::cop2:
{ {
RaiseException(inst_pc, Exception::CpU); RaiseException(Exception::CpU);
} }
break; 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()) switch (inst.cop.cop0_op())
{ {

View File

@ -45,15 +45,18 @@ private:
template<MemoryAccessType type, MemoryAccessSize size, bool is_instruction_fetch, bool raise_exceptions> template<MemoryAccessType type, MemoryAccessSize size, bool is_instruction_fetch, bool raise_exceptions>
bool DoMemoryAccess(VirtualMemoryAddress address, u32& value); bool DoMemoryAccess(VirtualMemoryAddress address, u32& value);
template<MemoryAccessType type, MemoryAccessSize size>
bool DoAlignmentCheck(VirtualMemoryAddress address);
template<MemoryAccessType type, MemoryAccessSize size> template<MemoryAccessType type, MemoryAccessSize size>
void DoScratchpadAccess(PhysicalMemoryAddress address, u32& value); void DoScratchpadAccess(PhysicalMemoryAddress address, u32& value);
u8 ReadMemoryByte(VirtualMemoryAddress addr); bool ReadMemoryByte(VirtualMemoryAddress addr, u8* value);
u16 ReadMemoryHalfWord(VirtualMemoryAddress addr); bool ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
u32 ReadMemoryWord(VirtualMemoryAddress addr); bool ReadMemoryWord(VirtualMemoryAddress addr, u32* value);
void WriteMemoryByte(VirtualMemoryAddress addr, u8 value); bool WriteMemoryByte(VirtualMemoryAddress addr, u8 value);
void WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value); bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
void WriteMemoryWord(VirtualMemoryAddress addr, u32 value); bool WriteMemoryWord(VirtualMemoryAddress addr, u32 value);
// state helpers // state helpers
bool InUserMode() const { return m_cop0_regs.sr.KUc; } bool InUserMode() const { return m_cop0_regs.sr.KUc; }
@ -63,10 +66,10 @@ private:
// Fetches the instruction at m_regs.npc // Fetches the instruction at m_regs.npc
void FetchInstruction(); void FetchInstruction();
void ExecuteInstruction(Instruction inst, u32 inst_pc); void ExecuteInstruction(Instruction inst);
void ExecuteCop0Instruction(Instruction inst, u32 inst_pc); void ExecuteCop0Instruction(Instruction inst);
void Branch(u32 target); void Branch(u32 target);
void RaiseException(u32 inst_pc, Exception excode); void RaiseException(Exception excode);
// clears pipeline of load/branch delays // clears pipeline of load/branch delays
void FlushPipeline(); void FlushPipeline();
@ -87,6 +90,9 @@ private:
bool m_in_branch_delay_slot = false; bool m_in_branch_delay_slot = false;
bool m_branched = false; bool m_branched = false;
// address of the instruction currently being executed
u32 m_current_instruction_pc = 0;
// load delays // load delays
Reg m_load_delay_reg = Reg::count; Reg m_load_delay_reg = Reg::count;
u32 m_load_delay_old_value = 0; u32 m_load_delay_old_value = 0;

View File

@ -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> template<MemoryAccessType type, MemoryAccessSize size>
void CPU::Core::DoScratchpadAccess(PhysicalMemoryAddress address, u32& value) void CPU::Core::DoScratchpadAccess(PhysicalMemoryAddress address, u32& value)
{ {