CPU: Add branch hints to interpreter dispatch

Eliminates the bounds check in the switch loop.
pull/3750/head
Stenzek 4 months ago
parent 18f581ef93
commit 667e1147a4
No known key found for this signature in database

@ -894,13 +894,17 @@ restart_instruction:
if (inst.bits == 0) if (inst.bits == 0)
return; return;
switch (inst.op) #define BEGIN_INSTRUCTION(name) case static_cast<u8>(InstructionOp::name):
#define BEGIN_FUNCT_INSTRUCTION(name) case static_cast<u8>(InstructionFunct::name):
#define END_INSTRUCTION() break;
switch (static_cast<u8>(inst.op.GetValue()))
{ {
case InstructionOp::funct: BEGIN_INSTRUCTION(funct)
{ {
switch (inst.r.funct) switch (static_cast<u8>(inst.r.funct.GetValue()))
{ {
case InstructionFunct::sll: BEGIN_FUNCT_INSTRUCTION(sll)
{ {
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
const u32 rdVal = rtVal << inst.r.shamt; const u32 rdVal = rtVal << inst.r.shamt;
@ -909,9 +913,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SLL(inst, rtVal); PGXP::CPU_SLL(inst, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::srl: BEGIN_FUNCT_INSTRUCTION(srl)
{ {
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
const u32 rdVal = rtVal >> inst.r.shamt; const u32 rdVal = rtVal >> inst.r.shamt;
@ -922,7 +926,7 @@ restart_instruction:
} }
break; break;
case InstructionFunct::sra: BEGIN_FUNCT_INSTRUCTION(sra)
{ {
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
const u32 rdVal = static_cast<u32>(static_cast<s32>(rtVal) >> inst.r.shamt); const u32 rdVal = static_cast<u32>(static_cast<s32>(rtVal) >> inst.r.shamt);
@ -931,9 +935,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SRA(inst, rtVal); PGXP::CPU_SRA(inst, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::sllv: BEGIN_FUNCT_INSTRUCTION(sllv)
{ {
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F); const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F);
@ -943,9 +947,9 @@ restart_instruction:
WriteReg(inst.r.rd, rdVal); WriteReg(inst.r.rd, rdVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::srlv: BEGIN_FUNCT_INSTRUCTION(srlv)
{ {
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F); const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F);
@ -955,9 +959,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SRLV(inst, rtVal, shamt); PGXP::CPU_SRLV(inst, rtVal, shamt);
} }
break; END_INSTRUCTION()
case InstructionFunct::srav: BEGIN_FUNCT_INSTRUCTION(srav)
{ {
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F); const u32 shamt = ReadReg(inst.r.rs) & UINT32_C(0x1F);
@ -967,9 +971,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SRAV(inst, rtVal, shamt); PGXP::CPU_SRAV(inst, rtVal, shamt);
} }
break; END_INSTRUCTION()
case InstructionFunct::and_: BEGIN_FUNCT_INSTRUCTION(and_)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -979,9 +983,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_AND_(inst, rsVal, rtVal); PGXP::CPU_AND_(inst, rsVal, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::or_: BEGIN_FUNCT_INSTRUCTION(or_)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -993,9 +997,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt); PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
} }
break; END_INSTRUCTION()
case InstructionFunct::xor_: BEGIN_FUNCT_INSTRUCTION(xor_)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1007,9 +1011,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt); PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
} }
break; END_INSTRUCTION()
case InstructionFunct::nor: BEGIN_FUNCT_INSTRUCTION(nor)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1019,9 +1023,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_NOR(inst, rsVal, rtVal); PGXP::CPU_NOR(inst, rsVal, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::add: BEGIN_FUNCT_INSTRUCTION(add)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1039,9 +1043,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt); PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
} }
break; END_INSTRUCTION()
case InstructionFunct::addu: BEGIN_FUNCT_INSTRUCTION(addu)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1053,9 +1057,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt); PGXP::TryMove(inst.r.rd, inst.r.rs, inst.r.rt);
} }
break; END_INSTRUCTION()
case InstructionFunct::sub: BEGIN_FUNCT_INSTRUCTION(sub)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1071,9 +1075,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SUB(inst, rsVal, rtVal); PGXP::CPU_SUB(inst, rsVal, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::subu: BEGIN_FUNCT_INSTRUCTION(subu)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1083,9 +1087,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SUB(inst, rsVal, rtVal); PGXP::CPU_SUB(inst, rsVal, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::slt: BEGIN_FUNCT_INSTRUCTION(slt)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1095,9 +1099,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SLT(inst, rsVal, rtVal); PGXP::CPU_SLT(inst, rsVal, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::sltu: BEGIN_FUNCT_INSTRUCTION(sltu)
{ {
const u32 rsVal = ReadReg(inst.r.rs); const u32 rsVal = ReadReg(inst.r.rs);
const u32 rtVal = ReadReg(inst.r.rt); const u32 rtVal = ReadReg(inst.r.rt);
@ -1107,9 +1111,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SLTU(inst, rsVal, rtVal); PGXP::CPU_SLTU(inst, rsVal, rtVal);
} }
break; END_INSTRUCTION()
case InstructionFunct::mfhi: BEGIN_FUNCT_INSTRUCTION(mfhi)
{ {
const u32 value = g_state.regs.hi; const u32 value = g_state.regs.hi;
WriteReg(inst.r.rd, value); WriteReg(inst.r.rd, value);
@ -1119,9 +1123,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::hi), value); PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::hi), value);
} }
break; END_INSTRUCTION()
case InstructionFunct::mthi: BEGIN_FUNCT_INSTRUCTION(mthi)
{ {
const u32 value = ReadReg(inst.r.rs); const u32 value = ReadReg(inst.r.rs);
g_state.regs.hi = value; g_state.regs.hi = value;
@ -1131,9 +1135,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(Reg::hi), static_cast<u32>(inst.r.rs.GetValue()), value); PGXP::CPU_MOVE(static_cast<u32>(Reg::hi), static_cast<u32>(inst.r.rs.GetValue()), value);
} }
break; END_INSTRUCTION()
case InstructionFunct::mflo: BEGIN_FUNCT_INSTRUCTION(mflo)
{ {
const u32 value = g_state.regs.lo; const u32 value = g_state.regs.lo;
WriteReg(inst.r.rd, value); WriteReg(inst.r.rd, value);
@ -1143,9 +1147,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::lo), value); PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::lo), value);
} }
break; END_INSTRUCTION()
case InstructionFunct::mtlo: BEGIN_FUNCT_INSTRUCTION(mtlo)
{ {
const u32 value = ReadReg(inst.r.rs); const u32 value = ReadReg(inst.r.rs);
g_state.regs.lo = value; g_state.regs.lo = value;
@ -1155,9 +1159,9 @@ restart_instruction:
if constexpr (pgxp_mode == PGXPMode::CPU) if constexpr (pgxp_mode == PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(Reg::lo), static_cast<u32>(inst.r.rs.GetValue()), value); PGXP::CPU_MOVE(static_cast<u32>(Reg::lo), static_cast<u32>(inst.r.rs.GetValue()), value);
} }
break; END_INSTRUCTION()
case InstructionFunct::mult: BEGIN_FUNCT_INSTRUCTION(mult)
{ {
const u32 lhs = ReadReg(inst.r.rs); const u32 lhs = ReadReg(inst.r.rs);
const u32 rhs = ReadReg(inst.r.rt); const u32 rhs = ReadReg(inst.r.rt);
@ -1173,9 +1177,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MULT(inst, lhs, rhs); PGXP::CPU_MULT(inst, lhs, rhs);
} }
break; END_INSTRUCTION()
case InstructionFunct::multu: BEGIN_FUNCT_INSTRUCTION(multu)
{ {
const u32 lhs = ReadReg(inst.r.rs); const u32 lhs = ReadReg(inst.r.rs);
const u32 rhs = ReadReg(inst.r.rt); const u32 rhs = ReadReg(inst.r.rt);
@ -1190,20 +1194,20 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MULTU(inst, lhs, rhs); PGXP::CPU_MULTU(inst, lhs, rhs);
} }
break; END_INSTRUCTION()
case InstructionFunct::div: BEGIN_FUNCT_INSTRUCTION(div)
{ {
const s32 num = static_cast<s32>(ReadReg(inst.r.rs)); const s32 num = static_cast<s32>(ReadReg(inst.r.rs));
const s32 denom = static_cast<s32>(ReadReg(inst.r.rt)); const s32 denom = static_cast<s32>(ReadReg(inst.r.rt));
if (denom == 0) if (denom == 0) [[unlikely]]
{ {
// divide by zero // divide by zero
g_state.regs.lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1); g_state.regs.lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1);
g_state.regs.hi = static_cast<u32>(num); g_state.regs.hi = static_cast<u32>(num);
} }
else if (static_cast<u32>(num) == UINT32_C(0x80000000) && denom == -1) else if (static_cast<u32>(num) == UINT32_C(0x80000000) && denom == -1) [[unlikely]]
{ {
// unrepresentable // unrepresentable
g_state.regs.lo = UINT32_C(0x80000000); g_state.regs.lo = UINT32_C(0x80000000);
@ -1221,14 +1225,14 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_DIV(inst, num, denom); PGXP::CPU_DIV(inst, num, denom);
} }
break; END_INSTRUCTION()
case InstructionFunct::divu: BEGIN_FUNCT_INSTRUCTION(divu)
{ {
const u32 num = ReadReg(inst.r.rs); const u32 num = ReadReg(inst.r.rs);
const u32 denom = ReadReg(inst.r.rt); const u32 denom = ReadReg(inst.r.rt);
if (denom == 0) if (denom == 0) [[unlikely]]
{ {
// divide by zero // divide by zero
g_state.regs.lo = UINT32_C(0xFFFFFFFF); g_state.regs.lo = UINT32_C(0xFFFFFFFF);
@ -1246,50 +1250,61 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_DIVU(inst, num, denom); PGXP::CPU_DIVU(inst, num, denom);
} }
break; END_INSTRUCTION()
case InstructionFunct::jr: BEGIN_FUNCT_INSTRUCTION(jr)
{ {
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
const u32 target = ReadReg(inst.r.rs); const u32 target = ReadReg(inst.r.rs);
Branch(target); Branch(target);
} }
break; END_INSTRUCTION()
case InstructionFunct::jalr: BEGIN_FUNCT_INSTRUCTION(jalr)
{ {
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
const u32 target = ReadReg(inst.r.rs); const u32 target = ReadReg(inst.r.rs);
WriteReg(inst.r.rd, g_state.npc); WriteReg(inst.r.rd, g_state.npc);
Branch(target); Branch(target);
} }
break; END_INSTRUCTION()
case InstructionFunct::syscall: BEGIN_FUNCT_INSTRUCTION(syscall)
{ {
RaiseException(Exception::Syscall); RaiseException(Exception::Syscall);
} }
break; END_INSTRUCTION()
case InstructionFunct::break_: BEGIN_FUNCT_INSTRUCTION(break_)
{ {
RaiseBreakException(Cop0Registers::CAUSE::MakeValueForException( RaiseBreakException(Cop0Registers::CAUSE::MakeValueForException(
Exception::BP, g_state.current_instruction_in_branch_delay_slot, Exception::BP, g_state.current_instruction_in_branch_delay_slot,
g_state.current_instruction_was_branch_taken, g_state.current_instruction.cop.cop_n), g_state.current_instruction_was_branch_taken, g_state.current_instruction.cop.cop_n),
g_state.current_instruction_pc, g_state.current_instruction.bits); g_state.current_instruction_pc, g_state.current_instruction.bits);
} }
break; END_INSTRUCTION()
default: // clang-format off
{ [[unlikely]] case 1: [[unlikely]] case 5: [[unlikely]] case 10: [[unlikely]] case 11:
[[unlikely]] case 14: [[unlikely]] case 15: [[unlikely]] case 20: [[unlikely]] case 21:
[[unlikely]] case 22: [[unlikely]] case 23: [[unlikely]] case 28: [[unlikely]] case 29:
[[unlikely]] case 30: [[unlikely]] case 31: [[unlikely]] case 40: [[unlikely]] case 41:
[[unlikely]] case 44: [[unlikely]] case 45: [[unlikely]] case 46: [[unlikely]] case 47:
[[unlikely]] case 48: [[unlikely]] case 49: [[unlikely]] case 50: [[unlikely]] case 51:
[[unlikely]] case 52: [[unlikely]] case 53: [[unlikely]] case 54: [[unlikely]] case 55:
[[unlikely]] case 56: [[unlikely]] case 57: [[unlikely]] case 58: [[unlikely]] case 59:
[[unlikely]] case 60: [[unlikely]] case 61: [[unlikely]] case 62: [[unlikely]] case 63:
{ // clang-format on
RaiseException(Exception::RI); RaiseException(Exception::RI);
break; break;
} }
DefaultCaseIsUnreachable();
} }
} }
break; END_INSTRUCTION()
case InstructionOp::lui: BEGIN_INSTRUCTION(lui)
{ {
const u32 value = inst.i.imm_zext32() << 16; const u32 value = inst.i.imm_zext32() << 16;
WriteReg(inst.i.rt, value); WriteReg(inst.i.rt, value);
@ -1297,9 +1312,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_LUI(inst); PGXP::CPU_LUI(inst);
} }
break; END_INSTRUCTION()
case InstructionOp::andi: BEGIN_INSTRUCTION(andi)
{ {
const u32 rsVal = ReadReg(inst.i.rs); const u32 rsVal = ReadReg(inst.i.rs);
const u32 new_value = rsVal & inst.i.imm_zext32(); const u32 new_value = rsVal & inst.i.imm_zext32();
@ -1308,9 +1323,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_ANDI(inst, rsVal); PGXP::CPU_ANDI(inst, rsVal);
} }
break; END_INSTRUCTION()
case InstructionOp::ori: BEGIN_INSTRUCTION(ori)
{ {
const u32 rsVal = ReadReg(inst.i.rs); const u32 rsVal = ReadReg(inst.i.rs);
const u32 imm = inst.i.imm_zext32(); const u32 imm = inst.i.imm_zext32();
@ -1322,9 +1337,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm); PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
} }
break; END_INSTRUCTION()
case InstructionOp::xori: BEGIN_INSTRUCTION(xori)
{ {
const u32 rsVal = ReadReg(inst.i.rs); const u32 rsVal = ReadReg(inst.i.rs);
const u32 imm = inst.i.imm_zext32(); const u32 imm = inst.i.imm_zext32();
@ -1336,9 +1351,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm); PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
} }
break; END_INSTRUCTION()
case InstructionOp::addi: BEGIN_INSTRUCTION(addi)
{ {
const u32 rsVal = ReadReg(inst.i.rs); const u32 rsVal = ReadReg(inst.i.rs);
const u32 imm = inst.i.imm_sext32(); const u32 imm = inst.i.imm_sext32();
@ -1356,9 +1371,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm); PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
} }
break; END_INSTRUCTION()
case InstructionOp::addiu: BEGIN_INSTRUCTION(addiu)
{ {
const u32 rsVal = ReadReg(inst.i.rs); const u32 rsVal = ReadReg(inst.i.rs);
const u32 imm = inst.i.imm_sext32(); const u32 imm = inst.i.imm_sext32();
@ -1370,9 +1385,9 @@ restart_instruction:
else if constexpr (pgxp_mode >= PGXPMode::Memory) else if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm); PGXP::TryMoveImm(inst.r.rd, inst.r.rs, imm);
} }
break; END_INSTRUCTION()
case InstructionOp::slti: BEGIN_INSTRUCTION(slti)
{ {
const u32 rsVal = ReadReg(inst.i.rs); const u32 rsVal = ReadReg(inst.i.rs);
const u32 result = BoolToUInt32(static_cast<s32>(rsVal) < static_cast<s32>(inst.i.imm_sext32())); const u32 result = BoolToUInt32(static_cast<s32>(rsVal) < static_cast<s32>(inst.i.imm_sext32()));
@ -1381,9 +1396,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SLTI(inst, rsVal); PGXP::CPU_SLTI(inst, rsVal);
} }
break; END_INSTRUCTION()
case InstructionOp::sltiu: BEGIN_INSTRUCTION(sltiu)
{ {
const u32 result = BoolToUInt32(ReadReg(inst.i.rs) < inst.i.imm_sext32()); const u32 result = BoolToUInt32(ReadReg(inst.i.rs) < inst.i.imm_sext32());
WriteReg(inst.i.rt, result); WriteReg(inst.i.rt, result);
@ -1391,9 +1406,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_SLTIU(inst, ReadReg(inst.i.rs)); PGXP::CPU_SLTIU(inst, ReadReg(inst.i.rs));
} }
break; END_INSTRUCTION()
case InstructionOp::lb: BEGIN_INSTRUCTION(lb)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1413,9 +1428,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_LBx(inst, addr, sxvalue); PGXP::CPU_LBx(inst, addr, sxvalue);
} }
break; END_INSTRUCTION()
case InstructionOp::lh: BEGIN_INSTRUCTION(lh)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1434,9 +1449,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_LH(inst, addr, sxvalue); PGXP::CPU_LH(inst, addr, sxvalue);
} }
break; END_INSTRUCTION()
case InstructionOp::lw: BEGIN_INSTRUCTION(lw)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1454,9 +1469,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_LW(inst, addr, value); PGXP::CPU_LW(inst, addr, value);
} }
break; END_INSTRUCTION()
case InstructionOp::lbu: BEGIN_INSTRUCTION(lbu)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1475,9 +1490,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_LBx(inst, addr, zxvalue); PGXP::CPU_LBx(inst, addr, zxvalue);
} }
break; END_INSTRUCTION()
case InstructionOp::lhu: BEGIN_INSTRUCTION(lhu)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1496,10 +1511,10 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_LHU(inst, addr, zxvalue); PGXP::CPU_LHU(inst, addr, zxvalue);
} }
break; END_INSTRUCTION()
case InstructionOp::lwl: BEGIN_INSTRUCTION(lwl)
case InstructionOp::lwr: BEGIN_INSTRUCTION(lwr)
{ {
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);
@ -1533,9 +1548,9 @@ restart_instruction:
WriteRegDelayed(inst.i.rt, new_value); WriteRegDelayed(inst.i.rt, new_value);
} }
break; END_INSTRUCTION() break;
case InstructionOp::sb: BEGIN_INSTRUCTION(sb)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1550,9 +1565,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_SB(inst, addr, value); PGXP::CPU_SB(inst, addr, value);
} }
break; END_INSTRUCTION()
case InstructionOp::sh: BEGIN_INSTRUCTION(sh)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1567,9 +1582,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_SH(inst, addr, value); PGXP::CPU_SH(inst, addr, value);
} }
break; END_INSTRUCTION()
case InstructionOp::sw: BEGIN_INSTRUCTION(sw)
{ {
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32(); const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
if constexpr (debug) if constexpr (debug)
@ -1584,10 +1599,10 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_SW(inst, addr, value); PGXP::CPU_SW(inst, addr, value);
} }
break; END_INSTRUCTION()
case InstructionOp::swl: BEGIN_INSTRUCTION(swl)
case InstructionOp::swr: BEGIN_INSTRUCTION(swr)
{ {
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);
@ -1620,24 +1635,24 @@ restart_instruction:
WriteMemoryWord(aligned_addr, new_value); WriteMemoryWord(aligned_addr, new_value);
} }
break; END_INSTRUCTION()
case InstructionOp::j: BEGIN_INSTRUCTION(j)
{ {
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
Branch((g_state.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2)); Branch((g_state.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
} }
break; END_INSTRUCTION()
case InstructionOp::jal: BEGIN_INSTRUCTION(jal)
{ {
WriteReg(Reg::ra, g_state.npc); WriteReg(Reg::ra, g_state.npc);
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
Branch((g_state.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2)); Branch((g_state.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
} }
break; END_INSTRUCTION()
case InstructionOp::beq: BEGIN_INSTRUCTION(beq)
{ {
// We're still flagged as a branch delay slot even if the branch isn't taken. // We're still flagged as a branch delay slot even if the branch isn't taken.
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
@ -1645,36 +1660,36 @@ restart_instruction:
if (branch) if (branch)
Branch(g_state.pc + (inst.i.imm_sext32() << 2)); Branch(g_state.pc + (inst.i.imm_sext32() << 2));
} }
break; END_INSTRUCTION()
case InstructionOp::bne: BEGIN_INSTRUCTION(bne)
{ {
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
const bool branch = (ReadReg(inst.i.rs) != ReadReg(inst.i.rt)); const bool branch = (ReadReg(inst.i.rs) != ReadReg(inst.i.rt));
if (branch) if (branch)
Branch(g_state.pc + (inst.i.imm_sext32() << 2)); Branch(g_state.pc + (inst.i.imm_sext32() << 2));
} }
break; END_INSTRUCTION()
case InstructionOp::bgtz: BEGIN_INSTRUCTION(bgtz)
{ {
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) > 0); const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) > 0);
if (branch) if (branch)
Branch(g_state.pc + (inst.i.imm_sext32() << 2)); Branch(g_state.pc + (inst.i.imm_sext32() << 2));
} }
break; END_INSTRUCTION()
case InstructionOp::blez: BEGIN_INSTRUCTION(blez)
{ {
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) <= 0); const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) <= 0);
if (branch) if (branch)
Branch(g_state.pc + (inst.i.imm_sext32() << 2)); Branch(g_state.pc + (inst.i.imm_sext32() << 2));
} }
break; END_INSTRUCTION()
case InstructionOp::b: BEGIN_INSTRUCTION(b)
{ {
g_state.next_instruction_is_branch_delay_slot = true; g_state.next_instruction_is_branch_delay_slot = true;
const u8 rt = static_cast<u8>(inst.i.rt.GetValue()); const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
@ -1691,11 +1706,11 @@ restart_instruction:
if (branch) if (branch)
Branch(g_state.pc + (inst.i.imm_sext32() << 2)); Branch(g_state.pc + (inst.i.imm_sext32() << 2));
} }
break; END_INSTRUCTION()
case InstructionOp::cop0: BEGIN_INSTRUCTION(cop0)
{ {
if (InUserMode() && !g_state.cop0_regs.sr.CU0) if (InUserMode() && !g_state.cop0_regs.sr.CU0) [[unlikely]]
{ {
WARNING_LOG("Coprocessor 0 not present in user mode"); WARNING_LOG("Coprocessor 0 not present in user mode");
RaiseException(Exception::CpU); RaiseException(Exception::CpU);
@ -1856,9 +1871,8 @@ restart_instruction:
} }
break; break;
default: [[unlikely]] default:
[[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc, ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc, inst.bits);
inst.bits);
break; break;
} }
} }
@ -1875,23 +1889,22 @@ restart_instruction:
} }
break; break;
case Cop0Instruction::tlbr: [[unlikely]] case Cop0Instruction::tlbr:
case Cop0Instruction::tlbwi: [[unlikely]] case Cop0Instruction::tlbwi:
case Cop0Instruction::tlbwr: [[unlikely]] case Cop0Instruction::tlbwr:
case Cop0Instruction::tlbp: [[unlikely]] case Cop0Instruction::tlbp:
RaiseException(Exception::RI); RaiseException(Exception::RI);
return; return;
default: [[unlikely]] default:
[[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc, ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc, inst.bits);
inst.bits);
break; break;
} }
} }
} }
break; END_INSTRUCTION()
case InstructionOp::cop2: BEGIN_INSTRUCTION(cop2)
{ {
if (!g_state.cop0_regs.sr.CE2) [[unlikely]] if (!g_state.cop0_regs.sr.CE2) [[unlikely]]
{ {
@ -1903,9 +1916,9 @@ restart_instruction:
if (inst.cop.IsCommonInstruction()) if (inst.cop.IsCommonInstruction())
{ {
// TODO: Combine with cop0. // TODO: Combine with cop0.
switch (inst.cop.CommonOp()) switch (static_cast<u8>(inst.cop.CommonOp()))
{ {
case CopCommonInstruction::cfcn: case static_cast<u8>(CopCommonInstruction::cfcn):
{ {
StallUntilGTEComplete(); StallUntilGTEComplete();
@ -1917,7 +1930,7 @@ restart_instruction:
} }
break; break;
case CopCommonInstruction::ctcn: case static_cast<u8>(CopCommonInstruction::ctcn):
{ {
const u32 value = ReadReg(inst.r.rt); const u32 value = ReadReg(inst.r.rt);
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32, value); GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32, value);
@ -1927,7 +1940,7 @@ restart_instruction:
} }
break; break;
case CopCommonInstruction::mfcn: case static_cast<u8>(CopCommonInstruction::mfcn):
{ {
StallUntilGTEComplete(); StallUntilGTEComplete();
@ -1939,7 +1952,7 @@ restart_instruction:
} }
break; break;
case CopCommonInstruction::mtcn: case static_cast<u8>(CopCommonInstruction::mtcn):
{ {
const u32 value = ReadReg(inst.r.rt); const u32 value = ReadReg(inst.r.rt);
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()), value); GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()), value);
@ -1949,10 +1962,15 @@ restart_instruction:
} }
break; break;
default: // clang-format off
[[unlikely]] ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc, [[unlikely]] case 1: [[unlikely]] case 3: [[unlikely]] case 5: [[unlikely]] case 7:
inst.bits); [[unlikely]] case 8: [[unlikely]] case 9: [[unlikely]] case 10: [[unlikely]] case 11:
[[unlikely]] case 12: [[unlikely]] case 13: [[unlikely]] case 14: [[unlikely]] case 15:
// clang-format on
ERROR_LOG("Unhandled instruction at {:08X}: {:08X}", g_state.current_instruction_pc, inst.bits);
break; break;
DefaultCaseIsUnreachable();
} }
} }
else else
@ -1961,9 +1979,9 @@ restart_instruction:
GTE::ExecuteInstruction(inst.bits); GTE::ExecuteInstruction(inst.bits);
} }
} }
break; END_INSTRUCTION()
case InstructionOp::lwc2: BEGIN_INSTRUCTION(lwc2)
{ {
if (!g_state.cop0_regs.sr.CE2) [[unlikely]] if (!g_state.cop0_regs.sr.CE2) [[unlikely]]
{ {
@ -1980,7 +1998,7 @@ restart_instruction:
} }
u32 value; u32 value;
if (!ReadMemoryWord(addr, &value)) if (!ReadMemoryWord(addr, &value)) [[unlikely]]
return; return;
GTE::WriteRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())), value); GTE::WriteRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())), value);
@ -1988,9 +2006,9 @@ restart_instruction:
if constexpr (pgxp_mode >= PGXPMode::Memory) if constexpr (pgxp_mode >= PGXPMode::Memory)
PGXP::CPU_LWC2(inst, addr, value); PGXP::CPU_LWC2(inst, addr, value);
} }
break; END_INSTRUCTION()
case InstructionOp::swc2: BEGIN_INSTRUCTION(swc2)
{ {
if (!g_state.cop0_regs.sr.CE2) [[unlikely]] if (!g_state.cop0_regs.sr.CE2) [[unlikely]]
{ {
@ -2016,16 +2034,16 @@ restart_instruction:
} }
break; break;
// cop1/cop3 are essentially no-ops // cop1/cop3 are essentially no-ops
case InstructionOp::cop1: BEGIN_INSTRUCTION(cop1)
case InstructionOp::cop3: BEGIN_INSTRUCTION(cop3)
{ {
} }
break; END_INSTRUCTION()
case InstructionOp::lwc0: BEGIN_INSTRUCTION(lwc0)
case InstructionOp::lwc1: BEGIN_INSTRUCTION(lwc1)
case InstructionOp::lwc3: BEGIN_INSTRUCTION(lwc3)
{ {
// todo: check enable // todo: check enable
// lwc0/1/3 should still perform the memory read, but discard the result // lwc0/1/3 should still perform the memory read, but discard the result
@ -2039,11 +2057,11 @@ restart_instruction:
u32 value; u32 value;
ReadMemoryWord(addr, &value); ReadMemoryWord(addr, &value);
} }
break; END_INSTRUCTION()
case InstructionOp::swc0: BEGIN_INSTRUCTION(swc0)
case InstructionOp::swc1: BEGIN_INSTRUCTION(swc1)
case InstructionOp::swc3: BEGIN_INSTRUCTION(swc3)
{ {
// todo: check enable // todo: check enable
// lwc0/1/3 should still perform the memory read, but discard the result // lwc0/1/3 should still perform the memory read, but discard the result
@ -2056,12 +2074,17 @@ restart_instruction:
WriteMemoryWord(addr, 0); WriteMemoryWord(addr, 0);
} }
break; END_INSTRUCTION()
// everything else is reserved/invalid // everything else is reserved/invalid
[[unlikely]] // clang-format off
default: [[unlikely]] case 20: [[unlikely]] case 21: [[unlikely]] case 22: [[unlikely]] case 23:
{ [[unlikely]] case 24: [[unlikely]] case 25: [[unlikely]] case 26: [[unlikely]] case 27:
[[unlikely]] case 28: [[unlikely]] case 29: [[unlikely]] case 30: [[unlikely]] case 31:
[[unlikely]] case 39: [[unlikely]] case 44: [[unlikely]] case 45: [[unlikely]] case 47:
[[unlikely]] case 52: [[unlikely]] case 53: [[unlikely]] case 54: [[unlikely]] case 55:
[[unlikely]] case 60: [[unlikely]] case 61: [[unlikely]] case 62: [[unlikely]] case 63:
{ // clang-format on
u32 ram_value; u32 ram_value;
if (SafeReadInstruction(g_state.current_instruction_pc, &ram_value) && if (SafeReadInstruction(g_state.current_instruction_pc, &ram_value) &&
ram_value != g_state.current_instruction.bits) [[unlikely]] ram_value != g_state.current_instruction.bits) [[unlikely]]
@ -2075,6 +2098,8 @@ restart_instruction:
RaiseException(Exception::RI); RaiseException(Exception::RI);
} }
break; break;
DefaultCaseIsUnreachable();
} }
} }

Loading…
Cancel
Save