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();
|
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())
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue