need to figure out why n64-systemtest loops indefinitely at some address that appears to be valid (i think it's me not invalidating the cache properly)
This commit is contained in:
@@ -186,9 +186,6 @@ endif()
|
|||||||
|
|
||||||
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
|
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
|
||||||
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
||||||
if(USE_JIT)
|
|
||||||
target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (SANITIZERS)
|
if (SANITIZERS)
|
||||||
message("UBSAN AND ASAN: ON")
|
message("UBSAN AND ASAN: ON")
|
||||||
|
|||||||
+3
-16
@@ -4,18 +4,10 @@
|
|||||||
#include <Options.hpp>
|
#include <Options.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
Core::Core() :
|
Core::Core() : interpreter(*mem, regs) {
|
||||||
interpreter(*mem, regs)
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
,
|
|
||||||
jit(*mem, regs)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||||
if (selectedCpu == "interpreter") {
|
if (selectedCpu == "interpreter") {
|
||||||
cpuType = Interpreted;
|
cpuType = PlainInterpreter;
|
||||||
} else if (selectedCpu == "jit") {
|
|
||||||
cpuType = DynamicRecompiler;
|
|
||||||
} else if (selectedCpu == "cached_interpreter") {
|
} else if (selectedCpu == "cached_interpreter") {
|
||||||
cpuType = CachedInterpreter;
|
cpuType = CachedInterpreter;
|
||||||
} else {
|
} else {
|
||||||
@@ -65,17 +57,12 @@ void Core::LoadROM(const std::string &rom_) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 Core::StepCPU() {
|
u32 Core::StepCPU() {
|
||||||
if (cpuType == Interpreted)
|
if (cpuType == PlainInterpreter)
|
||||||
return interpreter.Step() + regs.PopStalledCycles();
|
return interpreter.Step() + regs.PopStalledCycles();
|
||||||
|
|
||||||
if (cpuType == CachedInterpreter)
|
if (cpuType == CachedInterpreter)
|
||||||
return interpreter.ExecuteCached() + regs.PopStalledCycles();
|
return interpreter.ExecuteCached() + regs.PopStalledCycles();
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
if (cpuType == DynamicRecompiler)
|
|
||||||
return jit.Step() + regs.PopStalledCycles();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
panic("Invalid CPU type?");
|
panic("Invalid CPU type?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-10
@@ -1,17 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <ParallelRDPWrapper.hpp>
|
#include <ParallelRDPWrapper.hpp>
|
||||||
#include <backend/core/Interpreter.hpp>
|
#include <backend/core/Interpreter.hpp>
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
#include <backend/core/JIT.hpp>
|
|
||||||
#endif
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <Registers.hpp>
|
#include <Registers.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core {
|
struct Core {
|
||||||
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter;
|
enum CPUType { PlainInterpreter, CachedInterpreter } cpuType = CachedInterpreter;
|
||||||
|
|
||||||
|
bool idleSkip = true;
|
||||||
explicit Core();
|
explicit Core();
|
||||||
|
|
||||||
static Core &GetInstance() {
|
static Core &GetInstance() {
|
||||||
@@ -51,14 +49,8 @@ struct Core {
|
|||||||
size_t memSize{}, cpuSize{}, verSize{};
|
size_t memSize{}, cpuSize{}, verSize{};
|
||||||
std::string rom;
|
std::string rom;
|
||||||
std::set<s64> breakpoints{};
|
std::set<s64> breakpoints{};
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
JIT jit;
|
|
||||||
std::unique_ptr<Mem> mem = std::make_unique<Mem>(jit);
|
|
||||||
Registers regs(jit);
|
|
||||||
#else
|
|
||||||
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
|
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
|
||||||
Registers regs;
|
Registers regs;
|
||||||
#endif
|
|
||||||
Interpreter interpreter;
|
Interpreter interpreter;
|
||||||
ParallelRDP parallel;
|
ParallelRDP parallel;
|
||||||
};
|
};
|
||||||
|
|||||||
+31
-31
@@ -9,40 +9,40 @@ namespace Util {
|
|||||||
|
|
||||||
template <bool toBE = false>
|
template <bool toBE = false>
|
||||||
FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
|
FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
|
||||||
u8 altByteShift = 0;
|
u8 altByteShift = 0;
|
||||||
if (endianness >> 24 != 0x80) {
|
if (endianness >> 24 != 0x80) {
|
||||||
if ((endianness & 0xFF) != 0x80) {
|
if ((endianness & 0xFF) != 0x80) {
|
||||||
if ((endianness >> 16 & 0xff) != 0x80) {
|
if ((endianness >> 16 & 0xff) != 0x80) {
|
||||||
Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness");
|
panic("Unrecognized rom endianness");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
altByteShift = 12;
|
altByteShift = 12;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
altByteShift = 24;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
altByteShift = 24;
|
altByteShift = 0;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
altByteShift = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
endianness &= ~(0xFF << altByteShift);
|
endianness &= ~(0xFF << altByteShift);
|
||||||
|
|
||||||
switch (endianness) {
|
switch (endianness) {
|
||||||
case V64:
|
case V64:
|
||||||
ircolib::SwapBuffer<u16>(rom);
|
ircolib::SwapBuffer<u16>(rom);
|
||||||
if constexpr (!toBE)
|
if constexpr (!toBE)
|
||||||
ircolib::SwapBuffer<u32>(rom);
|
ircolib::SwapBuffer<u32>(rom);
|
||||||
break;
|
break;
|
||||||
case N64:
|
case N64:
|
||||||
if constexpr (toBE)
|
if constexpr (toBE)
|
||||||
ircolib::SwapBuffer<u32>(rom);
|
ircolib::SwapBuffer<u32>(rom);
|
||||||
break;
|
break;
|
||||||
case Z64:
|
case Z64:
|
||||||
if constexpr (!toBE)
|
if constexpr (!toBE)
|
||||||
ircolib::SwapBuffer<u32>(rom);
|
ircolib::SwapBuffer<u32>(rom);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!");
|
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace Util
|
} // namespace Util
|
||||||
|
|||||||
@@ -52,14 +52,10 @@ void Scheduler::HandleEvents() {
|
|||||||
case NONE:
|
case NONE:
|
||||||
break;
|
break;
|
||||||
case IMPOSSIBLE:
|
case IMPOSSIBLE:
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
panic("Impossible scheduler event happened");
|
||||||
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
|
|
||||||
"Unrecognized rom endianness");
|
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
panic("Unknown scheduler event type {}", static_cast<int>(type));
|
||||||
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
|
|
||||||
"Unknown scheduler event type {}", static_cast<int>(type));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
events.pop();
|
events.pop();
|
||||||
|
|||||||
@@ -2,16 +2,10 @@ file(GLOB SOURCES *.cpp)
|
|||||||
file(GLOB HEADERS *.hpp)
|
file(GLOB HEADERS *.hpp)
|
||||||
|
|
||||||
add_subdirectory(interpreter)
|
add_subdirectory(interpreter)
|
||||||
if(USE_JIT)
|
|
||||||
add_subdirectory(jit)
|
|
||||||
endif()
|
|
||||||
add_subdirectory(mem)
|
add_subdirectory(mem)
|
||||||
add_subdirectory(mmio)
|
add_subdirectory(mmio)
|
||||||
add_subdirectory(registers)
|
add_subdirectory(registers)
|
||||||
add_subdirectory(rsp)
|
add_subdirectory(rsp)
|
||||||
|
|
||||||
add_library(core ${SOURCES} ${HEADERS})
|
add_library(core ${SOURCES} ${HEADERS})
|
||||||
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
||||||
if(USE_JIT)
|
|
||||||
target_link_libraries(core PRIVATE jit)
|
|
||||||
endif()
|
|
||||||
@@ -52,7 +52,7 @@ void DataCache::WriteBack<false>(u64 vaddr, u32 paddr) {
|
|||||||
|
|
||||||
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||||
u32 lineStart = GetDCacheLineStart(origPhysAddr);
|
u32 lineStart = GetDCacheLineStart(origPhysAddr);
|
||||||
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
|
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) {
|
|||||||
if (line.ptag == ptag && line.valid) {
|
if (line.ptag == ptag && line.valid) {
|
||||||
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||||
u32 lineStart = GetICacheLineStart(origPhysAddr);
|
u32 lineStart = GetICacheLineStart(origPhysAddr);
|
||||||
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
|
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,25 +9,31 @@ static constexpr u32 MAX_INSTR_PER_BLOCK = 128;
|
|||||||
#define CACHE_GET_BLOCK(addr) (addr / (cachedState.MAX_LINES))
|
#define CACHE_GET_BLOCK(addr) (addr / (cachedState.MAX_LINES))
|
||||||
#define CACHE_GET_LINE(addr) ((addr & ((cachedState.MAX_LINES) - 1)) >> 2)
|
#define CACHE_GET_LINE(addr) ((addr & ((cachedState.MAX_LINES) - 1)) >> 2)
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
struct CachedLine {
|
struct CachedLine {
|
||||||
bool idleSkip = false;
|
bool idleSkip = false;
|
||||||
std::array<Instruction, MAX_INSTR_PER_BLOCK> code = {};
|
std::array<T, MAX_INSTR_PER_BLOCK> code = {};
|
||||||
u32 len = 0;
|
u32 len = 0;
|
||||||
u32 cycles = 0;
|
u32 cycles = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
struct CachedBlock {
|
struct CachedBlock {
|
||||||
CachedBlock(u32 lineAmount) { lines.resize(lineAmount); }
|
CachedBlock(u32 lineAmount) { lines.resize(lineAmount); }
|
||||||
std::vector<CachedLine *> lines = {};
|
std::vector<CachedLine<T> *> lines = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <u32 blockBits, u64 addressSpace>
|
template <u32 blockBits, u64 addressSpace, typename T = Instruction>
|
||||||
struct CachedState {
|
struct CachedState {
|
||||||
static constexpr u32 MAX_LINES = 1 << blockBits;
|
static constexpr u32 MAX_LINES = 1 << blockBits;
|
||||||
std::vector<CachedBlock *> blocks = {};
|
std::vector<CachedBlock<T> *> blocks = {};
|
||||||
bool exception = false;
|
bool exception = false;
|
||||||
|
|
||||||
void EvictCachedBlock(u32 addr) { blocks[addr / MAX_LINES] = {}; }
|
void EvictCachedBlock(u32 addr) { blocks[addr / MAX_LINES] = {}; }
|
||||||
|
void EvictCachedBlocksRange(u32 start, u32 end) {
|
||||||
|
for (u32 i = start; i < end; i += 4)
|
||||||
|
blocks[i / MAX_LINES] = {};
|
||||||
|
}
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
for (auto block : blocks) {
|
for (auto block : blocks) {
|
||||||
@@ -5,42 +5,43 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
struct Disassembler {
|
struct Disassembler {
|
||||||
struct DisassemblyResult {
|
struct DisassemblyResult {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
std::string full;
|
std::string full;
|
||||||
u64 address;
|
u64 address;
|
||||||
std::string mnemonic;
|
std::string mnemonic;
|
||||||
struct Operand {
|
struct Operand {
|
||||||
u32 color;
|
u32 color;
|
||||||
std::string str;
|
std::string str;
|
||||||
|
};
|
||||||
|
std::array<Operand, 3> ops{};
|
||||||
};
|
};
|
||||||
std::array<Operand, 3> ops{};
|
|
||||||
};
|
|
||||||
|
|
||||||
~Disassembler() { cs_close(&handle); }
|
~Disassembler() { cs_close(&handle); }
|
||||||
|
|
||||||
static Disassembler &GetInstance(bool rsp = false) {
|
static Disassembler &GetInstance(bool rsp = false) {
|
||||||
static Disassembler ret(rsp);
|
static Disassembler ret(rsp);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] DisassemblyResult Disassemble(const u32 address) const;
|
|
||||||
[[nodiscard]] DisassemblyResult DisassembleDetailed(u32 address, u32 instruction) const;
|
|
||||||
[[nodiscard]] DisassemblyResult DisassembleSimple(u32 address, u32 instruction) const;
|
|
||||||
private:
|
|
||||||
explicit Disassembler(const bool rsp) : rsp(rsp) {
|
|
||||||
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>((rsp ? CS_MODE_32 : CS_MODE_64) | CS_MODE_BIG_ENDIAN), &handle) !=
|
|
||||||
CS_ERR_OK) {
|
|
||||||
panic("Could not initialize {} disassembler!", rsp ? "RSP" : "CPU");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON) != CS_ERR_OK) {
|
[[nodiscard]] DisassemblyResult Disassemble(const u32 address) const;
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::WARN}, {Util::Error::Type::CAPSTONE_ERROR}, {}, {}, "Could not enable disassembler's details!");
|
[[nodiscard]] DisassemblyResult DisassembleDetailed(u32 address, u32 instruction) const;
|
||||||
details = false;
|
[[nodiscard]] DisassemblyResult DisassembleSimple(u32 address, u32 instruction) const;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rsp = false;
|
private:
|
||||||
bool details = true;
|
explicit Disassembler(const bool rsp) : rsp(rsp) {
|
||||||
csh handle{};
|
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>((rsp ? CS_MODE_32 : CS_MODE_64) | CS_MODE_BIG_ENDIAN),
|
||||||
|
&handle) != CS_ERR_OK) {
|
||||||
|
panic("Could not initialize {} disassembler!", rsp ? "RSP" : "CPU");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON) != CS_ERR_OK) {
|
||||||
|
warn("Could not enable disassembler's details!");
|
||||||
|
details = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rsp = false;
|
||||||
|
bool details = true;
|
||||||
|
csh handle{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <Scheduler.hpp>
|
#include <Scheduler.hpp>
|
||||||
#include "jit/helpers.hpp"
|
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {}
|
Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {}
|
||||||
@@ -88,6 +87,7 @@ bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) {
|
|||||||
|
|
||||||
u32 Interpreter::Step() {
|
u32 Interpreter::Step() {
|
||||||
Instruction instr;
|
Instruction instr;
|
||||||
|
|
||||||
if (!FetchThenMaybeAdvance(instr))
|
if (!FetchThenMaybeAdvance(instr))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -99,14 +99,12 @@ u32 Interpreter::Step() {
|
|||||||
u32 Interpreter::CacheBlock(u32 addr) {
|
u32 Interpreter::CacheBlock(u32 addr) {
|
||||||
u32 blockAddr = addr;
|
u32 blockAddr = addr;
|
||||||
|
|
||||||
CachedLine line;
|
CachedLine<Instruction> line;
|
||||||
u32 i;
|
u32 i;
|
||||||
bool fetchDelaySlot = false;
|
bool fetchDelaySlot = false;
|
||||||
|
|
||||||
for (i = 0; i < MAX_INSTR_PER_BLOCK; i++) {
|
for (i = 0; i < MAX_INSTR_PER_BLOCK; i++) {
|
||||||
Instruction instr;
|
Instruction instr = mem.Read<u32>(addr);
|
||||||
if (!Fetch(instr, addr))
|
|
||||||
return i + 1;
|
|
||||||
|
|
||||||
addr += 4;
|
addr += 4;
|
||||||
line.code[i] = instr;
|
line.code[i] = instr;
|
||||||
@@ -129,43 +127,44 @@ u32 Interpreter::CacheBlock(u32 addr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto delay = line.code[i - 1];
|
if (Core::GetInstance().idleSkip) {
|
||||||
|
auto delay = line.code[i - 1];
|
||||||
|
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
// branch to itself
|
// branch to itself
|
||||||
// _nop
|
// _nop
|
||||||
|
|
||||||
auto branch = line.code[i - 2];
|
auto branch = line.code[i - 2];
|
||||||
line.idleSkip = branch.IsBranch() && branch.offset() == -4 && delay.instr.raw == 0;
|
line.idleSkip = branch.IsBranch() && branch.offset() == -4 && delay.instr.raw == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 3) {
|
if (i == 3) {
|
||||||
// load reg1, [some location]
|
// load reg1, [some location]
|
||||||
// bcond reg2
|
// bcond reg2
|
||||||
// _andi reg2, reg1, immediate
|
// _andi reg2, reg1, immediate
|
||||||
|
|
||||||
auto branch = line.code[i - 2];
|
auto branch = line.code[i - 2];
|
||||||
auto load = line.code[i - 3]; // load
|
auto load = line.code[i - 3]; // load
|
||||||
|
|
||||||
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
|
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
|
||||||
delay.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 &&
|
delay.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -8 &&
|
||||||
load.rt() == delay.rs() && delay.rt() == branch.rs();
|
load.rt() == delay.rs() && delay.rt() == branch.rs();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 5) {
|
if (i == 4) {
|
||||||
// lui reg1
|
// load reg2, [reg1(offset)]
|
||||||
// load reg2, [reg1(offset)]
|
// andi reg3, reg2, immediate
|
||||||
// andi reg3, reg2, immediate
|
// bcond reg3, reg4
|
||||||
// bcond reg3
|
// _nop
|
||||||
// _nop
|
|
||||||
|
|
||||||
auto branch = line.code[i - 2];
|
auto branch = line.code[i - 2];
|
||||||
auto andi = line.code[i - 3]; // andi
|
auto andi = line.code[i - 3]; // andi
|
||||||
auto load = line.code[i - 4]; // load
|
auto load = line.code[i - 4]; // load
|
||||||
|
|
||||||
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
|
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
|
||||||
andi.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 &&
|
andi.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -12 &&
|
||||||
load.rt() == andi.rs() && andi.rt() == branch.rs();
|
load.rt() == andi.rs() && andi.rt() == branch.rs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line.cycles = i;
|
line.cycles = i;
|
||||||
@@ -176,11 +175,17 @@ u32 Interpreter::CacheBlock(u32 addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 Interpreter::ExecuteCached() {
|
u32 Interpreter::ExecuteCached() {
|
||||||
u32 addr = regs.pc;
|
u32 addr;
|
||||||
auto &blocks = cachedState.blocks;
|
auto &blocks = cachedState.blocks;
|
||||||
|
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, addr)) {
|
||||||
|
regs.cop0.HandleTLBException(regs.pc);
|
||||||
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!blocks[CACHE_GET_BLOCK(addr)]) {
|
if (!blocks[CACHE_GET_BLOCK(addr)]) {
|
||||||
blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock(cachedState.MAX_LINES / 4);
|
blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock<Instruction>(cachedState.MAX_LINES / 4);
|
||||||
return CacheBlock(addr);
|
return CacheBlock(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Cache.hpp>
|
#include <Cache.hpp>
|
||||||
#include <Mem.hpp>
|
#include <Mem.hpp>
|
||||||
#include <JITUtils.hpp>
|
#include <CodeCache.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core;
|
struct Core;
|
||||||
|
|||||||
@@ -1,243 +0,0 @@
|
|||||||
#include <Core.hpp>
|
|
||||||
#include <JIT.hpp>
|
|
||||||
#include <Disassembler.hpp>
|
|
||||||
|
|
||||||
namespace n64 {
|
|
||||||
JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) {
|
|
||||||
blockCache.resize(kUpperSize);
|
|
||||||
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
|
||||||
CS_ERR_OK) {
|
|
||||||
panic("Failed to initialize MIPS disassembler");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cs_open(CS_ARCH_X86, static_cast<cs_mode>(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) {
|
|
||||||
panic("Failed to initialize x86 disassembler");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JIT::ShouldServiceInterrupt() const {
|
|
||||||
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
|
||||||
const bool interrupts_enabled = regs.cop0.status.ie == 1;
|
|
||||||
const bool currently_handling_exception = regs.cop0.status.exl == 1;
|
|
||||||
const bool currently_handling_error = regs.cop0.status.erl == 1;
|
|
||||||
|
|
||||||
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::CheckCompareInterrupt() const {
|
|
||||||
regs.cop0.count++;
|
|
||||||
regs.cop0.count &= 0x1FFFFFFFF;
|
|
||||||
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
|
||||||
regs.cop0.cause.ip7 = 1;
|
|
||||||
Core::GetMem().mmio.mi.UpdateInterrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::InvalidateBlock(const u32 paddr) {
|
|
||||||
if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty())
|
|
||||||
blockCache[index] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Instruction> JIT::FetchInstruction(s64 vaddr) {
|
|
||||||
u32 paddr = 0;
|
|
||||||
|
|
||||||
if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
|
|
||||||
/*regs.cop0.HandleTLBException(blockPC);
|
|
||||||
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC);
|
|
||||||
return 1;*/
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
|
||||||
blockPC, {},
|
|
||||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
|
||||||
/*regs.cop0.HandleTLBException(blockPC);
|
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
|
||||||
return 1;*/
|
|
||||||
Util::Error::GetInstance().Throw(
|
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
|
||||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
|
||||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Instruction instr = Core::GetMem().Read<u32>(paddr);
|
|
||||||
|
|
||||||
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
|
|
||||||
|
|
||||||
return instr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::SetPC32(const s32 val) {
|
|
||||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
|
||||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
|
||||||
code.mov(code.SCR1.cvt32(), val);
|
|
||||||
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
|
||||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
|
||||||
code.mov(code.SCR1.cvt32(), val + 4);
|
|
||||||
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
|
||||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::SetPC64(const s64 val) {
|
|
||||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
|
||||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
|
||||||
code.mov(code.SCR1, val);
|
|
||||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
|
||||||
code.mov(code.SCR1, val + 4);
|
|
||||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::SetPC32(const Xbyak::Reg32 &val) {
|
|
||||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
|
||||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
|
||||||
code.movsxd(val.cvt64(), val);
|
|
||||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
|
||||||
code.add(val, 4);
|
|
||||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::SetPC64(const Xbyak::Reg64 &val) {
|
|
||||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
|
||||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
|
||||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
|
||||||
code.add(val, 4);
|
|
||||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 JIT::Step() {
|
|
||||||
blockOldPC = regs.oldPC;
|
|
||||||
blockPC = regs.pc;
|
|
||||||
blockNextPC = regs.nextPC;
|
|
||||||
u32 paddr = 0;
|
|
||||||
|
|
||||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) {
|
|
||||||
/*regs.cop0.HandleTLBException(blockPC);
|
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
|
||||||
return 1;*/
|
|
||||||
Util::Error::GetInstance().Throw(
|
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
|
||||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
|
||||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 upperIndex = paddr >> kUpperShift;
|
|
||||||
const u32 lowerIndex = paddr & kLowerMask;
|
|
||||||
|
|
||||||
if (!blockCache[upperIndex].empty()) {
|
|
||||||
if (blockCache[upperIndex][lowerIndex]) {
|
|
||||||
// trace("[JIT]: Executing already compiled block @ 0x{:016X}", blockPC);
|
|
||||||
return blockCache[upperIndex][lowerIndex]();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
blockCache[upperIndex].resize(kLowerSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
info("[JIT]: Compiling block @ 0x{:016X}:", static_cast<u64>(blockPC));
|
|
||||||
const auto blockInfo = code.getCurr();
|
|
||||||
const auto block = code.getCurr<BlockFn>();
|
|
||||||
blockCache[upperIndex][lowerIndex] = block;
|
|
||||||
|
|
||||||
code.setProtectModeRW();
|
|
||||||
|
|
||||||
u32 instructionsInBlock = 0;
|
|
||||||
|
|
||||||
bool instrEndsBlock = false;
|
|
||||||
|
|
||||||
code.sub(code.rsp, 8);
|
|
||||||
code.push(code.rbp);
|
|
||||||
code.mov(code.rbp, reinterpret_cast<uintptr_t>(this)); // Load context pointer
|
|
||||||
|
|
||||||
cs_insn *insn;
|
|
||||||
info("\tMIPS code (guest PC = 0x{:016X}):", static_cast<u64>(blockPC));
|
|
||||||
|
|
||||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
paddr = 0;
|
|
||||||
|
|
||||||
auto instruction = FetchInstruction(blockPC);
|
|
||||||
|
|
||||||
if (!instruction)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
instructionsInBlock++;
|
|
||||||
|
|
||||||
blockOldPC = blockPC;
|
|
||||||
blockPC = blockNextPC;
|
|
||||||
blockNextPC += 4;
|
|
||||||
|
|
||||||
if (instruction.value().EndsBlock()) {
|
|
||||||
auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
|
||||||
if (!delay_instruction)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (delay_instruction.value().EndsBlock()) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
|
||||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
|
||||||
"[JIT]: Unhandled case of branch from delay slot!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
instructionsInBlock++;
|
|
||||||
|
|
||||||
blockOldPC = blockPC;
|
|
||||||
blockPC = blockNextPC;
|
|
||||||
blockNextPC += 4;
|
|
||||||
|
|
||||||
Emit(delay_instruction.value());
|
|
||||||
Emit(instruction.value());
|
|
||||||
|
|
||||||
if (!branch_taken) {
|
|
||||||
Xbyak::Label runtime_branch_taken;
|
|
||||||
code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]);
|
|
||||||
code.cmp(code.SCR1, 0);
|
|
||||||
code.jne(runtime_branch_taken);
|
|
||||||
code.mov(code.SCR1, blockOldPC);
|
|
||||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
|
||||||
code.mov(code.SCR1, blockPC);
|
|
||||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
|
||||||
code.mov(code.SCR1, blockNextPC);
|
|
||||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
|
||||||
code.L(runtime_branch_taken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (branch_taken)
|
|
||||||
branch_taken = false;
|
|
||||||
|
|
||||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Emit(instruction.value());
|
|
||||||
|
|
||||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
code.mov(code.rax, instructionsInBlock);
|
|
||||||
code.pop(code.rbp);
|
|
||||||
code.add(code.rsp, 8);
|
|
||||||
code.ret();
|
|
||||||
code.setProtectModeRE();
|
|
||||||
static size_t blockInfoSize = 0;
|
|
||||||
blockInfoSize = code.getSize() - blockInfoSize;
|
|
||||||
|
|
||||||
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
|
|
||||||
const auto count =
|
|
||||||
cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
|
|
||||||
if (count > 0) {
|
|
||||||
for (size_t j = 0; j < count; j++) {
|
|
||||||
info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
cs_free(insn, count);
|
|
||||||
}
|
|
||||||
// panic("");
|
|
||||||
return block();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode<u8 *>(), code.getSize(), "jit.dump"); }
|
|
||||||
} // namespace n64
|
|
||||||
@@ -1,271 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Cache.hpp>
|
|
||||||
#include <Registers.hpp>
|
|
||||||
#include <Mem.hpp>
|
|
||||||
#include <vector>
|
|
||||||
#include <xbyak.h>
|
|
||||||
#include <jit/helpers.hpp>
|
|
||||||
#include <capstone/capstone.h>
|
|
||||||
|
|
||||||
namespace n64 {
|
|
||||||
struct Core;
|
|
||||||
|
|
||||||
static constexpr u64 kAddressSpaceSize = 0x8000'0000;
|
|
||||||
static constexpr u8 kUpperShift = 8;
|
|
||||||
static constexpr u8 kLowerMask = 0xff;
|
|
||||||
static constexpr u32 kUpperSize = kAddressSpaceSize >> kUpperShift; // 0x800000
|
|
||||||
static constexpr u32 kLowerSize = 0x100; // 0x80
|
|
||||||
static constexpr u32 kCodeCacheSize = 32_mb;
|
|
||||||
static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
|
|
||||||
|
|
||||||
#define OLD_PC_OFFSET (reinterpret_cast<uintptr_t>(®s.oldPC) - reinterpret_cast<uintptr_t>(this))
|
|
||||||
#define PC_OFFSET (reinterpret_cast<uintptr_t>(®s.pc) - reinterpret_cast<uintptr_t>(this))
|
|
||||||
#define NEXT_PC_OFFSET (reinterpret_cast<uintptr_t>(®s.nextPC) - reinterpret_cast<uintptr_t>(this))
|
|
||||||
#define GPR_OFFSET(x) (reinterpret_cast<uintptr_t>(®s.gpr[(x)]) - reinterpret_cast<uintptr_t>(this))
|
|
||||||
#define BRANCH_TAKEN_OFFSET (reinterpret_cast<uintptr_t>(&branch_taken) - reinterpret_cast<uintptr_t>(this))
|
|
||||||
#define HI_OFFSET (reinterpret_cast<uintptr_t>(®s.hi) - reinterpret_cast<uintptr_t>(this))
|
|
||||||
#define LO_OFFSET (reinterpret_cast<uintptr_t>(®s.lo) - reinterpret_cast<uintptr_t>(this))
|
|
||||||
|
|
||||||
struct JIT final {
|
|
||||||
explicit JIT(Mem &, Registers &);
|
|
||||||
~JIT() = default;
|
|
||||||
u32 Step();
|
|
||||||
|
|
||||||
void Reset() {
|
|
||||||
code.reset();
|
|
||||||
blockCache = {};
|
|
||||||
blockCache.resize(kUpperSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DumpBlockCacheToDisk() const;
|
|
||||||
|
|
||||||
void AdvanceDelaySlot() {
|
|
||||||
regs.prevDelaySlot = regs.delaySlot;
|
|
||||||
regs.delaySlot = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InvalidateBlock(u32);
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend struct Cop1;
|
|
||||||
friend struct Registers;
|
|
||||||
using BlockFn = int (*)();
|
|
||||||
|
|
||||||
bool branch_taken;
|
|
||||||
Registers ®s;
|
|
||||||
Mem &mem;
|
|
||||||
u64 cop2Latch{};
|
|
||||||
s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
|
|
||||||
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
|
|
||||||
csh disassemblerMips{}, disassemblerX86{};
|
|
||||||
std::vector<std::vector<BlockFn>> blockCache;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Xbyak::Address GPR(const size_t index) {
|
|
||||||
if constexpr (sizeof(T) == 1) {
|
|
||||||
return code.byte[code.rbp + GPR_OFFSET(index)];
|
|
||||||
} else if constexpr (sizeof(T) == 2) {
|
|
||||||
return code.word[code.rbp + GPR_OFFSET(index)];
|
|
||||||
} else if constexpr (sizeof(T) == 4) {
|
|
||||||
return code.dword[code.rbp + GPR_OFFSET(index)];
|
|
||||||
} else if constexpr (sizeof(T) == 8) {
|
|
||||||
return code.qword[code.rbp + GPR_OFFSET(index)];
|
|
||||||
}
|
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
|
||||||
{Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING}, blockPC, {},
|
|
||||||
"[JIT]: Invalid register addressing mode {}!", sizeof(T));
|
|
||||||
return Xbyak::Address{0};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Thanks to https://github.com/grumpycoders/pcsx-redux
|
|
||||||
// Load a pointer to the JIT object in "reg"
|
|
||||||
template <typename T>
|
|
||||||
void emitMemberFunctionCall(T func, void *thisObject) {
|
|
||||||
uintptr_t functionPtr;
|
|
||||||
auto thisPtr = reinterpret_cast<uintptr_t>(thisObject);
|
|
||||||
|
|
||||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
|
|
||||||
static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer");
|
|
||||||
std::memcpy(&functionPtr, &func, sizeof(T));
|
|
||||||
#else
|
|
||||||
static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer");
|
|
||||||
uintptr_t arr[2];
|
|
||||||
std::memcpy(arr, &func, sizeof(T));
|
|
||||||
// First 8 bytes correspond to the actual pointer to the function
|
|
||||||
functionPtr = reinterpret_cast<uintptr_t>(reinterpret_cast<void *>(arr[0]));
|
|
||||||
// Next 8 bytes correspond to the "this" pointer adjustment
|
|
||||||
thisPtr += arr[1];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
code.mov(code.ARG1, thisPtr);
|
|
||||||
code.mov(code.rax, functionPtr);
|
|
||||||
code.sub(code.rsp, 8);
|
|
||||||
code.call(code.rax);
|
|
||||||
code.add(code.rsp, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetPC32(s32 val);
|
|
||||||
void SetPC64(s64 val);
|
|
||||||
void SetPC32(const Xbyak::Reg32 &val);
|
|
||||||
void SetPC64(const Xbyak::Reg64 &val);
|
|
||||||
void BranchNotTaken();
|
|
||||||
void BranchTaken(s64 offs);
|
|
||||||
void BranchTaken(const Xbyak::Reg64 &offs);
|
|
||||||
void BranchAbsTaken(s64 addr);
|
|
||||||
void BranchAbsTaken(const Xbyak::Reg64 &addr);
|
|
||||||
|
|
||||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
|
||||||
void CheckCompareInterrupt() const;
|
|
||||||
std::optional<Instruction> FetchInstruction(s64);
|
|
||||||
|
|
||||||
void Emit(Instruction);
|
|
||||||
void special(Instruction);
|
|
||||||
void regimm(Instruction);
|
|
||||||
void add(Instruction);
|
|
||||||
void addu(Instruction);
|
|
||||||
void addi(Instruction);
|
|
||||||
void addiu(Instruction);
|
|
||||||
void andi(Instruction);
|
|
||||||
void and_(Instruction);
|
|
||||||
void branch_constant(bool cond, s64 offset);
|
|
||||||
void branch_likely_constant(bool cond, s64 offset);
|
|
||||||
void branch_abs_constant(bool cond, s64 address);
|
|
||||||
void bltz(Instruction);
|
|
||||||
void bgez(Instruction);
|
|
||||||
void bltzl(Instruction);
|
|
||||||
void bgezl(Instruction);
|
|
||||||
void bltzal(Instruction);
|
|
||||||
void bgezal(Instruction);
|
|
||||||
void bltzall(Instruction);
|
|
||||||
void bgezall(Instruction);
|
|
||||||
void beq(Instruction);
|
|
||||||
void beql(Instruction);
|
|
||||||
void bne(Instruction);
|
|
||||||
void bnel(Instruction);
|
|
||||||
void blez(Instruction);
|
|
||||||
void blezl(Instruction);
|
|
||||||
void bgtz(Instruction);
|
|
||||||
void bgtzl(Instruction);
|
|
||||||
void bfc1(Instruction);
|
|
||||||
void blfc1(Instruction);
|
|
||||||
void bfc0(Instruction);
|
|
||||||
void blfc0(Instruction);
|
|
||||||
void dadd(Instruction);
|
|
||||||
void daddu(Instruction);
|
|
||||||
void daddi(Instruction);
|
|
||||||
void daddiu(Instruction);
|
|
||||||
void ddiv(Instruction);
|
|
||||||
void ddivu(Instruction);
|
|
||||||
void div(Instruction);
|
|
||||||
void divu(Instruction);
|
|
||||||
void dmult(Instruction);
|
|
||||||
void dmultu(Instruction);
|
|
||||||
void dsll(Instruction);
|
|
||||||
void dsllv(Instruction);
|
|
||||||
void dsll32(Instruction);
|
|
||||||
void dsra(Instruction);
|
|
||||||
void dsrav(Instruction);
|
|
||||||
void dsra32(Instruction);
|
|
||||||
void dsrl(Instruction);
|
|
||||||
void dsrlv(Instruction);
|
|
||||||
void dsrl32(Instruction);
|
|
||||||
void dsub(Instruction);
|
|
||||||
void dsubu(Instruction);
|
|
||||||
void j(Instruction);
|
|
||||||
void jr(Instruction);
|
|
||||||
void jal(Instruction);
|
|
||||||
void jalr(Instruction);
|
|
||||||
void lui(Instruction);
|
|
||||||
void lbu(Instruction);
|
|
||||||
void lb(Instruction);
|
|
||||||
void ld(Instruction);
|
|
||||||
void ldc1(Instruction);
|
|
||||||
void ldl(Instruction);
|
|
||||||
void ldr(Instruction);
|
|
||||||
void lh(Instruction);
|
|
||||||
void lhu(Instruction);
|
|
||||||
void ll(Instruction);
|
|
||||||
void lld(Instruction);
|
|
||||||
void lw(Instruction);
|
|
||||||
void lwc1(Instruction);
|
|
||||||
void lwl(Instruction);
|
|
||||||
void lwu(Instruction);
|
|
||||||
void lwr(Instruction);
|
|
||||||
void mfhi(Instruction);
|
|
||||||
void mflo(Instruction);
|
|
||||||
void mult(Instruction);
|
|
||||||
void multu(Instruction);
|
|
||||||
void mthi(Instruction);
|
|
||||||
void mtlo(Instruction);
|
|
||||||
void nor(Instruction);
|
|
||||||
void sb(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sb'!");
|
|
||||||
}
|
|
||||||
void sc(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sc'!");
|
|
||||||
}
|
|
||||||
void scd(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'scd'!");
|
|
||||||
}
|
|
||||||
void sd(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sd'!");
|
|
||||||
}
|
|
||||||
void sdc1(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
|
|
||||||
}
|
|
||||||
void sdl(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
|
|
||||||
}
|
|
||||||
void sdr(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
|
|
||||||
}
|
|
||||||
void sh(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
|
||||||
}
|
|
||||||
void sw(Instruction);
|
|
||||||
void swl(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'swl'!");
|
|
||||||
}
|
|
||||||
void swr(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
|
||||||
}
|
|
||||||
void slti(Instruction);
|
|
||||||
void sltiu(Instruction);
|
|
||||||
void slt(Instruction);
|
|
||||||
void sltu(Instruction);
|
|
||||||
void sll(Instruction);
|
|
||||||
void sllv(Instruction);
|
|
||||||
void sub(Instruction);
|
|
||||||
void subu(Instruction);
|
|
||||||
void swc1(const Instruction) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
|
||||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
|
||||||
"[JIT]: Unhandled case of branch from delay slot!");
|
|
||||||
}
|
|
||||||
void sra(Instruction);
|
|
||||||
void srav(Instruction);
|
|
||||||
void srl(Instruction);
|
|
||||||
void srlv(Instruction);
|
|
||||||
void trap(bool) {
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
|
||||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
|
||||||
"[JIT]: Unhandled case of branch from delay slot!");
|
|
||||||
}
|
|
||||||
void or_(Instruction);
|
|
||||||
void ori(Instruction);
|
|
||||||
void xor_(Instruction);
|
|
||||||
void xori(Instruction);
|
|
||||||
};
|
|
||||||
} // namespace n64
|
|
||||||
+21
-59
@@ -7,11 +7,7 @@
|
|||||||
#include <Registers.hpp>
|
#include <Registers.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
Mem::Mem(JIT &jit) : flash(saveData), jit(jit) {
|
|
||||||
#else
|
|
||||||
Mem::Mem() : flash(saveData) {
|
Mem::Mem() : flash(saveData) {
|
||||||
#endif
|
|
||||||
rom.cart.resize(CART_SIZE);
|
rom.cart.resize(CART_SIZE);
|
||||||
std::ranges::fill(rom.cart, 0);
|
std::ranges::fill(rom.cart, 0);
|
||||||
isviewer_sink = std::ofstream("isviewer.log");
|
isviewer_sink = std::ofstream("isviewer.log");
|
||||||
@@ -21,12 +17,10 @@ void Mem::Reset() {
|
|||||||
std::ranges::fill(isviewer, 0);
|
std::ranges::fill(isviewer, 0);
|
||||||
flash.Reset();
|
flash.Reset();
|
||||||
if (saveData.is_mapped()) {
|
if (saveData.is_mapped()) {
|
||||||
std::error_code error;
|
std::error_code err;
|
||||||
saveData.sync(error);
|
saveData.sync(err);
|
||||||
if (error) {
|
if (err) {
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
error("[Mem]: Could not sync save data!");
|
||||||
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
|
|
||||||
"[Mem]: Could not sync save data!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveData.unmap();
|
saveData.unmap();
|
||||||
@@ -36,18 +30,16 @@ void Mem::Reset() {
|
|||||||
|
|
||||||
void Mem::LoadSRAM(SaveType save_type, fs::path path) {
|
void Mem::LoadSRAM(SaveType save_type, fs::path path) {
|
||||||
if (save_type == SAVE_SRAM_256k) {
|
if (save_type == SAVE_SRAM_256k) {
|
||||||
std::error_code error;
|
std::error_code err;
|
||||||
std::string savePath = Options::GetInstance().GetValue<std::string>("general", "savePath");
|
std::string savePath = Options::GetInstance().GetValue<std::string>("general", "savePath");
|
||||||
if (!savePath.empty()) {
|
if (!savePath.empty()) {
|
||||||
path = savePath / path.filename();
|
path = savePath / path.filename();
|
||||||
}
|
}
|
||||||
sramPath = path.replace_extension(".sram").string();
|
sramPath = path.replace_extension(".sram").string();
|
||||||
if (saveData.is_mapped()) {
|
if (saveData.is_mapped()) {
|
||||||
saveData.sync(error);
|
saveData.sync(err);
|
||||||
if (error) {
|
if (err) {
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
error(R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
|
||||||
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
|
|
||||||
R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveData.unmap();
|
saveData.unmap();
|
||||||
@@ -60,17 +52,13 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sramVec.size() != SRAM_SIZE) {
|
if (sramVec.size() != SRAM_SIZE) {
|
||||||
Util::Error::GetInstance().Throw(
|
error("[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE}, {}, {},
|
|
||||||
"[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveData = mio::make_mmap_sink(sramPath, error);
|
saveData = mio::make_mmap_sink(sramPath, err);
|
||||||
if (error) {
|
if (err) {
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
error(R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
|
||||||
{Util::Error::Type::MMAP_MAKE_SINK_ERROR}, {}, {},
|
|
||||||
R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,9 +158,7 @@ u8 Mem::Read(const u32 paddr) {
|
|||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
|
||||||
Util::Error::GetInstance().Throw(
|
panic("8-bit read access from MMIO addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc);
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_INVALID_ACCESS}, regs.pc,
|
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access from MMIO");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,10 +172,7 @@ u8 Mem::Read(const u32 paddr) {
|
|||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
panic("8-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc);
|
||||||
regs.pc,
|
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0},
|
|
||||||
"8-bit read access in unhandled region");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,10 +203,7 @@ u16 Mem::Read(const u32 paddr) {
|
|||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
panic("16-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc);
|
||||||
regs.pc,
|
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0},
|
|
||||||
"16-bit read access in unhandled region");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,10 +235,7 @@ u32 Mem::Read(const u32 paddr) {
|
|||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
panic("32-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc);
|
||||||
regs.pc,
|
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0},
|
|
||||||
"32-bit read access in unhandled region");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,10 +267,7 @@ u64 Mem::Read(const u32 paddr) {
|
|||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
panic("64-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc);
|
||||||
regs.pc,
|
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0},
|
|
||||||
"64-bit read access in unhandled region");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,12 +276,9 @@ void Mem::Write<u8>(u32 paddr, u32 val) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
jit.InvalidateBlock(paddr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
mmio.rdp.WriteRDRAM<u8>(paddr, val);
|
mmio.rdp.WriteRDRAM<u8>(paddr, val);
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
@@ -352,12 +323,9 @@ void Mem::Write<u16>(u32 paddr, u32 val) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
jit.InvalidateBlock(paddr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
mmio.rdp.WriteRDRAM<u16>(paddr, val);
|
mmio.rdp.WriteRDRAM<u16>(paddr, val);
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
@@ -402,12 +370,9 @@ void Mem::Write<u32>(const u32 paddr, const u32 val) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
jit.InvalidateBlock(paddr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
mmio.rdp.WriteRDRAM<u32>(paddr, val);
|
mmio.rdp.WriteRDRAM<u32>(paddr, val);
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
@@ -449,12 +414,9 @@ void Mem::Write(const u32 paddr, u64 val) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
jit.InvalidateBlock(paddr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
mmio.rdp.WriteRDRAM<u64>(paddr, val);
|
mmio.rdp.WriteRDRAM<u64>(paddr, val);
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <log.hpp>
|
#include <log.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct ROMHeader {
|
struct ROMHeader {
|
||||||
@@ -74,12 +73,6 @@ struct Flash {
|
|||||||
T Read(u32 index) const;
|
T Read(u32 index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
struct JIT;
|
|
||||||
struct Mem {
|
|
||||||
Mem(JIT &jit);
|
|
||||||
JIT &jit;
|
|
||||||
#else
|
|
||||||
struct Mem {
|
struct Mem {
|
||||||
Mem();
|
Mem();
|
||||||
~Mem() = default;
|
~Mem() = default;
|
||||||
@@ -134,7 +127,6 @@ struct Mem {
|
|||||||
friend struct PI;
|
friend struct PI;
|
||||||
friend struct AI;
|
friend struct AI;
|
||||||
friend struct RSP;
|
friend struct RSP;
|
||||||
friend struct JIT;
|
|
||||||
friend struct Core;
|
friend struct Core;
|
||||||
|
|
||||||
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
||||||
@@ -148,5 +140,4 @@ struct Mem {
|
|||||||
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
|
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <log.hpp>
|
#include <log.hpp>
|
||||||
#include <jit/helpers.hpp>
|
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
RSP::RSP() { Reset(); }
|
RSP::RSP() { Reset(); }
|
||||||
@@ -118,6 +117,7 @@ void RSP::DMA<true>() {
|
|||||||
for (u32 j = 0; j < length; j++) {
|
for (u32 j = 0; j < length; j++) {
|
||||||
mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]);
|
mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]);
|
||||||
}
|
}
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlocksRange(dram_address, dram_address + length);
|
||||||
|
|
||||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <core/RDP.hpp>
|
#include <core/RDP.hpp>
|
||||||
#include <core/mmio/MI.hpp>
|
#include <core/mmio/MI.hpp>
|
||||||
#include <Instruction.hpp>
|
#include <Instruction.hpp>
|
||||||
#include <JITUtils.hpp>
|
|
||||||
|
|
||||||
#define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF])
|
#define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF])
|
||||||
#define GET_RSP_HALF(addr) ((RSP_BYTE(addr) << 8) | RSP_BYTE((addr) + 1))
|
#define GET_RSP_HALF(addr) ((RSP_BYTE(addr) << 8) | RSP_BYTE((addr) + 1))
|
||||||
|
|||||||
@@ -1310,7 +1310,6 @@ void Cop1::swc1(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(addr);
|
regs.cop0.HandleTLBException(addr);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(addr);
|
|
||||||
mem.Write<u32>(physical, FGR_T<u32>(regs.cop0.status, instr.ft()));
|
mem.Write<u32>(physical, FGR_T<u32>(regs.cop0.status, instr.ft()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1338,7 +1337,6 @@ void Cop1::sdc1(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(addr);
|
regs.cop0.HandleTLBException(addr);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(addr);
|
|
||||||
mem.Write(physical, FGR_T<u64>(regs.cop0.status, instr.ft()));
|
mem.Write(physical, FGR_T<u64>(regs.cop0.status, instr.ft()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,7 +409,6 @@ void Interpreter::sb(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write<u8>(paddr, regs.Read<s64>(instr.rt()));
|
mem.Write<u8>(paddr, regs.Read<s64>(instr.rt()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,7 +432,6 @@ void Interpreter::sc(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
||||||
regs.Write(instr.rt(), 1);
|
regs.Write(instr.rt(), 1);
|
||||||
}
|
}
|
||||||
@@ -466,7 +464,6 @@ void Interpreter::scd(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
||||||
regs.Write(instr.rt(), 1);
|
regs.Write(instr.rt(), 1);
|
||||||
}
|
}
|
||||||
@@ -483,7 +480,6 @@ void Interpreter::sh(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write<u16>(physical, regs.Read<s64>(instr.rt()));
|
mem.Write<u16>(physical, regs.Read<s64>(instr.rt()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,7 +498,6 @@ void Interpreter::sw(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write<u32>(physical, regs.Read<s64>(instr.rt()));
|
mem.Write<u32>(physical, regs.Read<s64>(instr.rt()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,7 +515,6 @@ void Interpreter::sd(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write(physical, regs.Read<s64>(instr.rt()));
|
mem.Write(physical, regs.Read<s64>(instr.rt()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,7 +530,6 @@ void Interpreter::sdl(const Instruction instr) {
|
|||||||
const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
||||||
const u64 data = mem.Read<u64>(paddr & ~7);
|
const u64 data = mem.Read<u64>(paddr & ~7);
|
||||||
const u64 rt = regs.Read<s64>(instr.rt());
|
const u64 rt = regs.Read<s64>(instr.rt());
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift));
|
mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,7 +545,6 @@ void Interpreter::sdr(const Instruction instr) {
|
|||||||
const u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
const u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
||||||
const u64 data = mem.Read<u64>(paddr & ~7);
|
const u64 data = mem.Read<u64>(paddr & ~7);
|
||||||
const u64 rt = regs.Read<s64>(instr.rt());
|
const u64 rt = regs.Read<s64>(instr.rt());
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write(paddr & ~7, (data & ~mask) | (rt << shift));
|
mem.Write(paddr & ~7, (data & ~mask) | (rt << shift));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -568,7 +560,6 @@ void Interpreter::swl(const Instruction instr) {
|
|||||||
const u32 mask = 0xFFFFFFFF >> shift;
|
const u32 mask = 0xFFFFFFFF >> shift;
|
||||||
const u32 data = mem.Read<u32>(paddr & ~3);
|
const u32 data = mem.Read<u32>(paddr & ~3);
|
||||||
const u32 rt = regs.Read<s64>(instr.rt());
|
const u32 rt = regs.Read<s64>(instr.rt());
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt >> shift));
|
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt >> shift));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -584,7 +575,6 @@ void Interpreter::swr(const Instruction instr) {
|
|||||||
const u32 mask = 0xFFFFFFFF << shift;
|
const u32 mask = 0xFFFFFFFF << shift;
|
||||||
const u32 data = mem.Read<u32>(paddr & ~3);
|
const u32 data = mem.Read<u32>(paddr & ~3);
|
||||||
const u32 rt = regs.Read<s64>(instr.rt());
|
const u32 rt = regs.Read<s64>(instr.rt());
|
||||||
cachedState.EvictCachedBlock(address);
|
|
||||||
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift));
|
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
file(GLOB_RECURSE SOURCES *.cpp)
|
|
||||||
file(GLOB_RECURSE HEADERS *.hpp)
|
|
||||||
|
|
||||||
add_library(jit ${SOURCES} ${HEADERS})
|
|
||||||
@@ -1,465 +0,0 @@
|
|||||||
#include <JIT.hpp>
|
|
||||||
#include <Instruction.hpp>
|
|
||||||
|
|
||||||
namespace n64 {
|
|
||||||
void JIT::special(const Instruction instr) {
|
|
||||||
// 00rr_rccc
|
|
||||||
switch (instr.special()) {
|
|
||||||
case Instruction::SLL:
|
|
||||||
if (instr != 0) {
|
|
||||||
sll(instr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Instruction::SRL:
|
|
||||||
srl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SRA:
|
|
||||||
sra(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SLLV:
|
|
||||||
sllv(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SRLV:
|
|
||||||
srlv(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SRAV:
|
|
||||||
srav(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::JR:
|
|
||||||
jr(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::JALR:
|
|
||||||
jalr(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SYSCALL:
|
|
||||||
regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC);
|
|
||||||
break;
|
|
||||||
case Instruction::BREAK:
|
|
||||||
regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC);
|
|
||||||
break;
|
|
||||||
case Instruction::SYNC:
|
|
||||||
break; // SYNC
|
|
||||||
case Instruction::MFHI:
|
|
||||||
mfhi(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::MTHI:
|
|
||||||
mthi(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::MFLO:
|
|
||||||
mflo(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::MTLO:
|
|
||||||
mtlo(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSLLV:
|
|
||||||
dsllv(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSRLV:
|
|
||||||
dsrlv(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSRAV:
|
|
||||||
dsrav(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::MULT:
|
|
||||||
mult(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::MULTU:
|
|
||||||
multu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DIV:
|
|
||||||
div(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DIVU:
|
|
||||||
divu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DMULT:
|
|
||||||
dmult(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DMULTU:
|
|
||||||
dmultu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DDIV:
|
|
||||||
ddiv(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DDIVU:
|
|
||||||
ddivu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::ADD:
|
|
||||||
add(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::ADDU:
|
|
||||||
addu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SUB:
|
|
||||||
sub(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SUBU:
|
|
||||||
subu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::AND:
|
|
||||||
and_(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::OR:
|
|
||||||
or_(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::XOR:
|
|
||||||
xor_(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::NOR:
|
|
||||||
nor(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SLT:
|
|
||||||
slt(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SLTU:
|
|
||||||
sltu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DADD:
|
|
||||||
dadd(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DADDU:
|
|
||||||
daddu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSUB:
|
|
||||||
dsub(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSUBU:
|
|
||||||
dsubu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::TGE:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) >= regs.Read<s64>(instr.rt()));
|
|
||||||
break;
|
|
||||||
case Instruction::TGEU:
|
|
||||||
trap(regs.Read<u64>(instr.rs()) >= regs.Read<u64>(instr.rt()));
|
|
||||||
break;
|
|
||||||
case Instruction::TLT:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
|
|
||||||
break;
|
|
||||||
case Instruction::TLTU:
|
|
||||||
trap(regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
|
||||||
break;
|
|
||||||
case Instruction::TEQ:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
|
|
||||||
break;
|
|
||||||
case Instruction::TNE:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
|
|
||||||
break;
|
|
||||||
case Instruction::DSLL:
|
|
||||||
dsll(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSRL:
|
|
||||||
dsrl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSRA:
|
|
||||||
dsra(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSLL32:
|
|
||||||
dsll32(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSRL32:
|
|
||||||
dsrl32(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DSRA32:
|
|
||||||
dsra32(instr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
panic("Unimplemented special {} ({:08X}) (pc: {:016X})", instr.special(), u32(instr),
|
|
||||||
static_cast<u64>(regs.oldPC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::regimm(const Instruction instr) {
|
|
||||||
// 000r_rccc
|
|
||||||
switch (instr.regimm()) {
|
|
||||||
case Instruction::BLTZ:
|
|
||||||
bltz(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BGEZ:
|
|
||||||
bgez(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BLTZL:
|
|
||||||
bltzl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BGEZL:
|
|
||||||
bgezl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::TGEI:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) >= static_cast<s64>(static_cast<s16>(instr)));
|
|
||||||
break;
|
|
||||||
case Instruction::TGEIU:
|
|
||||||
trap(regs.Read<u64>(instr.rs()) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
|
|
||||||
break;
|
|
||||||
case Instruction::TLTI:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) < static_cast<s64>(static_cast<s16>(instr)));
|
|
||||||
break;
|
|
||||||
case Instruction::TLTIU:
|
|
||||||
trap(regs.Read<u64>(instr.rs()) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
|
|
||||||
break;
|
|
||||||
case Instruction::TEQI:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) == static_cast<s64>(static_cast<s16>(instr)));
|
|
||||||
break;
|
|
||||||
case Instruction::TNEI:
|
|
||||||
trap(regs.Read<s64>(instr.rs()) != static_cast<s64>(static_cast<s16>(instr)));
|
|
||||||
break;
|
|
||||||
case Instruction::BLTZAL:
|
|
||||||
bltzal(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BGEZAL:
|
|
||||||
bgezal(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BLTZALL:
|
|
||||||
bltzall(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BGEZALL:
|
|
||||||
bgezall(instr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr),
|
|
||||||
static_cast<u64>(regs.oldPC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::Emit(const Instruction instr) {
|
|
||||||
switch (instr.opcode()) {
|
|
||||||
case Instruction::SPECIAL:
|
|
||||||
special(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::REGIMM:
|
|
||||||
regimm(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::J:
|
|
||||||
j(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::JAL:
|
|
||||||
jal(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BEQ:
|
|
||||||
beq(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BNE:
|
|
||||||
bne(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BLEZ:
|
|
||||||
blez(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BGTZ:
|
|
||||||
bgtz(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::ADDI:
|
|
||||||
addi(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::ADDIU:
|
|
||||||
addiu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SLTI:
|
|
||||||
slti(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SLTIU:
|
|
||||||
sltiu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::ANDI:
|
|
||||||
andi(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::ORI:
|
|
||||||
ori(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::XORI:
|
|
||||||
xori(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LUI:
|
|
||||||
lui(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::COP0:
|
|
||||||
switch (instr.cop_rs()) {
|
|
||||||
case 0x00:
|
|
||||||
code.mov(code.ARG2, instr);
|
|
||||||
emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x01:
|
|
||||||
code.mov(code.ARG2, instr);
|
|
||||||
emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x04:
|
|
||||||
code.mov(code.ARG2, instr);
|
|
||||||
emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x05:
|
|
||||||
code.mov(code.ARG2, instr);
|
|
||||||
emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x10 ... 0x1F:
|
|
||||||
switch (instr.cop_funct()) {
|
|
||||||
case 0x01:
|
|
||||||
emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x02:
|
|
||||||
code.mov(code.ARG2, COP0_REG_INDEX);
|
|
||||||
emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0);
|
|
||||||
code.mov(code.ARG2, code.rax);
|
|
||||||
code.and_(code.ARG2, 0x3F);
|
|
||||||
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x06:
|
|
||||||
emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0);
|
|
||||||
code.mov(code.ARG2, code.rax);
|
|
||||||
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x08:
|
|
||||||
emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0);
|
|
||||||
break;
|
|
||||||
case 0x18:
|
|
||||||
emitMemberFunctionCall(&Cop0::eret, ®s.cop0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
panic("Unimplemented COP0 instruction {}", instr.cop_rs());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Instruction::COP1:
|
|
||||||
{
|
|
||||||
if (instr.cop_rs() == 0x08) {
|
|
||||||
switch (instr.cop_rt()) {
|
|
||||||
case 0:
|
|
||||||
// if (!regs.cop1.CheckFPUUsable())
|
|
||||||
// return;
|
|
||||||
bfc0(instr);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
// if (!regs.cop1.CheckFPUUsable())
|
|
||||||
// return;
|
|
||||||
bfc1(instr);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
// if (!regs.cop1.CheckFPUUsable())
|
|
||||||
// return;
|
|
||||||
blfc0(instr);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// if (!regs.cop1.CheckFPUUsable())
|
|
||||||
// return;
|
|
||||||
blfc1(instr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
panic("Undefined BC COP1 {:02X}", instr.cop_rt());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
regs.cop1.decode(instr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Instruction::COP2:
|
|
||||||
break;
|
|
||||||
case Instruction::BEQL:
|
|
||||||
beql(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BNEL:
|
|
||||||
bnel(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BLEZL:
|
|
||||||
blezl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::BGTZL:
|
|
||||||
bgtzl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DADDI:
|
|
||||||
daddi(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::DADDIU:
|
|
||||||
daddiu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LDL:
|
|
||||||
ldl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LDR:
|
|
||||||
ldr(instr);
|
|
||||||
break;
|
|
||||||
case 0x1F:
|
|
||||||
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
|
||||||
break;
|
|
||||||
case Instruction::LB:
|
|
||||||
lb(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LH:
|
|
||||||
lh(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LWL:
|
|
||||||
lwl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LW:
|
|
||||||
lw(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LBU:
|
|
||||||
lbu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LHU:
|
|
||||||
lhu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LWR:
|
|
||||||
lwr(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LWU:
|
|
||||||
lwu(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SB:
|
|
||||||
sb(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SH:
|
|
||||||
sh(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SWL:
|
|
||||||
swl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SW:
|
|
||||||
sw(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SDL:
|
|
||||||
sdl(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SDR:
|
|
||||||
sdr(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SWR:
|
|
||||||
swr(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::CACHE:
|
|
||||||
break; // CACHE
|
|
||||||
case Instruction::LL:
|
|
||||||
ll(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LWC1:
|
|
||||||
lwc1(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LLD:
|
|
||||||
lld(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LDC1:
|
|
||||||
ldc1(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::LD:
|
|
||||||
ld(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SC:
|
|
||||||
sc(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SWC1:
|
|
||||||
swc1(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SCD:
|
|
||||||
scd(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SDC1:
|
|
||||||
sdc1(instr);
|
|
||||||
break;
|
|
||||||
case Instruction::SD:
|
|
||||||
sd(instr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
DumpBlockCacheToDisk();
|
|
||||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr),
|
|
||||||
static_cast<u64>(regs.oldPC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace n64
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Instruction.hpp>
|
|
||||||
|
|
||||||
namespace n64 {
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define ARG1 rcx
|
|
||||||
#define ARG2 rdx
|
|
||||||
#define ARG3 r8
|
|
||||||
#define ARG4 r9
|
|
||||||
#define SCR1 rax
|
|
||||||
#define SCR2 rcx
|
|
||||||
#define SCR3 rdx
|
|
||||||
#define SCR4 r8
|
|
||||||
#define SCR5 r9
|
|
||||||
#define SCR6 r10
|
|
||||||
#define SCR7 r11
|
|
||||||
#else
|
|
||||||
#define ARG1 rdi
|
|
||||||
#define ARG2 rsi
|
|
||||||
#define ARG3 rdx
|
|
||||||
#define ARG4 rcx
|
|
||||||
#define ARG5 r8
|
|
||||||
#define ARG6 r9
|
|
||||||
#define SCR1 rax
|
|
||||||
#define SCR2 rdi
|
|
||||||
#define SCR3 rsi
|
|
||||||
#define SCR4 rdx
|
|
||||||
#define SCR5 rcx
|
|
||||||
#define SCR6 r8
|
|
||||||
#define SCR7 r9
|
|
||||||
#define SCR8 r10
|
|
||||||
#define SCR9 r11
|
|
||||||
#endif
|
|
||||||
} // namespace n64
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -539,6 +539,7 @@ void PI::DMA<true>() {
|
|||||||
for (u32 i = 0; i < len; i++) {
|
for (u32 i = 0; i < len; i++) {
|
||||||
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, BusRead<u8, true>(cartAddr + i));
|
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, BusRead<u8, true>(cartAddr + i));
|
||||||
}
|
}
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlocksRange(dramAddr, dramAddr + len);
|
||||||
dramAddr += len;
|
dramAddr += len;
|
||||||
dramAddr = (dramAddr + 7) & ~7;
|
dramAddr = (dramAddr + 7) & ~7;
|
||||||
cartAddr += len;
|
cartAddr += len;
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ void SI::Write(u32 addr, u32 val) {
|
|||||||
status.dmaBusy = true;
|
status.dmaBusy = true;
|
||||||
toDram = true;
|
toDram = true;
|
||||||
Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlocksRange(dramAddr, dramAddr + 64);
|
||||||
break;
|
break;
|
||||||
case 0x04800010:
|
case 0x04800010:
|
||||||
pifAddr = val & 0x1FFFFFFF;
|
pifAddr = val & 0x1FFFFFFF;
|
||||||
|
|||||||
@@ -5,123 +5,122 @@ namespace n64 {
|
|||||||
VI::VI() { Reset(); }
|
VI::VI() { Reset(); }
|
||||||
|
|
||||||
void VI::Reset() {
|
void VI::Reset() {
|
||||||
status.raw = 0xF;
|
status.raw = 0xF;
|
||||||
intr = 256;
|
intr = 256;
|
||||||
origin = 0;
|
origin = 0;
|
||||||
width = 320;
|
width = 320;
|
||||||
current = 0;
|
current = 0;
|
||||||
vsync = 0;
|
vsync = 0;
|
||||||
hsync = 0;
|
hsync = 0;
|
||||||
numHalflines = 262;
|
numHalflines = 262;
|
||||||
numFields = 1;
|
numFields = 1;
|
||||||
cyclesPerHalfline = 1000;
|
cyclesPerHalfline = 1000;
|
||||||
xscale = {}, yscale = {};
|
xscale = {}, yscale = {};
|
||||||
hsyncLeap = {}, burst = {}, vburst = {};
|
hsyncLeap = {}, burst = {}, vburst = {};
|
||||||
hstart = {}, vstart = {};
|
hstart = {}, vstart = {};
|
||||||
isPal = false;
|
isPal = false;
|
||||||
swaps = {};
|
swaps = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 VI::Read(const u32 paddr) const {
|
u32 VI::Read(const u32 paddr) const {
|
||||||
switch (paddr) {
|
switch (paddr) {
|
||||||
case 0x04400000:
|
case 0x04400000:
|
||||||
return status.raw;
|
return status.raw;
|
||||||
case 0x04400004:
|
case 0x04400004:
|
||||||
return origin;
|
return origin;
|
||||||
case 0x04400008:
|
case 0x04400008:
|
||||||
return width;
|
return width;
|
||||||
case 0x0440000C:
|
case 0x0440000C:
|
||||||
return intr;
|
return intr;
|
||||||
case 0x04400010:
|
case 0x04400010:
|
||||||
return current << 1;
|
return current << 1;
|
||||||
case 0x04400014:
|
case 0x04400014:
|
||||||
return burst.raw;
|
return burst.raw;
|
||||||
case 0x04400018:
|
case 0x04400018:
|
||||||
return vsync;
|
return vsync;
|
||||||
case 0x0440001C:
|
case 0x0440001C:
|
||||||
return hsync;
|
return hsync;
|
||||||
case 0x04400020:
|
case 0x04400020:
|
||||||
return hsyncLeap.raw;
|
return hsyncLeap.raw;
|
||||||
case 0x04400024:
|
case 0x04400024:
|
||||||
return hstart.raw;
|
return hstart.raw;
|
||||||
case 0x04400028:
|
case 0x04400028:
|
||||||
return vstart.raw;
|
return vstart.raw;
|
||||||
case 0x0440002C:
|
case 0x0440002C:
|
||||||
return vburst;
|
return vburst;
|
||||||
case 0x04400030:
|
case 0x04400030:
|
||||||
return xscale.raw;
|
return xscale.raw;
|
||||||
case 0x04400034:
|
case 0x04400034:
|
||||||
return yscale.raw;
|
return yscale.raw;
|
||||||
default: {
|
default:
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
{
|
||||||
Util::Error::GetInstance().Throw(
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
panic("32-bit read access on unhandled VI register @ pc 0x{:016X}", u64(regs.oldPC));
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, "32-bit read access on unhandled VI register");
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VI::Write(const u32 paddr, const u32 val) {
|
void VI::Write(const u32 paddr, const u32 val) {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
switch (paddr) {
|
switch (paddr) {
|
||||||
case 0x04400000:
|
case 0x04400000:
|
||||||
status.raw = val;
|
status.raw = val;
|
||||||
numFields = status.serrate ? 2 : 1;
|
numFields = status.serrate ? 2 : 1;
|
||||||
break;
|
break;
|
||||||
case 0x04400004:
|
case 0x04400004:
|
||||||
{
|
{
|
||||||
const u32 masked = val & 0xFFFFFF;
|
const u32 masked = val & 0xFFFFFF;
|
||||||
if (origin != masked) {
|
if (origin != masked) {
|
||||||
swaps++;
|
swaps++;
|
||||||
}
|
}
|
||||||
origin = masked;
|
origin = masked;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x04400008:
|
||||||
|
width = val & 0x7FF;
|
||||||
|
break;
|
||||||
|
case 0x0440000C:
|
||||||
|
intr = val & 0x3FF;
|
||||||
|
break;
|
||||||
|
case 0x04400010:
|
||||||
|
mem.mmio.mi.InterruptLower(MI::Interrupt::VI);
|
||||||
|
break;
|
||||||
|
case 0x04400014:
|
||||||
|
burst.raw = val;
|
||||||
|
break;
|
||||||
|
case 0x04400018:
|
||||||
|
vsync = val & 0x3FF;
|
||||||
|
numHalflines = vsync >> 1;
|
||||||
|
cyclesPerHalfline = GetCyclesPerFrame(isPal) / numHalflines;
|
||||||
|
break;
|
||||||
|
case 0x0440001C:
|
||||||
|
hsync = val & 0x3FF;
|
||||||
|
break;
|
||||||
|
case 0x04400020:
|
||||||
|
hsyncLeap.raw = val;
|
||||||
|
break;
|
||||||
|
case 0x04400024:
|
||||||
|
hstart.raw = val;
|
||||||
|
break;
|
||||||
|
case 0x04400028:
|
||||||
|
vstart.raw = val;
|
||||||
|
break;
|
||||||
|
case 0x0440002C:
|
||||||
|
vburst = val;
|
||||||
|
break;
|
||||||
|
case 0x04400030:
|
||||||
|
xscale.raw = val;
|
||||||
|
break;
|
||||||
|
case 0x04400034:
|
||||||
|
yscale.raw = val;
|
||||||
|
break;
|
||||||
|
case 0x04400038:
|
||||||
|
break;
|
||||||
|
case 0x0440003C:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("Unimplemented VI[{:08X}] write ({:08X})", paddr, val);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 0x04400008:
|
|
||||||
width = val & 0x7FF;
|
|
||||||
break;
|
|
||||||
case 0x0440000C:
|
|
||||||
intr = val & 0x3FF;
|
|
||||||
break;
|
|
||||||
case 0x04400010:
|
|
||||||
mem.mmio.mi.InterruptLower(MI::Interrupt::VI);
|
|
||||||
break;
|
|
||||||
case 0x04400014:
|
|
||||||
burst.raw = val;
|
|
||||||
break;
|
|
||||||
case 0x04400018:
|
|
||||||
vsync = val & 0x3FF;
|
|
||||||
numHalflines = vsync >> 1;
|
|
||||||
cyclesPerHalfline = GetCyclesPerFrame(isPal) / numHalflines;
|
|
||||||
break;
|
|
||||||
case 0x0440001C:
|
|
||||||
hsync = val & 0x3FF;
|
|
||||||
break;
|
|
||||||
case 0x04400020:
|
|
||||||
hsyncLeap.raw = val;
|
|
||||||
break;
|
|
||||||
case 0x04400024:
|
|
||||||
hstart.raw = val;
|
|
||||||
break;
|
|
||||||
case 0x04400028:
|
|
||||||
vstart.raw = val;
|
|
||||||
break;
|
|
||||||
case 0x0440002C:
|
|
||||||
vburst = val;
|
|
||||||
break;
|
|
||||||
case 0x04400030:
|
|
||||||
xscale.raw = val;
|
|
||||||
break;
|
|
||||||
case 0x04400034:
|
|
||||||
yscale.raw = val;
|
|
||||||
break;
|
|
||||||
case 0x04400038:
|
|
||||||
break;
|
|
||||||
case 0x0440003C:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
panic("Unimplemented VI[{:08X}] write ({:08X})", paddr, val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -275,8 +275,6 @@ struct Cop0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct JIT;
|
|
||||||
|
|
||||||
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
||||||
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }
|
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ struct Cop1 {
|
|||||||
void Reset();
|
void Reset();
|
||||||
void decode(const Instruction);
|
void decode(const Instruction);
|
||||||
friend struct Interpreter;
|
friend struct Interpreter;
|
||||||
friend struct JIT;
|
|
||||||
|
|
||||||
template <bool preserveCause = false>
|
template <bool preserveCause = false>
|
||||||
bool CheckFPUUsable();
|
bool CheckFPUUsable();
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
#include <jit/helpers.hpp>
|
|
||||||
#include <core/registers/Registers.hpp>
|
#include <core/registers/Registers.hpp>
|
||||||
#include <core/JIT.hpp>
|
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
|
|
||||||
#else
|
|
||||||
Registers::Registers() { Reset(); }
|
Registers::Registers() { Reset(); }
|
||||||
#endif
|
|
||||||
|
|
||||||
void Registers::Reset() {
|
void Registers::Reset() {
|
||||||
hi = 0;
|
hi = 0;
|
||||||
@@ -76,88 +70,6 @@ s8 Registers::Read<s8>(size_t idx) {
|
|||||||
return static_cast<s8>(Read<u8>(idx));
|
return static_cast<s8>(Read<u8>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
template <>
|
|
||||||
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt64(), Read<u64>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt64(), Read<s64>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt32(), Read<u32>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt32(), jit.GPR<u32>(idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt32(), Read<s32>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt32(), jit.GPR<s32>(idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt16(), Read<u16>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt16(), Read<s16>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt8(), Read<u8>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt8(), jit.GPR<u8>(idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
|
|
||||||
if (IsRegConstant(idx)) {
|
|
||||||
jit.code.mov(reg.cvt8(), Read<s8>(idx));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit.code.mov(reg.cvt8(), jit.GPR<s8>(idx));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Write<bool>(size_t idx, bool v) {
|
void Registers::Write<bool>(size_t idx, bool v) {
|
||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
@@ -245,110 +157,4 @@ void Registers::Write<s8>(size_t idx, s8 v) {
|
|||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
template <>
|
|
||||||
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.movsx(v.cvt64(), v.cvt8());
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<s8>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.movsx(v.cvt64(), v.cvt8());
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<u8>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.movzx(v.cvt64(), v.cvt8());
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<s16>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.movsx(v.cvt64(), v.cvt16());
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<u16>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.movzx(v.cvt64(), v.cvt16());
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<s32>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.movsxd(v.cvt64(), v.cvt32());
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<u32>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.movzx(v.cvt64(), v.cvt32());
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<u64>(size_t idx, Xbyak::Reg v) {
|
|
||||||
if (idx == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
|
||||||
|
|
||||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Registers::Write<s64>(size_t idx, Xbyak::Reg v) {
|
|
||||||
Write<u64>(idx, v);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <xbyak.h>
|
|
||||||
#include <backend/core/registers/Cop1.hpp>
|
#include <backend/core/registers/Cop1.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
struct JIT;
|
|
||||||
struct Registers {
|
|
||||||
JIT &jit;
|
|
||||||
Registers(JIT &jit);
|
|
||||||
#else
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
Registers();
|
Registers();
|
||||||
void Reset();
|
void Reset();
|
||||||
@@ -59,11 +52,7 @@ struct Registers {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T Read(size_t);
|
T Read(size_t);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Read(size_t, Xbyak::Reg);
|
|
||||||
template <typename T>
|
|
||||||
void Write(size_t, T);
|
void Write(size_t, T);
|
||||||
template <typename T>
|
|
||||||
void Write(size_t, Xbyak::Reg);
|
|
||||||
|
|
||||||
std::array<s64, 32> gpr{};
|
std::array<s64, 32> gpr{};
|
||||||
|
|
||||||
@@ -71,5 +60,4 @@ struct Registers {
|
|||||||
"t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5",
|
"t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5",
|
||||||
"s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"};
|
"s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"};
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <types.hpp>
|
#include <types.hpp>
|
||||||
#include <ErrorData.hpp>
|
|
||||||
|
|
||||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||||
|
|
||||||
|
|||||||
@@ -160,24 +160,6 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
|
||||||
auto lastPC = Util::Error::GetLastPC();
|
|
||||||
if (lastPC.has_value()) {
|
|
||||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
|
||||||
if (memoryAccess.has_value()) {
|
|
||||||
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
|
||||||
ImGui::Text("%s",
|
|
||||||
std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
|
|
||||||
address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
|
||||||
.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {lastPC, memoryAccess};
|
|
||||||
}
|
|
||||||
|
|
||||||
void KaizenGui::RenderUI() {
|
void KaizenGui::RenderUI() {
|
||||||
n64::Core &core = n64::Core::GetInstance();
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
gui::StartFrame();
|
gui::StartFrame();
|
||||||
@@ -234,10 +216,6 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Util::Error::IsHandled()) {
|
|
||||||
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settingsWindow.isOpen) {
|
if (settingsWindow.isOpen) {
|
||||||
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
||||||
}
|
}
|
||||||
@@ -270,114 +248,6 @@ void KaizenGui::RenderUI() {
|
|||||||
|
|
||||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
emuThread.TogglePause();
|
|
||||||
switch (Util::Error::GetSeverity().as_enum) {
|
|
||||||
case Util::Error::Severity::WARN:
|
|
||||||
{
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
|
||||||
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
|
||||||
RenderErrorMessageDetails();
|
|
||||||
|
|
||||||
if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
|
||||||
const bool ignore = ImGui::Button("Try continuing");
|
|
||||||
ImGui::SameLine();
|
|
||||||
const bool stop = ImGui::Button("Stop emulation");
|
|
||||||
ImGui::SameLine();
|
|
||||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
|
||||||
if (ignore || stop || chooseAnother) {
|
|
||||||
Util::Error::SetHandled();
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignore) {
|
|
||||||
emuThread.TogglePause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop || chooseAnother) {
|
|
||||||
emuThread.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chooseAnother) {
|
|
||||||
fileDialogOpen = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("OK"))
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Util::Error::Severity::UNRECOVERABLE:
|
|
||||||
{
|
|
||||||
emuThread.Stop();
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
|
||||||
ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped...");
|
|
||||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
|
||||||
RenderErrorMessageDetails();
|
|
||||||
if (ImGui::Button("OK"))
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Util::Error::Severity::NON_FATAL:
|
|
||||||
{
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
|
||||||
ImGui::Text("An error has occurred!");
|
|
||||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
|
||||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
|
||||||
|
|
||||||
const bool ignore = ImGui::Button("Try continuing");
|
|
||||||
ImGui::SameLine();
|
|
||||||
const bool stop = ImGui::Button("Stop emulation");
|
|
||||||
ImGui::SameLine();
|
|
||||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
|
||||||
const bool openInDebugger =
|
|
||||||
lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
|
||||||
if (ignore || stop || chooseAnother || openInDebugger) {
|
|
||||||
Util::Error::SetHandled();
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignore) {
|
|
||||||
emuThread.TogglePause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop || chooseAnother) {
|
|
||||||
emuThread.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chooseAnother) {
|
|
||||||
fileDialogOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (openInDebugger) {
|
|
||||||
if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
|
||||||
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
|
||||||
|
|
||||||
debugger.Open();
|
|
||||||
emuThread.Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginMainStatusBar()) {
|
if (ImGui::BeginMainStatusBar()) {
|
||||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||||
ImGui::EndMainStatusBar();
|
ImGui::EndMainStatusBar();
|
||||||
|
|||||||
+32
-32
@@ -6,45 +6,45 @@
|
|||||||
#include <SDL3/SDL_gamepad.h>
|
#include <SDL3/SDL_gamepad.h>
|
||||||
|
|
||||||
class KaizenGui final {
|
class KaizenGui final {
|
||||||
gui::NativeWindow window;
|
gui::NativeWindow window;
|
||||||
public:
|
|
||||||
explicit KaizenGui() noexcept;
|
|
||||||
~KaizenGui();
|
|
||||||
|
|
||||||
double fpsCounter = -1.0;
|
public:
|
||||||
bool fastForward = false;
|
explicit KaizenGui() noexcept;
|
||||||
bool unlockFramerate = false;
|
~KaizenGui();
|
||||||
bool minimized = false;
|
|
||||||
|
|
||||||
SettingsWindow settingsWindow;
|
double fpsCounter = -1.0;
|
||||||
RenderWidget vulkanWidget;
|
bool fastForward = false;
|
||||||
EmuThread emuThread;
|
bool unlockFramerate = false;
|
||||||
Debugger debugger;
|
bool minimized = false;
|
||||||
|
|
||||||
SDL_Gamepad* gamepad = nullptr;
|
SettingsWindow settingsWindow;
|
||||||
|
RenderWidget vulkanWidget;
|
||||||
|
EmuThread emuThread;
|
||||||
|
Debugger debugger;
|
||||||
|
|
||||||
void run();
|
SDL_Gamepad *gamepad = nullptr;
|
||||||
static void LoadTAS(const std::string &path) noexcept;
|
|
||||||
void LoadROM(const std::string &path) noexcept;
|
void run();
|
||||||
private:
|
static void LoadTAS(const std::string &path) noexcept;
|
||||||
int width{}, height{};
|
void LoadROM(const std::string &path) noexcept;
|
||||||
bool aboutOpen = false;
|
|
||||||
bool fileDialogOpen = false;
|
private:
|
||||||
bool quit = false;
|
int width{}, height{};
|
||||||
bool shouldDisplaySpinner = false;
|
bool aboutOpen = false;
|
||||||
std::string fileToLoad = "";
|
bool fileDialogOpen = false;
|
||||||
void RenderUI();
|
bool quit = false;
|
||||||
void HandleInput(const SDL_Event &event);
|
bool shouldDisplaySpinner = false;
|
||||||
void QueryDevices(const SDL_Event &event);
|
std::string fileToLoad = "";
|
||||||
|
void RenderUI();
|
||||||
|
void HandleInput(const SDL_Event &event);
|
||||||
|
void QueryDevices(const SDL_Event &event);
|
||||||
|
|
||||||
|
void FileWorker() {
|
||||||
|
if (fileToLoad.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
void FileWorker() {
|
|
||||||
while (true) {
|
|
||||||
if (!fileToLoad.empty()) {
|
|
||||||
LoadROM(fileToLoad);
|
LoadROM(fileToLoad);
|
||||||
shouldDisplaySpinner = false;
|
shouldDisplaySpinner = false;
|
||||||
fileToLoad = "";
|
fileToLoad = "";
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,21 +6,17 @@
|
|||||||
|
|
||||||
CPUSettings::CPUSettings() {
|
CPUSettings::CPUSettings() {
|
||||||
auto selectedCpuType = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
auto selectedCpuType = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||||
if (selectedCpuType == "jit") {
|
if (selectedCpuType == "cached_interpreter") {
|
||||||
selectedCpuTypeIndex = 2;
|
|
||||||
} else if (selectedCpuType == "cached_interpreter") {
|
|
||||||
selectedCpuTypeIndex = 1;
|
selectedCpuTypeIndex = 1;
|
||||||
} else {
|
} else {
|
||||||
selectedCpuTypeIndex = 0;
|
selectedCpuTypeIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idleSkip = Options::GetInstance().GetValue<bool>("cpu", "idleSkip");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPUSettings::render() {
|
void CPUSettings::render() {
|
||||||
const char *items[] = {"Interpreter", "Cached Interpreter",
|
const char *items[] = {"Interpreter", "Cached Interpreter"};
|
||||||
#ifdef KAIZEN_JIT_ENABLED
|
|
||||||
"Dynamic Recompiler"
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *combo_preview_value = items[selectedCpuTypeIndex];
|
const char *combo_preview_value = items[selectedCpuTypeIndex];
|
||||||
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
|
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
|
||||||
@@ -38,16 +34,23 @@ void CPUSettings::render() {
|
|||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool showIdleSkipping = std::string(items[selectedCpuTypeIndex]) == "Cached Interpreter";
|
||||||
|
if (showIdleSkipping)
|
||||||
|
if (ImGui::Checkbox("Idle skipping", &idleSkip))
|
||||||
|
modified = true;
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
if (selectedCpuTypeIndex == 0) {
|
if (selectedCpuTypeIndex == 0) {
|
||||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
|
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
|
||||||
n64::Core::GetInstance().cpuType = n64::Core::Interpreted;
|
idleSkip = false;
|
||||||
} else if (selectedCpuTypeIndex == 1) {
|
Options::GetInstance().SetValue<bool>("cpu", "idleSkip", idleSkip);
|
||||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "cached_interpreter");
|
n64::Core::GetInstance().cpuType = n64::Core::PlainInterpreter;
|
||||||
n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter;
|
n64::Core::GetInstance().idleSkip = idleSkip;
|
||||||
} else {
|
} else {
|
||||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
Options::GetInstance().SetValue<std::string>("cpu", "type", "cached_interpreter");
|
||||||
n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler;
|
Options::GetInstance().SetValue<bool>("cpu", "idleSkip", idleSkip);
|
||||||
|
n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter;
|
||||||
|
n64::Core::GetInstance().idleSkip = idleSkip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
#include <SettingsTab.hpp>
|
#include <SettingsTab.hpp>
|
||||||
|
|
||||||
struct CPUSettings final : SettingsTab {
|
struct CPUSettings final : SettingsTab {
|
||||||
int selectedCpuTypeIndex = 0;
|
int selectedCpuTypeIndex = 0;
|
||||||
void render() override;
|
bool idleSkip = false;
|
||||||
explicit CPUSettings();
|
void render() override;
|
||||||
|
explicit CPUSettings();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,149 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
#include <types.hpp>
|
|
||||||
#include <format>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
namespace Util {
|
|
||||||
struct Error {
|
|
||||||
struct Severity {
|
|
||||||
enum {
|
|
||||||
NONE,
|
|
||||||
WARN,
|
|
||||||
NON_FATAL,
|
|
||||||
UNRECOVERABLE,
|
|
||||||
} as_enum;
|
|
||||||
|
|
||||||
[[nodiscard]] const char* as_c_str() const {
|
|
||||||
switch(as_enum) {
|
|
||||||
case NONE: return "";
|
|
||||||
case WARN: return "Warning";
|
|
||||||
case NON_FATAL: return "Error";
|
|
||||||
case UNRECOVERABLE: return "Unrecoverable Error";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MemoryAccess {
|
|
||||||
bool is_write;
|
|
||||||
enum Size {
|
|
||||||
BYTE = 8, SHORT = 16, WORD = 32, DWORD = 64
|
|
||||||
} size;
|
|
||||||
u32 address;
|
|
||||||
u64 written_val;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Type {
|
|
||||||
enum {
|
|
||||||
SCHEDULER_EOL,
|
|
||||||
SCHEDULER_UNKNOWN,
|
|
||||||
UNHANDLED_EXCEPTION,
|
|
||||||
UNHANDLED_INSTRUCTION,
|
|
||||||
INVALID_INSTRUCTION_FORMAT,
|
|
||||||
TLB_LIMIT_EXCEEDED,
|
|
||||||
TLB_INVALID_ERROR,
|
|
||||||
TLB_UNHANDLED_ERROR,
|
|
||||||
TLB_UNHANDLED_MAPPING,
|
|
||||||
JIT_BRANCH_INSIDE_DELAY_SLOT,
|
|
||||||
JIT_INVALID_X86_REG_ADDRESSING,
|
|
||||||
COULD_NOT_SYNC_SAVE_DATA,
|
|
||||||
SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE,
|
|
||||||
MMAP_MAKE_SINK_ERROR,
|
|
||||||
MEM_INVALID_ACCESS,
|
|
||||||
MEM_UNHANDLED_ACCESS,
|
|
||||||
RDP_LIMIT_EXCEEDED,
|
|
||||||
FLASH_EXECUTE_COMMAND,
|
|
||||||
PIF_UNHANDLED_CHANNEL,
|
|
||||||
UNHANDLED_COP0_STATUS_BIT,
|
|
||||||
COP0_INVALID_ACCESS,
|
|
||||||
COP0_UNHANDLED_ACCESS,
|
|
||||||
SYSTEM_DIALOG_ERROR,
|
|
||||||
TAS_LOAD_ERROR,
|
|
||||||
ROM_LOAD_ERROR,
|
|
||||||
SAVE_OPTIONS_ERROR,
|
|
||||||
DIALOG_CANCELED,
|
|
||||||
UNKNOWN_CIC_TYPE,
|
|
||||||
GAME_DB_NOT_MATCHED,
|
|
||||||
CAPSTONE_ERROR,
|
|
||||||
} as_enum;
|
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] const char* as_c_str() const {
|
|
||||||
switch(as_enum) {
|
|
||||||
case SCHEDULER_EOL: return "SCHEDULER_EOL";
|
|
||||||
case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN";
|
|
||||||
case UNHANDLED_EXCEPTION: return "UNHANDLED_EXCEPTION";
|
|
||||||
case UNHANDLED_INSTRUCTION: return "UNHANDLED_INSTRUCTION";
|
|
||||||
case INVALID_INSTRUCTION_FORMAT: return "INVALID_INSTRUCTION_FORMAT";
|
|
||||||
case TLB_LIMIT_EXCEEDED: return "TLB_LIMIT_EXCEEDED";
|
|
||||||
case TLB_INVALID_ERROR: return "TLB_INVALID_ERROR";
|
|
||||||
case TLB_UNHANDLED_ERROR: return "TLB_UNHANDLED_ERROR";
|
|
||||||
case TLB_UNHANDLED_MAPPING: return "TLB_UNHANDLED_MAPPING";
|
|
||||||
case JIT_BRANCH_INSIDE_DELAY_SLOT: return "JIT_BRANCH_INSIDE_DELAY_SLOT";
|
|
||||||
case JIT_INVALID_X86_REG_ADDRESSING: return "JIT_INVALID_X86_REG_ADDRESSING";
|
|
||||||
case COULD_NOT_SYNC_SAVE_DATA: return "COULD_NOT_SYNC_SAVE_DATA";
|
|
||||||
case SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE: return "SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE";
|
|
||||||
case MMAP_MAKE_SINK_ERROR: return "MMAP_MAKE_SINK_ERROR";
|
|
||||||
case MEM_INVALID_ACCESS: return "MEM_INVALID_ACCESS";
|
|
||||||
case MEM_UNHANDLED_ACCESS: return "MEM_UNHANDLED_ACCESS";
|
|
||||||
case RDP_LIMIT_EXCEEDED: return "RDP_LIMIT_EXCEEDED";
|
|
||||||
case FLASH_EXECUTE_COMMAND: return "FLASH_EXECUTE_COMMAND";
|
|
||||||
case PIF_UNHANDLED_CHANNEL: return "PIF_UNHANDLED_CHANNEL";
|
|
||||||
case UNHANDLED_COP0_STATUS_BIT: return "UNHANDLED_COP0_STATUS_BIT";
|
|
||||||
case COP0_INVALID_ACCESS: return "COP0_INVALID_ACCESS";
|
|
||||||
case COP0_UNHANDLED_ACCESS: return "COP0_UNHANDLED_ACCESS";
|
|
||||||
case SYSTEM_DIALOG_ERROR: return "SYSTEM_DIALOG_ERROR";
|
|
||||||
case TAS_LOAD_ERROR: return "TAS_LOAD_ERROR";
|
|
||||||
case ROM_LOAD_ERROR: return "ROM_LOAD_ERROR";
|
|
||||||
case SAVE_OPTIONS_ERROR: return "SAVE_OPTIONS_ERROR";
|
|
||||||
case DIALOG_CANCELED: return "DIALOG_CANCELED";
|
|
||||||
case UNKNOWN_CIC_TYPE: return "UNKNOWN_CIC_TYPE";
|
|
||||||
case GAME_DB_NOT_MATCHED: return "GAME_DB_NOT_MATCHED";
|
|
||||||
case CAPSTONE_ERROR: return "CAPSTONE_ERROR";
|
|
||||||
default: return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class... Args>
|
|
||||||
void Throw (const Severity severity, const Type type, const std::optional<u64> lastPC,
|
|
||||||
const std::optional<MemoryAccess> memoryAccess,
|
|
||||||
const std::format_string<Args...> fmt, Args... args) {
|
|
||||||
this->severity = severity;
|
|
||||||
this->lastPC = lastPC;
|
|
||||||
this->memoryAccess = memoryAccess;
|
|
||||||
this->type = type;
|
|
||||||
err = std::format(fmt, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Error& GetInstance() {
|
|
||||||
static Error instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string& GetError() { return GetInstance().err; }
|
|
||||||
static Severity& GetSeverity() { return GetInstance().severity; }
|
|
||||||
static Type& GetType() { return GetInstance().type; }
|
|
||||||
static std::optional<s64>& GetLastPC() { return GetInstance().lastPC; }
|
|
||||||
static std::optional<MemoryAccess>& GetMemoryAccess() { return GetInstance().memoryAccess; }
|
|
||||||
static bool IsHandled() {
|
|
||||||
return GetSeverity().as_enum == Severity::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetHandled() {
|
|
||||||
GetSeverity() = {};
|
|
||||||
GetError() = "";
|
|
||||||
GetType() = {};
|
|
||||||
GetLastPC() = {};
|
|
||||||
GetMemoryAccess() = {};
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::string err;
|
|
||||||
Severity severity = {};
|
|
||||||
Type type = {};
|
|
||||||
std::optional<s64> lastPC = {};
|
|
||||||
std::optional<MemoryAccess> memoryAccess = {};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ namespace fs = std::filesystem;
|
|||||||
struct Options {
|
struct Options {
|
||||||
Options() : file{"resources/options.ini"} {
|
Options() : file{"resources/options.ini"} {
|
||||||
auto fileExists = fs::exists("resources/options.ini");
|
auto fileExists = fs::exists("resources/options.ini");
|
||||||
if(fileExists) {
|
if (fileExists) {
|
||||||
file.read(structure);
|
file.read(structure);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,9 @@ struct Options {
|
|||||||
structure["audio"]["volumeR"] = "0.5";
|
structure["audio"]["volumeR"] = "0.5";
|
||||||
structure["audio"]["lock"] = "true";
|
structure["audio"]["lock"] = "true";
|
||||||
structure["cpu"]["type"] = "interpreter";
|
structure["cpu"]["type"] = "interpreter";
|
||||||
|
structure["cpu"]["idleSkip"] = "false";
|
||||||
|
|
||||||
if(!file.generate(structure))
|
if (!file.generate(structure))
|
||||||
panic("Couldn't generate settings' INI!");
|
panic("Couldn't generate settings' INI!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,10 +38,11 @@ struct Options {
|
|||||||
T GetValue(const std::string &key, const std::string &field);
|
T GetValue(const std::string &key, const std::string &field);
|
||||||
|
|
||||||
void Apply() {
|
void Apply() {
|
||||||
if(!file.write(structure))
|
if (!file.write(structure))
|
||||||
panic("Could not modify options on disk!");
|
panic("Could not modify options on disk!");
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
|
private:
|
||||||
mINI::INIFile file;
|
mINI::INIFile file;
|
||||||
mINI::INIStructure structure;
|
mINI::INIStructure structure;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user