Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 204f0e13b0 | |||
| cb8bb634ae |
@@ -19,7 +19,7 @@ struct Core {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool IsAddressError(u8 mask, u64 vaddr) {
|
static inline bool IsAddressError(u8 mask, s64 vaddr) {
|
||||||
auto regs = GetRegs();
|
auto regs = GetRegs();
|
||||||
return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0;
|
return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ u32 Interpreter::CacheBlock(u32 addr) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (InstrEndsBlock(instr)) {
|
if (instr.EndsBlock()) {
|
||||||
if (InstrHasDelaySlot(instr) && !fetchDelaySlot) {
|
if (instr.HasDelaySlot() && !fetchDelaySlot) {
|
||||||
fetchDelaySlot = true;
|
fetchDelaySlot = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -129,6 +129,45 @@ u32 Interpreter::CacheBlock(u32 addr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto delay = line.code[i - 1];
|
||||||
|
|
||||||
|
if (i == 2) {
|
||||||
|
// branch to itself
|
||||||
|
// _nop
|
||||||
|
|
||||||
|
auto branch = line.code[i - 2];
|
||||||
|
line.idleSkip = branch.IsBranch() && branch.offset() == -4 && delay.instr.raw == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 3) {
|
||||||
|
// load reg1, [some location]
|
||||||
|
// bcond reg2
|
||||||
|
// _andi reg2, reg1, immediate
|
||||||
|
|
||||||
|
auto branch = line.code[i - 2];
|
||||||
|
auto load = line.code[i - 3]; // load
|
||||||
|
|
||||||
|
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
|
||||||
|
delay.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 &&
|
||||||
|
load.rt() == delay.rs() && delay.rt() == branch.rs();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 5) {
|
||||||
|
// lui reg1
|
||||||
|
// load reg2, [reg1(offset)]
|
||||||
|
// andi reg3, reg2, immediate
|
||||||
|
// bcond reg3
|
||||||
|
// _nop
|
||||||
|
|
||||||
|
auto branch = line.code[i - 2];
|
||||||
|
auto andi = line.code[i - 3]; // andi
|
||||||
|
auto load = line.code[i - 4]; // load
|
||||||
|
|
||||||
|
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
|
||||||
|
andi.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 &&
|
||||||
|
load.rt() == andi.rs() && andi.rt() == branch.rs();
|
||||||
|
}
|
||||||
|
|
||||||
line.cycles = i;
|
line.cycles = i;
|
||||||
line.len = i;
|
line.len = i;
|
||||||
cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line);
|
cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line);
|
||||||
@@ -162,7 +201,7 @@ u32 Interpreter::ExecuteCached() {
|
|||||||
return i + 1;
|
return i + 1;
|
||||||
|
|
||||||
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
||||||
if (IsBranchLikely(instr) && !regs.delaySlot)
|
if (instr.IsBranchLikely() && !regs.delaySlot)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ void JIT::InvalidateBlock(const u32 paddr) {
|
|||||||
blockCache[index] = {};
|
blockCache[index] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
std::optional<Instruction> JIT::FetchInstruction(s64 vaddr) {
|
||||||
u32 paddr = 0;
|
u32 paddr = 0;
|
||||||
|
|
||||||
if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
|
if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
|
||||||
@@ -62,7 +62,7 @@ std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 instr = Core::GetMem().Read<u32>(paddr);
|
const Instruction instr = Core::GetMem().Read<u32>(paddr);
|
||||||
|
|
||||||
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
|
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
|
||||||
|
|
||||||
@@ -169,12 +169,12 @@ u32 JIT::Step() {
|
|||||||
blockPC = blockNextPC;
|
blockPC = blockNextPC;
|
||||||
blockNextPC += 4;
|
blockNextPC += 4;
|
||||||
|
|
||||||
if (InstrEndsBlock(instruction.value())) {
|
if (instruction.value().EndsBlock()) {
|
||||||
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
||||||
if (!delay_instruction)
|
if (!delay_instruction)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (InstrEndsBlock(delay_instruction.value())) {
|
if (delay_instruction.value().EndsBlock()) {
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||||
"[JIT]: Unhandled case of branch from delay slot!");
|
"[JIT]: Unhandled case of branch from delay slot!");
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ struct JIT final {
|
|||||||
|
|
||||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||||
void CheckCompareInterrupt() const;
|
void CheckCompareInterrupt() const;
|
||||||
std::optional<u32> FetchInstruction(s64);
|
std::optional<Instruction> FetchInstruction(s64);
|
||||||
|
|
||||||
void Emit(Instruction);
|
void Emit(Instruction);
|
||||||
void special(Instruction);
|
void special(Instruction);
|
||||||
|
|||||||
@@ -2,144 +2,6 @@
|
|||||||
#include <Instruction.hpp>
|
#include <Instruction.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
static bool SpecialEndsBlock(const Instruction instr) {
|
|
||||||
switch (instr.special()) {
|
|
||||||
case Instruction::JR:
|
|
||||||
case Instruction::JALR:
|
|
||||||
case Instruction::SYSCALL:
|
|
||||||
case Instruction::BREAK:
|
|
||||||
case Instruction::TGE:
|
|
||||||
case Instruction::TGEU:
|
|
||||||
case Instruction::TLT:
|
|
||||||
case Instruction::TLTU:
|
|
||||||
case Instruction::TEQ:
|
|
||||||
case Instruction::TNE:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool InstrHasDelaySlot(const Instruction instr) {
|
|
||||||
switch (instr.opcode()) {
|
|
||||||
case Instruction::SPECIAL:
|
|
||||||
if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
case Instruction::REGIMM:
|
|
||||||
case Instruction::J:
|
|
||||||
case Instruction::JAL:
|
|
||||||
case Instruction::BEQ:
|
|
||||||
case Instruction::BNE:
|
|
||||||
case Instruction::BLEZ:
|
|
||||||
case Instruction::BGTZ:
|
|
||||||
case Instruction::BEQL:
|
|
||||||
case Instruction::BNEL:
|
|
||||||
case Instruction::BLEZL:
|
|
||||||
case Instruction::BGTZL:
|
|
||||||
return true;
|
|
||||||
case Instruction::COP1:
|
|
||||||
if (instr.cop_rs() == 8)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool InstrEndsBlock(const Instruction instr) {
|
|
||||||
switch (instr.opcode()) {
|
|
||||||
case Instruction::SPECIAL:
|
|
||||||
return SpecialEndsBlock(instr);
|
|
||||||
case Instruction::REGIMM:
|
|
||||||
case Instruction::J:
|
|
||||||
case Instruction::JAL:
|
|
||||||
case Instruction::BEQ:
|
|
||||||
case Instruction::BNE:
|
|
||||||
case Instruction::BLEZ:
|
|
||||||
case Instruction::BGTZ:
|
|
||||||
case Instruction::BEQL:
|
|
||||||
case Instruction::BNEL:
|
|
||||||
case Instruction::BLEZL:
|
|
||||||
case Instruction::BGTZL:
|
|
||||||
return true;
|
|
||||||
case Instruction::COP1:
|
|
||||||
if (instr.cop_rs() == 8)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case Instruction::COP0:
|
|
||||||
switch (instr.cop_rs()) {
|
|
||||||
case 0x10 ... 0x1F:
|
|
||||||
switch (instr.cop_funct()) {
|
|
||||||
case 0x18: // eret
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsBranchLikely(const Instruction instr) {
|
|
||||||
switch (instr.opcode()) {
|
|
||||||
case Instruction::BEQL:
|
|
||||||
case Instruction::BNEL:
|
|
||||||
case Instruction::BLEZL:
|
|
||||||
case Instruction::BGTZL:
|
|
||||||
return true;
|
|
||||||
case Instruction::REGIMM:
|
|
||||||
switch (instr.regimm()) {
|
|
||||||
case Instruction::BLTZL:
|
|
||||||
case Instruction::BGEZL:
|
|
||||||
case Instruction::BLTZALL:
|
|
||||||
case Instruction::BGEZALL:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case Instruction::COP1:
|
|
||||||
if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsBranch(const Instruction instr) {
|
|
||||||
switch (instr.opcode()) {
|
|
||||||
case Instruction::BEQ:
|
|
||||||
case Instruction::BNE:
|
|
||||||
case Instruction::BLEZ:
|
|
||||||
case Instruction::BGTZ:
|
|
||||||
return true;
|
|
||||||
case Instruction::REGIMM:
|
|
||||||
switch (instr.regimm()) {
|
|
||||||
case Instruction::BLTZ:
|
|
||||||
case Instruction::BGEZ:
|
|
||||||
case Instruction::BLTZAL:
|
|
||||||
case Instruction::BGEZAL:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case Instruction::COP1:
|
|
||||||
if (instr.cop_rs() == 8 && (instr.cop_rt() == 0 || instr.cop_rt() == 1))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define ARG1 rcx
|
#define ARG1 rcx
|
||||||
#define ARG2 rdx
|
#define ARG2 rdx
|
||||||
|
|||||||
+137
-2
@@ -24,8 +24,8 @@ struct Instruction {
|
|||||||
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
||||||
inline u8 e2() const { return rs() & 0x0f; }
|
inline u8 e2() const { return rs() & 0x0f; }
|
||||||
inline u8 op() const { return rt(); }
|
inline u8 op() const { return rt(); }
|
||||||
inline u16 imm() const { return instr.itype.imm; }
|
inline s16 imm() const { return instr.itype.imm; }
|
||||||
inline u16 offset() const { return imm(); }
|
inline s64 offset() const { return s64(imm()) << 2; }
|
||||||
inline u32 target() const { return instr.jtype.target; }
|
inline u32 target() const { return instr.jtype.target; }
|
||||||
inline u8 opcode() const { return instr.opcode.op; }
|
inline u8 opcode() const { return instr.opcode.op; }
|
||||||
inline u8 special() const { return instr.opcode.special; }
|
inline u8 special() const { return instr.opcode.special; }
|
||||||
@@ -99,6 +99,141 @@ struct Instruction {
|
|||||||
u32 raw;
|
u32 raw;
|
||||||
} instr{};
|
} instr{};
|
||||||
|
|
||||||
|
bool IsBranch() {
|
||||||
|
switch (opcode()) {
|
||||||
|
case Instruction::BEQ:
|
||||||
|
case Instruction::BNE:
|
||||||
|
case Instruction::BLEZ:
|
||||||
|
case Instruction::BGTZ:
|
||||||
|
return true;
|
||||||
|
case Instruction::REGIMM:
|
||||||
|
switch (regimm()) {
|
||||||
|
case Instruction::BLTZ:
|
||||||
|
case Instruction::BGEZ:
|
||||||
|
case Instruction::BLTZAL:
|
||||||
|
case Instruction::BGEZAL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case Instruction::COP1:
|
||||||
|
if (cop_rs() == 8 && (cop_rt() == 0 || cop_rt() == 1))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasDelaySlot() {
|
||||||
|
switch (opcode()) {
|
||||||
|
case Instruction::SPECIAL:
|
||||||
|
if (special() == Instruction::JR || special() == Instruction::JALR)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
case Instruction::REGIMM:
|
||||||
|
case Instruction::J:
|
||||||
|
case Instruction::JAL:
|
||||||
|
case Instruction::BEQ:
|
||||||
|
case Instruction::BNE:
|
||||||
|
case Instruction::BLEZ:
|
||||||
|
case Instruction::BGTZ:
|
||||||
|
case Instruction::BEQL:
|
||||||
|
case Instruction::BNEL:
|
||||||
|
case Instruction::BLEZL:
|
||||||
|
case Instruction::BGTZL:
|
||||||
|
return true;
|
||||||
|
case Instruction::COP1:
|
||||||
|
if (cop_rs() == 8)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EndsBlock() {
|
||||||
|
switch (opcode()) {
|
||||||
|
case Instruction::SPECIAL:
|
||||||
|
switch (special()) {
|
||||||
|
case Instruction::JR:
|
||||||
|
case Instruction::JALR:
|
||||||
|
case Instruction::SYSCALL:
|
||||||
|
case Instruction::BREAK:
|
||||||
|
case Instruction::TGE:
|
||||||
|
case Instruction::TGEU:
|
||||||
|
case Instruction::TLT:
|
||||||
|
case Instruction::TLTU:
|
||||||
|
case Instruction::TEQ:
|
||||||
|
case Instruction::TNE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case Instruction::REGIMM:
|
||||||
|
case Instruction::J:
|
||||||
|
case Instruction::JAL:
|
||||||
|
case Instruction::BEQ:
|
||||||
|
case Instruction::BNE:
|
||||||
|
case Instruction::BLEZ:
|
||||||
|
case Instruction::BGTZ:
|
||||||
|
case Instruction::BEQL:
|
||||||
|
case Instruction::BNEL:
|
||||||
|
case Instruction::BLEZL:
|
||||||
|
case Instruction::BGTZL:
|
||||||
|
return true;
|
||||||
|
case Instruction::COP1:
|
||||||
|
if (cop_rs() == 8)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case Instruction::COP0:
|
||||||
|
switch (cop_rs()) {
|
||||||
|
case 0x10 ... 0x1F:
|
||||||
|
switch (cop_funct()) {
|
||||||
|
case 0x18: // eret
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBranchLikely() {
|
||||||
|
switch (opcode()) {
|
||||||
|
case Instruction::BEQL:
|
||||||
|
case Instruction::BNEL:
|
||||||
|
case Instruction::BLEZL:
|
||||||
|
case Instruction::BGTZL:
|
||||||
|
return true;
|
||||||
|
case Instruction::REGIMM:
|
||||||
|
switch (regimm()) {
|
||||||
|
case Instruction::BLTZL:
|
||||||
|
case Instruction::BGEZL:
|
||||||
|
case Instruction::BLTZALL:
|
||||||
|
case Instruction::BGEZALL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case Instruction::COP1:
|
||||||
|
if (cop_rs() == 8 && (cop_rt() == 2 || cop_rt() == 3))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static constexpr u8 SPECIAL = 0b000000;
|
static constexpr u8 SPECIAL = 0b000000;
|
||||||
static constexpr u8 REGIMM = 0b000001;
|
static constexpr u8 REGIMM = 0b000001;
|
||||||
static constexpr u8 J = 0b000010;
|
static constexpr u8 J = 0b000010;
|
||||||
|
|||||||
Reference in New Issue
Block a user