CPU: Refactoring, implement LWC/SWC

This commit is contained in:
Connor McLaughlin 2019-09-22 02:06:47 +10:00
parent 2875a22987
commit 948ac50020
4 changed files with 248 additions and 200 deletions

View File

@ -283,6 +283,117 @@ void Core::WriteRegDelayed(Reg rd, u32 value)
m_regs.r[static_cast<u8>(rd)] = value;
}
u32 Core::ReadCop0Reg(Cop0Reg reg)
{
switch (reg)
{
case Cop0Reg::BPC:
return m_cop0_regs.BPC;
case Cop0Reg::BPCM:
return m_cop0_regs.BPCM;
case Cop0Reg::BDA:
return m_cop0_regs.BDA;
case Cop0Reg::BDAM:
return m_cop0_regs.BDAM;
case Cop0Reg::DCIC:
return m_cop0_regs.dcic.bits;
case Cop0Reg::JUMPDEST:
return m_cop0_regs.JUMPDEST;
case Cop0Reg::BadVaddr:
return m_cop0_regs.BadVaddr;
case Cop0Reg::SR:
return m_cop0_regs.sr.bits;
case Cop0Reg::CAUSE:
return m_cop0_regs.cause.bits;
case Cop0Reg::EPC:
return m_cop0_regs.EPC;
case Cop0Reg::PRID:
return m_cop0_regs.PRID;
default:
Panic("Unknown COP0 reg");
return 0;
}
}
void Core::WriteCop0Reg(Cop0Reg reg, u32 value)
{
switch (reg)
{
case Cop0Reg::BPC:
{
m_cop0_regs.BPC = value;
Log_WarningPrintf("COP0 BPC <- %08X", value);
}
break;
case Cop0Reg::BPCM:
{
m_cop0_regs.BPCM = value;
Log_WarningPrintf("COP0 BPCM <- %08X", value);
}
break;
case Cop0Reg::BDA:
{
m_cop0_regs.BDA = value;
Log_WarningPrintf("COP0 BDA <- %08X", value);
}
break;
case Cop0Reg::BDAM:
{
m_cop0_regs.BDAM = value;
Log_WarningPrintf("COP0 BDAM <- %08X", value);
}
break;
case Cop0Reg::JUMPDEST:
{
Log_WarningPrintf("Ignoring write to Cop0 JUMPDEST");
}
break;
case Cop0Reg::DCIC:
{
m_cop0_regs.dcic.bits =
(m_cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK);
Log_WarningPrintf("COP0 DCIC <- %08X (now %08X)", value, m_cop0_regs.dcic.bits);
}
break;
case Cop0Reg::SR:
{
m_cop0_regs.sr.bits =
(m_cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) | (value & Cop0Registers::SR::WRITE_MASK);
Log_WarningPrintf("COP0 SR <- %08X (now %08X)", value, m_cop0_regs.sr.bits);
}
break;
case Cop0Reg::CAUSE:
{
m_cop0_regs.cause.bits =
(m_cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) | (value & Cop0Registers::CAUSE::WRITE_MASK);
Log_WarningPrintf("COP0 CAUSE <- %08X (now %08X)", value, m_cop0_regs.cause.bits);
}
break;
default:
Panic("Unknown COP0 reg");
break;
}
}
void Core::WriteCacheControl(u32 value)
{
Log_WarningPrintf("Cache control <- 0x%08X", value);
@ -895,7 +1006,7 @@ void Core::ExecuteInstruction(Instruction inst)
case InstructionOp::cop0:
{
if (!m_cop0_regs.sr.CU0 && InUserMode())
if (InUserMode() && !m_cop0_regs.sr.CU0)
{
Log_WarningPrintf("Coprocessor 0 not present in user mode");
RaiseException(Exception::CpU, 0);
@ -908,7 +1019,7 @@ void Core::ExecuteInstruction(Instruction inst)
case InstructionOp::cop2:
{
if (!m_cop0_regs.sr.CU0 && InUserMode())
if (InUserMode() && !m_cop0_regs.sr.CU2)
{
Log_WarningPrintf("Coprocessor 2 not present in user mode");
RaiseException(Exception::CpU, 2);
@ -919,9 +1030,46 @@ void Core::ExecuteInstruction(Instruction inst)
}
break;
case InstructionOp::lwc2:
{
if (InUserMode() && !m_cop0_regs.sr.CU2)
{
Log_WarningPrintf("Coprocessor 2 not present in user mode");
RaiseException(Exception::CpU, 2);
return;
}
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
u32 value;
if (!ReadMemoryWord(addr, &value))
return;
m_cop2.WriteDataRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())), value);
}
break;
case InstructionOp::swc2:
{
if (InUserMode() && !m_cop0_regs.sr.CU2)
{
Log_WarningPrintf("Coprocessor 2 not present in user mode");
RaiseException(Exception::CpU, 2);
return;
}
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u32 value = m_cop2.ReadDataRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())));
WriteMemoryWord(addr, value);
}
break;
// COP1/COP3 are not present
case InstructionOp::cop1:
case InstructionOp::cop3:
case InstructionOp::lwc1:
case InstructionOp::swc1:
case InstructionOp::lwc3:
case InstructionOp::swc3:
{
RaiseException(Exception::CpU, inst.cop.cop_n);
}
@ -935,137 +1083,27 @@ void Core::ExecuteInstruction(Instruction inst)
void Core::ExecuteCop0Instruction(Instruction inst)
{
switch (inst.cop.cop0_op())
if (inst.cop.IsCommonInstruction())
{
case Cop0Instruction::mtc0:
switch (inst.cop.CommonOp())
{
const u32 value = ReadReg(inst.r.rt);
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
{
case Cop0Reg::BPC:
{
m_cop0_regs.BPC = value;
Log_WarningPrintf("COP0 BPC <- %08X", value);
}
case CopCommonInstruction::mfcn:
WriteRegDelayed(inst.r.rt, ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue())));
break;
case Cop0Reg::BPCM:
{
m_cop0_regs.BPCM = value;
Log_WarningPrintf("COP0 BPCM <- %08X", value);
}
break;
case Cop0Reg::BDA:
{
m_cop0_regs.BDA = value;
Log_WarningPrintf("COP0 BDA <- %08X", value);
}
break;
case Cop0Reg::BDAM:
{
m_cop0_regs.BDAM = value;
Log_WarningPrintf("COP0 BDAM <- %08X", value);
}
break;
case Cop0Reg::JUMPDEST:
{
Log_WarningPrintf("Ignoring write to Cop0 JUMPDEST");
}
break;
case Cop0Reg::DCIC:
{
m_cop0_regs.dcic.bits =
(m_cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK);
Log_WarningPrintf("COP0 DCIC <- %08X (now %08X)", value, m_cop0_regs.dcic.bits);
}
break;
case Cop0Reg::SR:
{
m_cop0_regs.sr.bits =
(m_cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) | (value & Cop0Registers::SR::WRITE_MASK);
Log_WarningPrintf("COP0 SR <- %08X (now %08X)", value, m_cop0_regs.sr.bits);
}
break;
case Cop0Reg::CAUSE:
{
m_cop0_regs.cause.bits =
(m_cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) | (value & Cop0Registers::CAUSE::WRITE_MASK);
Log_WarningPrintf("COP0 CAUSE <- %08X (now %08X)", value, m_cop0_regs.cause.bits);
}
case CopCommonInstruction::mtcn:
WriteCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()), ReadReg(inst.r.rt));
break;
default:
Panic("Unknown COP0 reg");
Panic("Missing implementation");
break;
}
}
break;
case Cop0Instruction::mfc0:
else
{
u32 value;
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
switch (inst.cop.Cop0Op())
{
case Cop0Reg::BPC:
value = m_cop0_regs.BPC;
break;
case Cop0Reg::BPCM:
value = m_cop0_regs.BPCM;
break;
case Cop0Reg::BDA:
value = m_cop0_regs.BDA;
break;
case Cop0Reg::BDAM:
value = m_cop0_regs.BDAM;
break;
case Cop0Reg::DCIC:
value = m_cop0_regs.dcic.bits;
break;
case Cop0Reg::JUMPDEST:
value = m_cop0_regs.JUMPDEST;
break;
case Cop0Reg::BadVaddr:
value = m_cop0_regs.BadVaddr;
break;
case Cop0Reg::SR:
value = m_cop0_regs.sr.bits;
break;
case Cop0Reg::CAUSE:
value = m_cop0_regs.cause.bits;
break;
case Cop0Reg::EPC:
value = m_cop0_regs.EPC;
break;
case Cop0Reg::PRID:
value = m_cop0_regs.PRID;
break;
default:
Panic("Unknown COP0 reg");
value = 0;
break;
}
WriteRegDelayed(inst.r.rt, value);
}
break;
case Cop0Instruction::rfe:
{
// restore mode
@ -1074,30 +1112,36 @@ void Core::ExecuteCop0Instruction(Instruction inst)
break;
default:
Panic("Unhandled instruction");
Panic("Missing implementation");
break;
}
}
}
void Core::ExecuteCop2Instruction(Instruction inst)
{
if (inst.cop.IsCommonInstruction())
{
// TODO: Combine with cop0.
switch (inst.cop.cop2_op())
switch (inst.cop.CommonOp())
{
case Cop2Instruction::mfc2:
case Cop2Instruction::cfc2:
case Cop2Instruction::mtc2:
case Cop2Instruction::ctc2:
{
const u32 index = static_cast<u32>(inst.r.rd.GetValue());
const u32 value = ReadReg(inst.r.rt);
m_cop2.WriteControlRegister(index, value);
}
case CopCommonInstruction::cfcn:
WriteRegDelayed(inst.r.rt, m_cop2.ReadControlRegister(static_cast<u32>(inst.r.rd.GetValue())));
break;
case Cop2Instruction::bc2c:
case CopCommonInstruction::ctcn:
m_cop2.WriteControlRegister(static_cast<u32>(inst.r.rd.GetValue()), ReadReg(inst.r.rt));
break;
case CopCommonInstruction::mfcn:
WriteRegDelayed(inst.r.rt, m_cop2.ReadDataRegister(static_cast<u32>(inst.r.rd.GetValue())));
break;
case CopCommonInstruction::mtcn:
m_cop2.WriteDataRegister(static_cast<u32>(inst.r.rd.GetValue()), ReadReg(inst.r.rt));
break;
case CopCommonInstruction::bcnc:
default:
Panic("Missing implementation");
break;

View File

@ -103,6 +103,10 @@ private:
// write to cache control register
void WriteCacheControl(u32 value);
// read/write cop0 regs
u32 ReadCop0Reg(Cop0Reg reg);
void WriteCop0Reg(Cop0Reg reg, u32 value);
Bus* m_bus = nullptr;
// ticks the CPU has executed

View File

@ -160,20 +160,14 @@ static const std::array<const char*, 64> s_special_table = {{
"UNKNOWN" // 63
}};
static const std::array<std::pair<Cop0Instruction, const char*>, 6> s_cop0_table = {
{{Cop0Instruction::mfc0, "mfc0 $rt, $coprd"},
{Cop0Instruction::cfc0, "cfc0 $rt, $copcr"},
{Cop0Instruction::mtc0, "mtc0 $rt, $coprd"},
{Cop0Instruction::ctc0, "ctc0 $rt, $copcr"},
{Cop0Instruction::bc0c, "bc0$copcc $rel"},
{Cop0Instruction::rfe, "rfe"}}};
static const std::array<std::pair<CopCommonInstruction, const char*>, 5> s_cop_common_table = {
{{CopCommonInstruction::mfcn, "mfc$cop $rt, $coprd"},
{CopCommonInstruction::cfcn, "cfc$cop $rt, $copcr"},
{CopCommonInstruction::mtcn, "mtc$cop $rt, $coprd"},
{CopCommonInstruction::ctcn, "ctc$cop $rt, $copcr"},
{CopCommonInstruction::bcnc, "bc$cop$copcc $rel"}}};
static const std::array<std::pair<Cop2Instruction, const char*>, 6> s_cop2_common_table = {
{{Cop2Instruction::mfc2, "mfc2 $rt, $coprd"},
{Cop2Instruction::cfc2, "cfc2 $rt, $copcr"},
{Cop2Instruction::mtc2, "mtc2 $rt, $coprd"},
{Cop2Instruction::ctc2, "ctc2 $rt, $copcr"},
{Cop2Instruction::bc2c, "bc2$copcc $rel"}}};
static const std::array<std::pair<Cop0Instruction, const char*>, 1> s_cop0_table = {{{Cop0Instruction::rfe, "rfe"}}};
static void FormatInstruction(String* dest, const Instruction inst, u32 pc, const char* format)
{
@ -286,26 +280,35 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits)
return;
case InstructionOp::cop0:
FormatCopInstruction(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.cop0_op());
return;
case InstructionOp::cop1:
case InstructionOp::cop2:
case InstructionOp::cop3:
{
if (inst.cop.IsCommonInstruction())
{
FormatCopInstruction(dest, pc, inst, s_cop2_common_table.data(), s_cop2_common_table.size(),
inst.cop.cop2_op());
FormatCopInstruction(dest, pc, inst, s_cop_common_table.data(), s_cop_common_table.size(), inst.cop.CommonOp());
}
else
{
dest->Format("<cop%u 0x%08X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
}
switch (inst.op)
{
case InstructionOp::cop0:
{
FormatCopInstruction(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
}
break;
case InstructionOp::cop1:
case InstructionOp::cop2:
case InstructionOp::cop3:
default:
{
dest->Format("<cop%u 0x%08X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
}
break;
}
}
}
break;
// special case for bltz/bgez{al}

View File

@ -74,7 +74,15 @@ enum class InstructionOp : u8
sh = 41,
swl = 42,
sw = 43,
swr = 46
swr = 46,
lwc0 = 48,
lwc1 = 49,
lwc2 = 50,
lwc3 = 51,
swc0 = 56,
swc1 = 57,
swc2 = 58,
swc3 = 59,
};
constexpr u8 INSTRUCTION_COP_BITS = 0x10;
constexpr u8 INSTRUCTION_COP_MASK = 0x3C;
@ -113,27 +121,22 @@ enum class InstructionFunct : u8
sltu = 43
};
enum class Cop0Instruction : u32 // 25:21 | 0:5
enum class CopCommonInstruction : u32
{
mfc0 = 0b00000'000000,
cfc0 = 0b00010'000000,
mtc0 = 0b00100'000000,
ctc0 = 0b00110'000000,
bc0c = 0b01000'000000,
tlbr = 0b10000'000001,
tlbwi = 0b10000'000010,
tlbwr = 0b10000'000100,
tlbp = 0b10000'001000,
rfe = 0b10000'010000,
mfcn = 0b0000,
cfcn = 0b0010,
mtcn = 0b0100,
ctcn = 0b0110,
bcnc = 0b1000,
};
enum class Cop2Instruction : u32 // 25:21
enum class Cop0Instruction : u32
{
mfc2 = 0b0000,
cfc2 = 0b0010,
mtc2 = 0b0100,
ctc2 = 0b0110,
bc2c = 0b1000,
tlbr = 0x01,
tlbwi = 0x02,
tlbwr = 0x04,
tlbp = 0x08,
rfe = 0x10,
};
union Instruction
@ -175,15 +178,9 @@ union Instruction
bool IsCommonInstruction() const { return (bits & (UINT32_C(1) << 25)) == 0; }
Cop0Instruction cop0_op() const
{
return static_cast<Cop0Instruction>(((bits >> 15) & UINT32_C(0b11111000000)) | (bits & UINT32_C(0b111111)));
}
CopCommonInstruction CommonOp() const { return static_cast<CopCommonInstruction>((bits >> 21) & UINT32_C(0b1111)); }
Cop2Instruction cop2_op() const
{
return static_cast<Cop2Instruction>((bits >> 21) & UINT32_C(0b1111));
}
Cop0Instruction Cop0Op() const { return static_cast<Cop0Instruction>(bits & UINT32_C(0x3F)); }
} cop;
};