|
|
|
@ -186,7 +186,8 @@ bool CPU::IsMemoryStoreInstruction(const Instruction instruction)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<VirtualMemoryAddress> CPU::GetLoadStoreEffectiveAddress(const Instruction instruction, const Registers* regs)
|
|
|
|
|
std::optional<VirtualMemoryAddress> CPU::GetLoadStoreEffectiveAddress(const Instruction instruction,
|
|
|
|
|
const Registers* regs)
|
|
|
|
|
{
|
|
|
|
|
switch (instruction.op)
|
|
|
|
|
{
|
|
|
|
@ -265,109 +266,107 @@ bool CPU::IsExitBlockInstruction(const Instruction instruction)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CPU::CanInstructionTrap(const Instruction instruction, bool in_user_mode)
|
|
|
|
|
bool CPU::IsValidInstruction(const Instruction instruction)
|
|
|
|
|
{
|
|
|
|
|
switch (instruction.op)
|
|
|
|
|
{
|
|
|
|
|
case InstructionOp::lui:
|
|
|
|
|
case InstructionOp::andi:
|
|
|
|
|
case InstructionOp::ori:
|
|
|
|
|
case InstructionOp::xori:
|
|
|
|
|
case InstructionOp::addiu:
|
|
|
|
|
case InstructionOp::slti:
|
|
|
|
|
case InstructionOp::sltiu:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case InstructionOp::cop0:
|
|
|
|
|
case InstructionOp::cop2:
|
|
|
|
|
case InstructionOp::lwc2:
|
|
|
|
|
case InstructionOp::swc2:
|
|
|
|
|
return in_user_mode;
|
|
|
|
|
|
|
|
|
|
// swc0/lwc0/cop1/cop3 are essentially no-ops
|
|
|
|
|
case InstructionOp::cop1:
|
|
|
|
|
case InstructionOp::cop3:
|
|
|
|
|
case InstructionOp::lwc0:
|
|
|
|
|
case InstructionOp::lwc1:
|
|
|
|
|
case InstructionOp::lwc3:
|
|
|
|
|
case InstructionOp::swc0:
|
|
|
|
|
case InstructionOp::swc1:
|
|
|
|
|
case InstructionOp::swc3:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case InstructionOp::addi:
|
|
|
|
|
case InstructionOp::lb:
|
|
|
|
|
case InstructionOp::lh:
|
|
|
|
|
case InstructionOp::lw:
|
|
|
|
|
case InstructionOp::lbu:
|
|
|
|
|
case InstructionOp::lhu:
|
|
|
|
|
case InstructionOp::lwl:
|
|
|
|
|
case InstructionOp::lwr:
|
|
|
|
|
case InstructionOp::sb:
|
|
|
|
|
case InstructionOp::sh:
|
|
|
|
|
case InstructionOp::sw:
|
|
|
|
|
case InstructionOp::swl:
|
|
|
|
|
case InstructionOp::swr:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// These can fault on the branch address. Perhaps we should move this to the next instruction?
|
|
|
|
|
case InstructionOp::j:
|
|
|
|
|
case InstructionOp::jal:
|
|
|
|
|
case InstructionOp::b:
|
|
|
|
|
case InstructionOp::beq:
|
|
|
|
|
case InstructionOp::bgtz:
|
|
|
|
|
case InstructionOp::blez:
|
|
|
|
|
case InstructionOp::bne:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case InstructionOp::funct:
|
|
|
|
|
{
|
|
|
|
|
switch (instruction.r.funct)
|
|
|
|
|
{
|
|
|
|
|
case InstructionFunct::sll:
|
|
|
|
|
case InstructionFunct::srl:
|
|
|
|
|
case InstructionFunct::sra:
|
|
|
|
|
case InstructionFunct::sllv:
|
|
|
|
|
case InstructionFunct::srlv:
|
|
|
|
|
case InstructionFunct::srav:
|
|
|
|
|
case InstructionFunct::and_:
|
|
|
|
|
case InstructionFunct::or_:
|
|
|
|
|
case InstructionFunct::xor_:
|
|
|
|
|
case InstructionFunct::nor:
|
|
|
|
|
case InstructionFunct::addu:
|
|
|
|
|
case InstructionFunct::subu:
|
|
|
|
|
case InstructionFunct::slt:
|
|
|
|
|
case InstructionFunct::sltu:
|
|
|
|
|
case InstructionFunct::mfhi:
|
|
|
|
|
case InstructionFunct::mthi:
|
|
|
|
|
case InstructionFunct::mflo:
|
|
|
|
|
case InstructionFunct::mtlo:
|
|
|
|
|
case InstructionFunct::mult:
|
|
|
|
|
case InstructionFunct::multu:
|
|
|
|
|
case InstructionFunct::div:
|
|
|
|
|
case InstructionFunct::divu:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
case InstructionFunct::jr:
|
|
|
|
|
case InstructionFunct::jalr:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case InstructionFunct::add:
|
|
|
|
|
case InstructionFunct::sub:
|
|
|
|
|
case InstructionFunct::syscall:
|
|
|
|
|
case InstructionFunct::break_:
|
|
|
|
|
default:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CPU::IsInvalidInstruction(const Instruction instruction)
|
|
|
|
|
{
|
|
|
|
|
// TODO
|
|
|
|
|
return true;
|
|
|
|
|
// No constexpr std::bitset until C++23 :(
|
|
|
|
|
static constexpr const std::array<u32, 64 / 32> valid_op_map = []() constexpr {
|
|
|
|
|
std::array<u32, 64 / 32> ret = {};
|
|
|
|
|
|
|
|
|
|
#define SET(op) ret[static_cast<size_t>(op) / 32] |= (1u << (static_cast<size_t>(op) % 32));
|
|
|
|
|
|
|
|
|
|
SET(InstructionOp::b);
|
|
|
|
|
SET(InstructionOp::j);
|
|
|
|
|
SET(InstructionOp::jal);
|
|
|
|
|
SET(InstructionOp::beq);
|
|
|
|
|
SET(InstructionOp::bne);
|
|
|
|
|
SET(InstructionOp::blez);
|
|
|
|
|
SET(InstructionOp::bgtz);
|
|
|
|
|
SET(InstructionOp::addi);
|
|
|
|
|
SET(InstructionOp::addiu);
|
|
|
|
|
SET(InstructionOp::slti);
|
|
|
|
|
SET(InstructionOp::sltiu);
|
|
|
|
|
SET(InstructionOp::andi);
|
|
|
|
|
SET(InstructionOp::ori);
|
|
|
|
|
SET(InstructionOp::xori);
|
|
|
|
|
SET(InstructionOp::lui);
|
|
|
|
|
|
|
|
|
|
// Invalid COP0-3 ops don't raise #RI?
|
|
|
|
|
SET(InstructionOp::cop0);
|
|
|
|
|
SET(InstructionOp::cop1);
|
|
|
|
|
SET(InstructionOp::cop2);
|
|
|
|
|
SET(InstructionOp::cop3);
|
|
|
|
|
|
|
|
|
|
SET(InstructionOp::lb);
|
|
|
|
|
SET(InstructionOp::lh);
|
|
|
|
|
SET(InstructionOp::lwl);
|
|
|
|
|
SET(InstructionOp::lw);
|
|
|
|
|
SET(InstructionOp::lbu);
|
|
|
|
|
SET(InstructionOp::lhu);
|
|
|
|
|
SET(InstructionOp::lwr);
|
|
|
|
|
SET(InstructionOp::sb);
|
|
|
|
|
SET(InstructionOp::sh);
|
|
|
|
|
SET(InstructionOp::swl);
|
|
|
|
|
SET(InstructionOp::sw);
|
|
|
|
|
SET(InstructionOp::swr);
|
|
|
|
|
SET(InstructionOp::lwc0);
|
|
|
|
|
SET(InstructionOp::lwc1);
|
|
|
|
|
SET(InstructionOp::lwc2);
|
|
|
|
|
SET(InstructionOp::lwc3);
|
|
|
|
|
SET(InstructionOp::swc0);
|
|
|
|
|
SET(InstructionOp::swc1);
|
|
|
|
|
SET(InstructionOp::swc2);
|
|
|
|
|
SET(InstructionOp::swc3);
|
|
|
|
|
|
|
|
|
|
#undef SET
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
static constexpr const std::array<u32, 64 / 32> valid_func_map = []() constexpr {
|
|
|
|
|
std::array<u32, 64 / 32> ret = {};
|
|
|
|
|
|
|
|
|
|
#define SET(op) ret[static_cast<size_t>(op) / 32] |= (1u << (static_cast<size_t>(op) % 32));
|
|
|
|
|
|
|
|
|
|
SET(InstructionFunct::sll);
|
|
|
|
|
SET(InstructionFunct::srl);
|
|
|
|
|
SET(InstructionFunct::sra);
|
|
|
|
|
SET(InstructionFunct::sllv);
|
|
|
|
|
SET(InstructionFunct::srlv);
|
|
|
|
|
SET(InstructionFunct::srav);
|
|
|
|
|
SET(InstructionFunct::jr);
|
|
|
|
|
SET(InstructionFunct::jalr);
|
|
|
|
|
SET(InstructionFunct::syscall);
|
|
|
|
|
SET(InstructionFunct::break_);
|
|
|
|
|
SET(InstructionFunct::mfhi);
|
|
|
|
|
SET(InstructionFunct::mthi);
|
|
|
|
|
SET(InstructionFunct::mflo);
|
|
|
|
|
SET(InstructionFunct::mtlo);
|
|
|
|
|
SET(InstructionFunct::mult);
|
|
|
|
|
SET(InstructionFunct::multu);
|
|
|
|
|
SET(InstructionFunct::div);
|
|
|
|
|
SET(InstructionFunct::divu);
|
|
|
|
|
SET(InstructionFunct::add);
|
|
|
|
|
SET(InstructionFunct::addu);
|
|
|
|
|
SET(InstructionFunct::sub);
|
|
|
|
|
SET(InstructionFunct::subu);
|
|
|
|
|
SET(InstructionFunct::and_);
|
|
|
|
|
SET(InstructionFunct::or_);
|
|
|
|
|
SET(InstructionFunct::xor_);
|
|
|
|
|
SET(InstructionFunct::nor);
|
|
|
|
|
SET(InstructionFunct::slt);
|
|
|
|
|
SET(InstructionFunct::sltu);
|
|
|
|
|
|
|
|
|
|
#undef SET
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
#define CHECK(arr, val) ((arr[static_cast<size_t>(val) / 32] & (1u << (static_cast<size_t>(val) % 32))) != 0u)
|
|
|
|
|
|
|
|
|
|
if (instruction.op == InstructionOp::funct)
|
|
|
|
|
return CHECK(valid_func_map, instruction.r.funct.GetValue());
|
|
|
|
|
else
|
|
|
|
|
return CHECK(valid_op_map, instruction.op.GetValue());
|
|
|
|
|
|
|
|
|
|
#undef CHECK
|
|
|
|
|
}
|
|
|
|
|