Compare commits
12
Commits
6710c37adc
...
58e9cc1bc3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58e9cc1bc3 | ||
|
|
24fb2898e9 | ||
|
|
2147195774 | ||
|
|
bb97dcc23f | ||
|
|
920b77d381 | ||
|
|
430ccdab40 | ||
|
|
4f42a673a3 | ||
|
|
c9a0307878 | ||
|
|
5fbda03ceb | ||
|
|
366637aba3 | ||
|
|
609fa2fb08 | ||
|
|
e140a6d124 |
@@ -4,6 +4,7 @@ saves/
|
|||||||
.cache/
|
.cache/
|
||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.zed/
|
||||||
out/
|
out/
|
||||||
*.toml
|
*.toml
|
||||||
*.ini
|
*.ini
|
||||||
@@ -20,9 +21,11 @@ vgcore.*
|
|||||||
*.data
|
*.data
|
||||||
disasm.txt
|
disasm.txt
|
||||||
*log*.txt
|
*log*.txt
|
||||||
|
*.log
|
||||||
CMakeSettings.json
|
CMakeSettings.json
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
*.diagsession
|
*.diagsession
|
||||||
tests/
|
tests/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
resources/version.hpp
|
resources/version.hpp
|
||||||
|
__cmake_systeminformation/CMakeFiles/
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ if(APPLE)
|
|||||||
enable_language(OBJC)
|
enable_language(OBJC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(USE_JIT FALSE)
|
||||||
set(VULKAN_VALIDATION FALSE)
|
set(VULKAN_VALIDATION FALSE)
|
||||||
set(SANITIZERS FALSE)
|
set(SANITIZERS FALSE)
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
@@ -115,6 +116,9 @@ endif()
|
|||||||
set(SIMD_FLAG NULL)
|
set(SIMD_FLAG NULL)
|
||||||
|
|
||||||
if(ARM64)
|
if(ARM64)
|
||||||
|
if(USE_JIT)
|
||||||
|
message(FATAL_ERROR "JIT unsupported in aarch64 at the moment")
|
||||||
|
endif()
|
||||||
message("Defining USE_NEON...")
|
message("Defining USE_NEON...")
|
||||||
add_compile_definitions(USE_NEON)
|
add_compile_definitions(USE_NEON)
|
||||||
add_compile_definitions(SIMD_SUPPORT)
|
add_compile_definitions(SIMD_SUPPORT)
|
||||||
@@ -182,6 +186,9 @@ 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")
|
||||||
|
|||||||
+7
-5
@@ -34,7 +34,8 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsig
|
|||||||
new_context->set_num_thread_indices(num_thread_indices);
|
new_context->set_num_thread_indices(num_thread_indices);
|
||||||
new_context->set_system_handles(system_handles);
|
new_context->set_system_handles(system_handles);
|
||||||
|
|
||||||
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(), CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
|
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(),
|
||||||
|
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
|
||||||
panic("Failed to create Vulkan instance.\n");
|
panic("Failed to create Vulkan instance.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,15 +100,16 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
|
|||||||
constexpr auto sizeVert = sizeof(vertex_shader);
|
constexpr auto sizeVert = sizeof(vertex_shader);
|
||||||
constexpr auto sizeFrag = sizeof(fragment_shader);
|
constexpr auto sizeFrag = sizeof(fragment_shader);
|
||||||
|
|
||||||
fullscreen_quad_program = wsi->get_device().request_program(
|
fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast<const u32 *>(vertex_shader), sizeVert,
|
||||||
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
|
reinterpret_cast<const u32 *>(fragment_shader),
|
||||||
sizeFrag, &vertLayout, &fragLayout);
|
sizeFrag, &vertLayout, &fragLayout);
|
||||||
|
|
||||||
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
|
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
|
||||||
uintptr_t offset = 0;
|
uintptr_t offset = 0;
|
||||||
|
|
||||||
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
||||||
const size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
const size_t align =
|
||||||
|
wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
||||||
offset = aligned_rdram & align - 1;
|
offset = aligned_rdram & align - 1;
|
||||||
aligned_rdram -= offset;
|
aligned_rdram -= offset;
|
||||||
}
|
}
|
||||||
@@ -191,7 +193,7 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
void ParallelRDP::UpdateScreen<true>() const {
|
void ParallelRDP::UpdateScreen<true>() const {
|
||||||
const n64::VI& vi = n64::Core::GetMem().mmio.vi;
|
const n64::VI &vi = n64::Core::GetMem().mmio.vi;
|
||||||
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
||||||
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
||||||
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
||||||
|
|||||||
+30
-28
@@ -4,18 +4,20 @@
|
|||||||
#include <Options.hpp>
|
#include <Options.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
Core::Core() {
|
Core::Core() :
|
||||||
|
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 = Interpreted;
|
||||||
cpu = std::make_unique<Interpreter>(*mem, regs);
|
|
||||||
} else if (selectedCpu == "jit") {
|
} else if (selectedCpu == "jit") {
|
||||||
#ifndef __aarch64__
|
|
||||||
cpuType = DynamicRecompiler;
|
cpuType = DynamicRecompiler;
|
||||||
cpu = std::make_unique<JIT>(*mem, regs);
|
} else if (selectedCpu == "cached_interpreter") {
|
||||||
#else
|
cpuType = CachedInterpreter;
|
||||||
panic("JIT currently unsupported on aarch64");
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
panic("Unimplemented CPU type");
|
panic("Unimplemented CPU type");
|
||||||
}
|
}
|
||||||
@@ -30,7 +32,7 @@ void Core::Stop() {
|
|||||||
void Core::Reset() {
|
void Core::Reset() {
|
||||||
regs.Reset();
|
regs.Reset();
|
||||||
mem->Reset();
|
mem->Reset();
|
||||||
cpu->Reset();
|
interpreter.Reset();
|
||||||
if (romLoaded)
|
if (romLoaded)
|
||||||
mem->mmio.si.pif.Execute();
|
mem->mmio.si.pif.Execute();
|
||||||
}
|
}
|
||||||
@@ -62,7 +64,20 @@ void Core::LoadROM(const std::string &rom_) {
|
|||||||
romLoaded = true;
|
romLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Core::StepCPU() { return cpu->Step() + regs.PopStalledCycles(); }
|
u32 Core::StepCPU() {
|
||||||
|
if (cpuType == Interpreted)
|
||||||
|
return interpreter.Step() + regs.PopStalledCycles();
|
||||||
|
|
||||||
|
if (cpuType == CachedInterpreter)
|
||||||
|
return interpreter.ExecuteCached() + regs.PopStalledCycles();
|
||||||
|
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
|
if (cpuType == DynamicRecompiler)
|
||||||
|
return jit.Step() + regs.PopStalledCycles();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
panic("Invalid CPU type?");
|
||||||
|
}
|
||||||
|
|
||||||
void Core::StepRSP(const u32 cpuCycles) {
|
void Core::StepRSP(const u32 cpuCycles) {
|
||||||
MMIO &mmio = mem->mmio;
|
MMIO &mmio = mem->mmio;
|
||||||
@@ -91,35 +106,25 @@ void Core::Run(const float volumeL, const float volumeR) {
|
|||||||
|
|
||||||
bool broken = false;
|
bool broken = false;
|
||||||
for (int field = 0; field < mmio.vi.numFields; field++) {
|
for (int field = 0; field < mmio.vi.numFields; field++) {
|
||||||
|
Scheduler::GetInstance().HandleEvents();
|
||||||
u32 frameCycles = 0;
|
u32 frameCycles = 0;
|
||||||
for (int i = 0; i < mmio.vi.numHalflines; i++) {
|
for (int halfline = 0; halfline < mmio.vi.numHalflines; halfline++) {
|
||||||
mmio.vi.current = (i << 1) + field;
|
mmio.vi.current = (halfline << 1) + field;
|
||||||
|
|
||||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||||
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cycles < mem->mmio.vi.cyclesPerHalfline) {
|
for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) {
|
||||||
|
Scheduler::GetInstance().HandleEvents();
|
||||||
const u32 taken = StepCPU();
|
const u32 taken = StepCPU();
|
||||||
cycles += taken;
|
cycles += taken;
|
||||||
|
|
||||||
if ((broken = breakpoints.contains(regs.nextPC)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
StepRSP(taken);
|
|
||||||
frameCycles += taken;
|
frameCycles += taken;
|
||||||
|
StepRSP(taken);
|
||||||
Scheduler::GetInstance().Tick(taken);
|
Scheduler::GetInstance().Tick(taken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (broken)
|
|
||||||
break;
|
|
||||||
|
|
||||||
cycles -= mmio.vi.cyclesPerHalfline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (broken)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||||
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
||||||
}
|
}
|
||||||
@@ -127,8 +132,5 @@ void Core::Run(const float volumeL, const float volumeR) {
|
|||||||
mmio.ai.Step(frameCycles, volumeL, volumeR);
|
mmio.ai.Step(frameCycles, volumeL, volumeR);
|
||||||
Scheduler::GetInstance().Tick(frameCycles);
|
Scheduler::GetInstance().Tick(frameCycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (broken)
|
|
||||||
pause = true;
|
|
||||||
}
|
}
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
+19
-16
@@ -1,33 +1,32 @@
|
|||||||
#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>
|
#include <backend/core/JIT.hpp>
|
||||||
|
#endif
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <variant>
|
#include <Registers.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core {
|
struct Core {
|
||||||
enum CPUType {
|
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter;
|
||||||
Interpreted,
|
|
||||||
DynamicRecompiler,
|
|
||||||
CachedInterpreter
|
|
||||||
} cpuType = Interpreted;
|
|
||||||
|
|
||||||
explicit Core();
|
explicit Core();
|
||||||
|
|
||||||
static Core& GetInstance() {
|
static Core &GetInstance() {
|
||||||
static Core instance;
|
static Core instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Registers& GetRegs() {
|
static inline bool IsAddressError(u8 mask, u64 vaddr) {
|
||||||
return GetInstance().regs;
|
auto regs = GetRegs();
|
||||||
|
return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Mem& GetMem() {
|
static Registers &GetRegs() { return GetInstance().regs; }
|
||||||
return *GetInstance().mem;
|
|
||||||
}
|
static Mem &GetMem() { return *GetInstance().mem; }
|
||||||
|
|
||||||
u32 StepCPU();
|
u32 StepCPU();
|
||||||
void StepRSP(u32 cpuCycles);
|
void StepRSP(u32 cpuCycles);
|
||||||
@@ -38,7 +37,7 @@ struct Core {
|
|||||||
void Run(float volumeL, float volumeR);
|
void Run(float volumeL, float volumeR);
|
||||||
void TogglePause() { pause = !pause; }
|
void TogglePause() { pause = !pause; }
|
||||||
inline void ToggleBreakpoint(s64 addr) {
|
inline void ToggleBreakpoint(s64 addr) {
|
||||||
if(breakpoints.contains(addr)) {
|
if (breakpoints.contains(addr)) {
|
||||||
breakpoints.erase(addr);
|
breakpoints.erase(addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -49,14 +48,18 @@ struct Core {
|
|||||||
bool pause = true;
|
bool pause = true;
|
||||||
bool romLoaded = false;
|
bool romLoaded = false;
|
||||||
int slot = 0;
|
int slot = 0;
|
||||||
u32 cycles = 0;
|
|
||||||
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>();
|
||||||
std::unique_ptr<BaseCPU> cpu;
|
|
||||||
|
|
||||||
Registers regs;
|
Registers regs;
|
||||||
|
#endif
|
||||||
|
Interpreter interpreter;
|
||||||
ParallelRDP parallel;
|
ParallelRDP parallel;
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
+29
-13
@@ -5,22 +5,34 @@ void Scheduler::EnqueueRelative(const u64 t, const EventType type) { EnqueueAbso
|
|||||||
|
|
||||||
void Scheduler::EnqueueAbsolute(const u64 t, const EventType type) { events.push({t, type}); }
|
void Scheduler::EnqueueAbsolute(const u64 t, const EventType type) { events.push({t, type}); }
|
||||||
|
|
||||||
u64 Scheduler::Remove(const EventType eventType) const {
|
Event *Scheduler::Find(const EventType eventType) const {
|
||||||
for (auto &[time, type] : events) {
|
for (auto &event : events) {
|
||||||
if (type == eventType) {
|
if (event.type == eventType) {
|
||||||
const u64 ret = time - ticks;
|
const u64 ret = event.time - ticks;
|
||||||
type = NONE;
|
return &event;
|
||||||
time = ticks;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::Tick(const u64 t) {
|
u64 Scheduler::Remove(const EventType eventType) const {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
auto event = Find(eventType);
|
||||||
ticks += t;
|
if (!event)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const u64 ret = event->time - ticks;
|
||||||
|
event->type = NONE;
|
||||||
|
event->time = ticks;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::SkipToNext() { ticks = events.top().time; }
|
||||||
|
|
||||||
|
void Scheduler::Tick(const u64 t) { ticks += t; }
|
||||||
|
|
||||||
|
void Scheduler::HandleEvents() {
|
||||||
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
n64::MI &mi = mem.mmio.mi;
|
n64::MI &mi = mem.mmio.mi;
|
||||||
n64::SI &si = mem.mmio.si;
|
n64::SI &si = mem.mmio.si;
|
||||||
n64::PI &pi = mem.mmio.pi;
|
n64::PI &pi = mem.mmio.pi;
|
||||||
@@ -40,10 +52,14 @@ void Scheduler::Tick(const u64 t) {
|
|||||||
case NONE:
|
case NONE:
|
||||||
break;
|
break;
|
||||||
case IMPOSSIBLE:
|
case IMPOSSIBLE:
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness");
|
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
||||||
|
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
|
||||||
|
"Unrecognized rom endianness");
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast<int>(type));
|
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
||||||
|
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
|
||||||
|
"Unknown scheduler event type {}", static_cast<int>(type));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
events.pop();
|
events.pop();
|
||||||
|
|||||||
@@ -35,10 +35,13 @@ struct Scheduler {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HandleEvents();
|
||||||
void EnqueueRelative(u64, EventType);
|
void EnqueueRelative(u64, EventType);
|
||||||
void EnqueueAbsolute(u64, EventType);
|
void EnqueueAbsolute(u64, EventType);
|
||||||
[[nodiscard]] u64 Remove(EventType) const;
|
[[nodiscard]] u64 Remove(EventType) const;
|
||||||
|
[[nodiscard]] Event *Find(EventType) const;
|
||||||
void Tick(u64 t);
|
void Tick(u64 t);
|
||||||
|
void SkipToNext();
|
||||||
|
|
||||||
u8 index = 0;
|
u8 index = 0;
|
||||||
u64 ticks = 0;
|
u64 ticks = 0;
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Mem.hpp>
|
|
||||||
#include <Registers.hpp>
|
|
||||||
#include <Disassembler.hpp>
|
|
||||||
|
|
||||||
namespace n64 {
|
|
||||||
struct alignas(32) InstructionCache {
|
|
||||||
bool valid;
|
|
||||||
u32 data[8];
|
|
||||||
u32 ptag;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
|
||||||
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct alignas(32) DataCache {
|
|
||||||
bool valid, dirty;
|
|
||||||
u8 data[16];
|
|
||||||
u32 ptag;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
|
||||||
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BaseCPU {
|
|
||||||
virtual ~BaseCPU() = default;
|
|
||||||
virtual u32 Step() = 0;
|
|
||||||
virtual void Reset() = 0;
|
|
||||||
};
|
|
||||||
} // namespace n64
|
|
||||||
@@ -2,7 +2,7 @@ file(GLOB SOURCES *.cpp)
|
|||||||
file(GLOB HEADERS *.hpp)
|
file(GLOB HEADERS *.hpp)
|
||||||
|
|
||||||
add_subdirectory(interpreter)
|
add_subdirectory(interpreter)
|
||||||
if(NOT ARM64)
|
if(USE_JIT)
|
||||||
add_subdirectory(jit)
|
add_subdirectory(jit)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(mem)
|
add_subdirectory(mem)
|
||||||
@@ -12,6 +12,6 @@ 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(NOT ARM64)
|
if(USE_JIT)
|
||||||
target_link_libraries(core PRIVATE jit)
|
target_link_libraries(core PRIVATE jit)
|
||||||
endif()
|
endif()
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#include <Core.hpp>
|
||||||
|
|
||||||
|
namespace n64 {
|
||||||
|
void InstructionCache::FillLine(u64 vaddr, u32 paddr) {
|
||||||
|
int index = GetICacheLineIndex(vaddr);
|
||||||
|
auto &line = lines[index];
|
||||||
|
line.valid = true;
|
||||||
|
line.ptag = paddr & ~0x00000fff;
|
||||||
|
auto &mmio = Core::GetMem().mmio;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
line.data[i] = mmio.rdp.ReadRDRAM<u32>(line.ptag | index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) {
|
||||||
|
auto &line = lines[GetICacheLineIndex(vaddr)];
|
||||||
|
|
||||||
|
line.valid = (cop0.tagLo.pstate >> 1) & 1;
|
||||||
|
line.ptag = ptag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) {
|
||||||
|
auto &line = lines[GetDCacheLineIndex(vaddr)];
|
||||||
|
|
||||||
|
line.valid = (cop0.tagLo.pstate >> 1) & 1;
|
||||||
|
line.dirty = (cop0.tagLo.pstate >> 0) & 1;
|
||||||
|
line.ptag = ptag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionCache::LoadTag(u64 vaddr) {
|
||||||
|
auto &cop0 = Core::GetRegs().cop0;
|
||||||
|
auto &line = lines[GetICacheLineIndex(vaddr)];
|
||||||
|
cop0.tagLo.pstate = line.valid << 1;
|
||||||
|
cop0.tagLo.ptaglo = line.ptag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataCache::LoadTag(u64 vaddr) {
|
||||||
|
auto &cop0 = Core::GetRegs().cop0;
|
||||||
|
auto &line = lines[GetDCacheLineIndex(vaddr)];
|
||||||
|
cop0.tagLo.pstate = (line.valid << 1) | line.dirty;
|
||||||
|
cop0.tagLo.ptaglo = line.ptag;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DataCache::WriteBack<false>(u64 vaddr, u32 paddr) {
|
||||||
|
auto &mmio = Core::GetMem().mmio;
|
||||||
|
|
||||||
|
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
|
||||||
|
if (!line.valid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||||
|
u32 lineStart = GetDCacheLineStart(origPhysAddr);
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
line.dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DataCache::WriteBack<true>(u64 vaddr, u32 paddr) {
|
||||||
|
WriteBack<false>(vaddr, paddr);
|
||||||
|
lines[GetDCacheLineIndex(vaddr)].valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DataCache::WriteBack<false>(u64 vaddr, u32 paddr, u32 ptag) {
|
||||||
|
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
|
||||||
|
if (line.ptag == ptag)
|
||||||
|
WriteBack(vaddr, paddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void DataCache::WriteBack<true>(u64 vaddr, u32 paddr, u32 ptag) {
|
||||||
|
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
|
||||||
|
if (line.ptag == ptag)
|
||||||
|
WriteBack<true>(vaddr, paddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) {
|
||||||
|
auto &mmio = Core::GetMem().mmio;
|
||||||
|
ICacheLine &line = lines[GetICacheLineIndex(vaddr)];
|
||||||
|
|
||||||
|
if (line.ptag == ptag && line.valid) {
|
||||||
|
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||||
|
u32 lineStart = GetICacheLineStart(origPhysAddr);
|
||||||
|
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace n64
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <common.hpp>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace n64 {
|
||||||
|
struct alignas(32) ICacheLine {
|
||||||
|
bool valid;
|
||||||
|
u32 data[8];
|
||||||
|
u32 ptag;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct alignas(32) DCacheLine {
|
||||||
|
bool valid, dirty;
|
||||||
|
u8 data[16];
|
||||||
|
u32 ptag;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cop0;
|
||||||
|
|
||||||
|
inline const u32 GetPhysicalAddressPTag(const u32 paddr) { return paddr >> 12; }
|
||||||
|
inline const int GetICacheLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
||||||
|
inline const u32 GetICacheLineStart(u64 paddr) { return paddr & ~0x1F; }
|
||||||
|
inline const int GetDCacheLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
||||||
|
inline const u32 GetDCacheLineStart(u64 paddr) { return paddr & ~0xF; }
|
||||||
|
|
||||||
|
struct InstructionCache {
|
||||||
|
std::array<ICacheLine, 512> lines;
|
||||||
|
|
||||||
|
void FillLine(u64, u32);
|
||||||
|
void InvalidateIndex(u64 vaddr) { lines[GetICacheLineIndex(vaddr)].valid = false; }
|
||||||
|
void StoreTag(u64, u32, Cop0 &);
|
||||||
|
void LoadTag(u64 vaddr);
|
||||||
|
|
||||||
|
void WriteBack(u64 vaddr, u32 paddr, u32 ptag);
|
||||||
|
void InvalidateIndex(u64 vaddr, u32 ptag) {
|
||||||
|
int lineIndex = GetICacheLineIndex(vaddr);
|
||||||
|
if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag)
|
||||||
|
lines[lineIndex].valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DataCache {
|
||||||
|
std::array<DCacheLine, 512> lines;
|
||||||
|
|
||||||
|
void StoreTag(u64, u32, Cop0 &);
|
||||||
|
void LoadTag(u64 vaddr);
|
||||||
|
template <bool invalidate = false>
|
||||||
|
void WriteBack(u64 vaddr, u32 paddr);
|
||||||
|
template <bool invalidate = false>
|
||||||
|
void WriteBack(u64 vaddr, u32 paddr, u32 ptag);
|
||||||
|
void InvalidateIndex(u64 vaddr) { lines[GetDCacheLineIndex(vaddr)].valid = false; }
|
||||||
|
void InvalidateIndex(u64 vaddr, u32 ptag) {
|
||||||
|
int lineIndex = GetDCacheLineIndex(vaddr);
|
||||||
|
if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag)
|
||||||
|
lines[lineIndex].valid = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace n64
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
|
#include <Scheduler.hpp>
|
||||||
|
#include "jit/helpers.hpp"
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
Interpreter::Interpreter(Mem& mem, Registers& regs) : regs(regs), mem(mem) {}
|
Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {}
|
||||||
|
|
||||||
bool Interpreter::ShouldServiceInterrupt() const {
|
bool Interpreter::ShouldServiceInterrupt() const {
|
||||||
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
||||||
@@ -12,7 +14,7 @@ bool Interpreter::ShouldServiceInterrupt() const {
|
|||||||
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
|
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::CheckCompareInterrupt() const {
|
void Interpreter::UpdateCompareInterrupt() const {
|
||||||
regs.cop0.count++;
|
regs.cop0.count++;
|
||||||
regs.cop0.count &= 0x1FFFFFFFF;
|
regs.cop0.count &= 0x1FFFFFFFF;
|
||||||
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
||||||
@@ -21,38 +23,159 @@ void Interpreter::CheckCompareInterrupt() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Interpreter::Step() {
|
bool Interpreter::Fetch(Instruction &instr, u64 vaddr) {
|
||||||
CheckCompareInterrupt();
|
u32 paddr = 0;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
||||||
|
regs.cop0.HandleTLBException(vaddr);
|
||||||
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, vaddr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
instr = mem.Read<u32>(paddr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interpreter::MaybeAdvance() {
|
||||||
|
UpdateCompareInterrupt();
|
||||||
|
|
||||||
regs.prevDelaySlot = regs.delaySlot;
|
regs.prevDelaySlot = regs.delaySlot;
|
||||||
regs.delaySlot = false;
|
regs.delaySlot = false;
|
||||||
|
|
||||||
if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] {
|
if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] {
|
||||||
regs.cop0.HandleTLBException(regs.pc);
|
regs.cop0.HandleTLBException(regs.pc);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 paddr = 0;
|
|
||||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) {
|
|
||||||
regs.cop0.HandleTLBException(regs.pc);
|
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 instruction = mem.Read<u32>(paddr);
|
|
||||||
|
|
||||||
if (ShouldServiceInterrupt()) {
|
if (ShouldServiceInterrupt()) {
|
||||||
regs.cop0.FireException(ExceptionCode::Interrupt, 0, regs.pc);
|
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs.oldPC = regs.pc;
|
regs.oldPC = regs.pc;
|
||||||
regs.pc = regs.nextPC;
|
regs.pc = regs.nextPC;
|
||||||
regs.nextPC += 4;
|
regs.nextPC += 4;
|
||||||
|
|
||||||
Exec(instruction);
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) {
|
||||||
|
UpdateCompareInterrupt();
|
||||||
|
|
||||||
|
regs.prevDelaySlot = regs.delaySlot;
|
||||||
|
regs.delaySlot = false;
|
||||||
|
|
||||||
|
if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] {
|
||||||
|
regs.cop0.HandleTLBException(regs.pc);
|
||||||
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Fetch(instr, regs.pc))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ShouldServiceInterrupt()) {
|
||||||
|
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
regs.oldPC = regs.pc;
|
||||||
|
regs.pc = regs.nextPC;
|
||||||
|
regs.nextPC += 4;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Interpreter::Step() {
|
||||||
|
Instruction instr;
|
||||||
|
if (!FetchThenMaybeAdvance(instr))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
DecodeExecute(instr);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 Interpreter::CacheBlock(u32 addr) {
|
||||||
|
u32 blockAddr = addr;
|
||||||
|
|
||||||
|
CachedLine line;
|
||||||
|
u32 i;
|
||||||
|
bool fetchDelaySlot = false;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_INSTR_PER_BLOCK; i++) {
|
||||||
|
Instruction instr;
|
||||||
|
if (!Fetch(instr, addr))
|
||||||
|
return i + 1;
|
||||||
|
|
||||||
|
addr += 4;
|
||||||
|
line.code[i] = instr;
|
||||||
|
|
||||||
|
if (fetchDelaySlot) {
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InstrEndsBlock(instr)) {
|
||||||
|
if (InstrHasDelaySlot(instr) && !fetchDelaySlot) {
|
||||||
|
fetchDelaySlot = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
i = 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line.cycles = i;
|
||||||
|
line.len = i;
|
||||||
|
cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line);
|
||||||
|
|
||||||
|
return ExecuteCached();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Interpreter::ExecuteCached() {
|
||||||
|
u32 addr = regs.pc;
|
||||||
|
auto &blocks = cachedState.blocks;
|
||||||
|
|
||||||
|
if (!blocks[CACHE_GET_BLOCK(addr)]) {
|
||||||
|
blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock<cachedState.MAX_LINES / 4>();
|
||||||
|
return CacheBlock(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto line = blocks[CACHE_GET_BLOCK(addr)]->lines[CACHE_GET_LINE(addr)];
|
||||||
|
if (line) {
|
||||||
|
cachedState.exception = false;
|
||||||
|
// i copy the block cycles here in case the block evicts itself when executing which would set the cycles to
|
||||||
|
// 0, making so the emulator halts cause the outer loop won't advance
|
||||||
|
const auto blockCycles = line->cycles;
|
||||||
|
for (u32 i = 0; i < line->len; i++) {
|
||||||
|
if (!MaybeAdvance())
|
||||||
|
return i + 1;
|
||||||
|
|
||||||
|
Instruction instr = line->code[i];
|
||||||
|
DecodeExecute(instr);
|
||||||
|
|
||||||
|
if (cachedState.exception)
|
||||||
|
return i + 1;
|
||||||
|
|
||||||
|
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
||||||
|
if (IsBranchLikely(instr) && !regs.delaySlot)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line->idleSkip) {
|
||||||
|
Scheduler::GetInstance().SkipToNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockCycles == 0)
|
||||||
|
panic("Cycles are 0");
|
||||||
|
|
||||||
|
return blockCycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CacheBlock(addr);
|
||||||
|
}
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -1,34 +1,53 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <BaseCPU.hpp>
|
#include <Cache.hpp>
|
||||||
#include <Mem.hpp>
|
#include <Mem.hpp>
|
||||||
#include <vector>
|
#include <JITUtils.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core;
|
struct Core;
|
||||||
|
|
||||||
struct Interpreter final : BaseCPU {
|
struct Interpreter final {
|
||||||
explicit Interpreter(Mem&, Registers&);
|
explicit Interpreter(Mem &, Registers &);
|
||||||
~Interpreter() override = default;
|
~Interpreter() = default;
|
||||||
u32 Step() override;
|
u32 Step();
|
||||||
|
u32 ExecuteCached();
|
||||||
|
bool FetchThenMaybeAdvance(Instruction &);
|
||||||
|
bool MaybeAdvance();
|
||||||
|
u32 CacheBlock(u32 addr);
|
||||||
|
|
||||||
void Reset() override {
|
void SignalException(u32 addr) { cachedState.exception = true; }
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
cop2Latch = {};
|
cop2Latch = {};
|
||||||
|
cachedState.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
CachedState<12, std::numeric_limits<u32>::max()> cachedState;
|
||||||
Registers& regs;
|
|
||||||
Mem& mem;
|
private:
|
||||||
u64 cop2Latch{};
|
|
||||||
friend struct Cop1;
|
friend struct Cop1;
|
||||||
#define check_address_error(mask, vaddr) \
|
friend struct Mem;
|
||||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
|
||||||
|
void MaybeIdleSkip();
|
||||||
|
|
||||||
|
InstructionCache icache;
|
||||||
|
DataCache dcache;
|
||||||
|
Registers ®s;
|
||||||
|
Mem &mem;
|
||||||
|
u64 cop2Latch{};
|
||||||
|
u32 rspSyncCount = 0;
|
||||||
|
|
||||||
|
bool Fetch(Instruction &, u64);
|
||||||
|
void CacheTypeData(u8, u64, u32, u32);
|
||||||
|
void CacheTypeInstruction(u8, u64, u32, u32);
|
||||||
|
|
||||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||||
void CheckCompareInterrupt() const;
|
void UpdateCompareInterrupt() const;
|
||||||
|
|
||||||
void cop2Decode(Instruction);
|
void cop2Decode(Instruction);
|
||||||
void special(Instruction);
|
void special(Instruction);
|
||||||
void regimm(Instruction);
|
void regimm(Instruction);
|
||||||
void Exec(Instruction);
|
void DecodeExecute(Instruction);
|
||||||
void add(Instruction);
|
void add(Instruction);
|
||||||
void addu(Instruction);
|
void addu(Instruction);
|
||||||
void addi(Instruction);
|
void addi(Instruction);
|
||||||
@@ -41,6 +60,7 @@ private:
|
|||||||
void blink(Instruction, bool);
|
void blink(Instruction, bool);
|
||||||
void bl(Instruction, bool);
|
void bl(Instruction, bool);
|
||||||
void bllink(Instruction, bool);
|
void bllink(Instruction, bool);
|
||||||
|
void cache(Instruction);
|
||||||
void dadd(Instruction);
|
void dadd(Instruction);
|
||||||
void daddu(Instruction);
|
void daddu(Instruction);
|
||||||
void daddi(Instruction);
|
void daddi(Instruction);
|
||||||
|
|||||||
+24
-27
@@ -1,11 +1,9 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <jit/helpers.hpp>
|
#include <JIT.hpp>
|
||||||
|
#include <Disassembler.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
#ifndef __aarch64__
|
JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) {
|
||||||
JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) {
|
|
||||||
regs.SetJIT(this);
|
|
||||||
mem.SetJIT(this);
|
|
||||||
blockCache.resize(kUpperSize);
|
blockCache.resize(kUpperSize);
|
||||||
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
||||||
CS_ERR_OK) {
|
CS_ERR_OK) {
|
||||||
@@ -43,12 +41,12 @@ void JIT::InvalidateBlock(const u32 paddr) {
|
|||||||
std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
||||||
u32 paddr = 0;
|
u32 paddr = 0;
|
||||||
|
|
||||||
if (check_address_error(0b11, vaddr)) [[unlikely]] {
|
if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
|
||||||
/*regs.cop0.HandleTLBException(blockPC);
|
/*regs.cop0.HandleTLBException(blockPC);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC);
|
||||||
return 1;*/
|
return 1;*/
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
blockPC, {},
|
||||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -91,7 +89,7 @@ void JIT::SetPC64(const s64 val) {
|
|||||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::SetPC32(const Xbyak::Reg32& val) {
|
void JIT::SetPC32(const Xbyak::Reg32 &val) {
|
||||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
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 + OLD_PC_OFFSET], code.SCR1);
|
||||||
code.movsxd(val.cvt64(), val);
|
code.movsxd(val.cvt64(), val);
|
||||||
@@ -100,7 +98,7 @@ void JIT::SetPC32(const Xbyak::Reg32& val) {
|
|||||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::SetPC64(const Xbyak::Reg64& val) {
|
void JIT::SetPC64(const Xbyak::Reg64 &val) {
|
||||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
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 + OLD_PC_OFFSET], code.SCR1);
|
||||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||||
@@ -118,8 +116,8 @@ u32 JIT::Step() {
|
|||||||
/*regs.cop0.HandleTLBException(blockPC);
|
/*regs.cop0.HandleTLBException(blockPC);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
||||||
return 1;*/
|
return 1;*/
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
Util::Error::GetInstance().Throw(
|
||||||
blockPC, {},
|
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
||||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -162,7 +160,7 @@ u32 JIT::Step() {
|
|||||||
|
|
||||||
auto instruction = FetchInstruction(blockPC);
|
auto instruction = FetchInstruction(blockPC);
|
||||||
|
|
||||||
if(!instruction)
|
if (!instruction)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
instructionsInBlock++;
|
instructionsInBlock++;
|
||||||
@@ -171,15 +169,15 @@ u32 JIT::Step() {
|
|||||||
blockPC = blockNextPC;
|
blockPC = blockNextPC;
|
||||||
blockNextPC += 4;
|
blockNextPC += 4;
|
||||||
|
|
||||||
if(InstrEndsBlock(instruction.value())) {
|
if (InstrEndsBlock(instruction.value())) {
|
||||||
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
||||||
if(!delay_instruction)
|
if (!delay_instruction)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(InstrEndsBlock(delay_instruction.value())) {
|
if (InstrEndsBlock(delay_instruction.value())) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
"[JIT]: Unhandled case of branch from delay slot!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +190,7 @@ u32 JIT::Step() {
|
|||||||
Emit(delay_instruction.value());
|
Emit(delay_instruction.value());
|
||||||
Emit(instruction.value());
|
Emit(instruction.value());
|
||||||
|
|
||||||
if(!branch_taken) {
|
if (!branch_taken) {
|
||||||
Xbyak::Label runtime_branch_taken;
|
Xbyak::Label runtime_branch_taken;
|
||||||
code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]);
|
code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]);
|
||||||
code.cmp(code.SCR1, 0);
|
code.cmp(code.SCR1, 0);
|
||||||
@@ -206,7 +204,8 @@ u32 JIT::Step() {
|
|||||||
code.L(runtime_branch_taken);
|
code.L(runtime_branch_taken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(branch_taken) branch_taken = false;
|
if (branch_taken)
|
||||||
|
branch_taken = false;
|
||||||
|
|
||||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||||
|
|
||||||
@@ -227,7 +226,8 @@ u32 JIT::Step() {
|
|||||||
blockInfoSize = code.getSize() - blockInfoSize;
|
blockInfoSize = code.getSize() - blockInfoSize;
|
||||||
|
|
||||||
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
|
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);
|
const auto count =
|
||||||
|
cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
for (size_t j = 0; j < count; j++) {
|
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);
|
info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str);
|
||||||
@@ -239,8 +239,5 @@ u32 JIT::Step() {
|
|||||||
return block();
|
return block();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::DumpBlockCacheToDisk() const {
|
void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode<u8 *>(), code.getSize(), "jit.dump"); }
|
||||||
ircolib::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
+32
-47
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <BaseCPU.hpp>
|
#include <Cache.hpp>
|
||||||
|
#include <Registers.hpp>
|
||||||
#include <Mem.hpp>
|
#include <Mem.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <xbyak.h>
|
#include <xbyak.h>
|
||||||
@@ -25,15 +26,12 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
|
|||||||
#define HI_OFFSET (reinterpret_cast<uintptr_t>(®s.hi) - 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))
|
#define LO_OFFSET (reinterpret_cast<uintptr_t>(®s.lo) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
|
||||||
#ifdef __aarch64__
|
struct JIT final {
|
||||||
struct JIT : BaseCPU {};
|
explicit JIT(Mem &, Registers &);
|
||||||
#else
|
~JIT() = default;
|
||||||
struct JIT final : BaseCPU {
|
u32 Step();
|
||||||
explicit JIT(Mem&, Registers&);
|
|
||||||
~JIT() override = default;
|
|
||||||
u32 Step() override;
|
|
||||||
|
|
||||||
void Reset() override {
|
void Reset() {
|
||||||
code.reset();
|
code.reset();
|
||||||
blockCache = {};
|
blockCache = {};
|
||||||
blockCache.resize(kUpperSize);
|
blockCache.resize(kUpperSize);
|
||||||
@@ -47,14 +45,15 @@ struct JIT final : BaseCPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateBlock(u32);
|
void InvalidateBlock(u32);
|
||||||
private:
|
|
||||||
|
private:
|
||||||
friend struct Cop1;
|
friend struct Cop1;
|
||||||
friend struct Registers;
|
friend struct Registers;
|
||||||
using BlockFn = int (*)();
|
using BlockFn = int (*)();
|
||||||
|
|
||||||
bool branch_taken;
|
bool branch_taken;
|
||||||
Registers& regs;
|
Registers ®s;
|
||||||
Mem& mem;
|
Mem &mem;
|
||||||
u64 cop2Latch{};
|
u64 cop2Latch{};
|
||||||
s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
|
s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
|
||||||
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
|
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
|
||||||
@@ -73,9 +72,9 @@ private:
|
|||||||
return code.qword[code.rbp + GPR_OFFSET(index)];
|
return code.qword[code.rbp + GPR_OFFSET(index)];
|
||||||
}
|
}
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
||||||
{Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING},
|
{Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Invalid register addressing mode {}!", sizeof(T));
|
"[JIT]: Invalid register addressing mode {}!", sizeof(T));
|
||||||
return Xbyak::Address{0};
|
return Xbyak::Address{0};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,17 +108,14 @@ private:
|
|||||||
|
|
||||||
void SetPC32(s32 val);
|
void SetPC32(s32 val);
|
||||||
void SetPC64(s64 val);
|
void SetPC64(s64 val);
|
||||||
void SetPC32(const Xbyak::Reg32& val);
|
void SetPC32(const Xbyak::Reg32 &val);
|
||||||
void SetPC64(const Xbyak::Reg64& val);
|
void SetPC64(const Xbyak::Reg64 &val);
|
||||||
void BranchNotTaken();
|
void BranchNotTaken();
|
||||||
void BranchTaken(s64 offs);
|
void BranchTaken(s64 offs);
|
||||||
void BranchTaken(const Xbyak::Reg64 &offs);
|
void BranchTaken(const Xbyak::Reg64 &offs);
|
||||||
void BranchAbsTaken(s64 addr);
|
void BranchAbsTaken(s64 addr);
|
||||||
void BranchAbsTaken(const Xbyak::Reg64 &addr);
|
void BranchAbsTaken(const Xbyak::Reg64 &addr);
|
||||||
|
|
||||||
#define check_address_error(mask, vaddr) \
|
|
||||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
|
||||||
|
|
||||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||||
void CheckCompareInterrupt() const;
|
void CheckCompareInterrupt() const;
|
||||||
std::optional<u32> FetchInstruction(s64);
|
std::optional<u32> FetchInstruction(s64);
|
||||||
@@ -205,54 +201,44 @@ private:
|
|||||||
void mtlo(Instruction);
|
void mtlo(Instruction);
|
||||||
void nor(Instruction);
|
void nor(Instruction);
|
||||||
void sb(const Instruction) {
|
void sb(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sb'!");
|
blockPC, {}, "[JIT]: Unhandled 'sb'!");
|
||||||
}
|
}
|
||||||
void sc(const Instruction) {
|
void sc(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sc'!");
|
blockPC, {}, "[JIT]: Unhandled 'sc'!");
|
||||||
}
|
}
|
||||||
void scd(const Instruction) {
|
void scd(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'scd'!");
|
blockPC, {}, "[JIT]: Unhandled 'scd'!");
|
||||||
}
|
}
|
||||||
void sd(const Instruction) {
|
void sd(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sd'!");
|
blockPC, {}, "[JIT]: Unhandled 'sd'!");
|
||||||
}
|
}
|
||||||
void sdc1(const Instruction) {
|
void sdc1(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
|
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
|
||||||
}
|
}
|
||||||
void sdl(const Instruction) {
|
void sdl(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
|
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
|
||||||
}
|
}
|
||||||
void sdr(const Instruction) {
|
void sdr(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
|
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
|
||||||
}
|
}
|
||||||
void sh(const Instruction) {
|
void sh(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
||||||
}
|
}
|
||||||
void sw(Instruction);
|
void sw(Instruction);
|
||||||
void swl(const Instruction) {
|
void swl(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'swl'!");
|
blockPC, {}, "[JIT]: Unhandled 'swl'!");
|
||||||
}
|
}
|
||||||
void swr(const Instruction) {
|
void swr(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
||||||
}
|
}
|
||||||
void slti(Instruction);
|
void slti(Instruction);
|
||||||
@@ -264,23 +250,22 @@ private:
|
|||||||
void sub(Instruction);
|
void sub(Instruction);
|
||||||
void subu(Instruction);
|
void subu(Instruction);
|
||||||
void swc1(const Instruction) {
|
void swc1(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
"[JIT]: Unhandled case of branch from delay slot!");
|
||||||
}
|
}
|
||||||
void sra(Instruction);
|
void sra(Instruction);
|
||||||
void srav(Instruction);
|
void srav(Instruction);
|
||||||
void srl(Instruction);
|
void srl(Instruction);
|
||||||
void srlv(Instruction);
|
void srlv(Instruction);
|
||||||
void trap(bool) {
|
void trap(bool) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
"[JIT]: Unhandled case of branch from delay slot!");
|
||||||
}
|
}
|
||||||
void or_(Instruction);
|
void or_(Instruction);
|
||||||
void ori(Instruction);
|
void ori(Instruction);
|
||||||
void xor_(Instruction);
|
void xor_(Instruction);
|
||||||
void xori(Instruction);
|
void xori(Instruction);
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Instruction.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace n64 {
|
||||||
|
static constexpr u32 MAX_INSTR_PER_BLOCK = 128;
|
||||||
|
|
||||||
|
#define CACHE_GET_BLOCK(addr) (addr / (cachedState.MAX_LINES))
|
||||||
|
#define CACHE_GET_LINE(addr) ((addr & ((cachedState.MAX_LINES) - 1)) >> 2)
|
||||||
|
|
||||||
|
struct CachedLine {
|
||||||
|
bool idleSkip = false;
|
||||||
|
std::array<Instruction, MAX_INSTR_PER_BLOCK> code = {};
|
||||||
|
u32 len = 0;
|
||||||
|
u32 cycles = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <u32 lineAmount>
|
||||||
|
struct CachedBlock {
|
||||||
|
CachedBlock() { lines.resize(lineAmount); }
|
||||||
|
std::vector<CachedLine *> lines = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <u32 blockBits, u64 addressSpace>
|
||||||
|
struct CachedState {
|
||||||
|
static constexpr u32 MAX_LINES = 1 << blockBits;
|
||||||
|
std::vector<CachedBlock<MAX_LINES / 4> *> blocks = {};
|
||||||
|
bool exception = false;
|
||||||
|
|
||||||
|
void EvictCachedBlock(u32 addr) { blocks[addr / MAX_LINES] = {}; }
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
for (auto block : blocks) {
|
||||||
|
if (block)
|
||||||
|
for (auto line : block->lines)
|
||||||
|
delete line;
|
||||||
|
|
||||||
|
delete block;
|
||||||
|
}
|
||||||
|
blocks = {};
|
||||||
|
blocks.resize((addressSpace + 1) / MAX_LINES);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace n64
|
||||||
+181
-159
@@ -4,9 +4,14 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <Options.hpp>
|
#include <Options.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");
|
||||||
@@ -19,9 +24,9 @@ void Mem::Reset() {
|
|||||||
std::error_code error;
|
std::error_code error;
|
||||||
saveData.sync(error);
|
saveData.sync(error);
|
||||||
if (error) {
|
if (error) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
|
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
|
||||||
{}, {}, "[Mem]: Could not sync save data!");
|
"[Mem]: Could not sync save data!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveData.unmap();
|
saveData.unmap();
|
||||||
@@ -40,9 +45,9 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
|
|||||||
if (saveData.is_mapped()) {
|
if (saveData.is_mapped()) {
|
||||||
saveData.sync(error);
|
saveData.sync(error);
|
||||||
if (error) {
|
if (error) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
|
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
|
||||||
{}, {}, R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
|
R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveData.unmap();
|
saveData.unmap();
|
||||||
@@ -56,16 +61,16 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
|
|||||||
|
|
||||||
if (sramVec.size() != SRAM_SIZE) {
|
if (sramVec.size() != SRAM_SIZE) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw(
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE},
|
{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);
|
"[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, error);
|
||||||
if (error) {
|
if (error) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MMAP_MAKE_SINK_ERROR},
|
{Util::Error::Type::MMAP_MAKE_SINK_ERROR}, {}, {},
|
||||||
{}, {}, R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
|
R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,23 +150,25 @@ void Mem::LoadROM(const bool isArchive, const std::string &filename) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
u8 Mem::Read(const u32 paddr) {
|
u8 Mem::Read(const u32 paddr) {
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u8>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
return mmio.rdp.ReadRDRAM<u8>(paddr);
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return src[BYTE_ADDRESS(paddr & 0xfff)];
|
return src[BYTE_ADDRESS(paddr & 0xfff)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u8, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
if(ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) {
|
return mmio.pi.BusRead<u8, false>(paddr);
|
||||||
|
if (ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) {
|
||||||
const u32 w = mmio.ai.Read(paddr & ~3);
|
const u32 w = mmio.ai.Read(paddr & ~3);
|
||||||
const int offs = 3 - (paddr & 3);
|
const int offs = 3 - (paddr & 3);
|
||||||
return w >> offs * 8 & 0xff;
|
return w >> offs * 8 & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
Util::Error::GetInstance().Throw(
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_INVALID_ACCESS}, regs.pc,
|
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_INVALID_ACCESS}, regs.pc,
|
||||||
@@ -169,126 +176,160 @@ u8 Mem::Read(const u32 paddr) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START];
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return si.pif.ram[paddr - PIF_RAM_REGION_START];
|
return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START];
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return si.pif.ram[paddr - PIF_RAM_REGION_START];
|
||||||
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0},
|
||||||
|
"8-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
u16 Mem::Read(const u32 paddr) {
|
u16 Mem::Read(const u32 paddr) {
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u16>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
return mmio.rdp.ReadRDRAM<u16>(paddr);
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return ircolib::ReadAccess<u16>(src, HALF_ADDRESS(paddr & 0xfff));
|
return ircolib::ReadAccess<u16>(src, HALF_ADDRESS(paddr & 0xfff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u16, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
return mmio.pi.BusRead<u16, false>(paddr);
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START);
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
return mmio.Read(paddr);
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
|
return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START);
|
||||||
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
||||||
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0}, "16-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0},
|
||||||
|
"16-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
u32 Mem::Read(const u32 paddr) {
|
u32 Mem::Read(const u32 paddr) {
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u32>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
return mmio.rdp.ReadRDRAM<u32>(paddr);
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return ircolib::ReadAccess<u32>(src, paddr & 0xfff);
|
return ircolib::ReadAccess<u32>(src, paddr & 0xfff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u32, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
return mmio.pi.BusRead<u32, false>(paddr);
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
return mmio.Read(paddr);
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
||||||
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, "32-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0},
|
||||||
|
"32-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
u64 Mem::Read(const u32 paddr) {
|
u64 Mem::Read(const u32 paddr) {
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u64>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
return mmio.rdp.ReadRDRAM<u64>(paddr);
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return ircolib::ReadAccess<u64>(src, paddr & 0xfff);
|
return ircolib::ReadAccess<u64>(src, paddr & 0xfff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u64, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
return mmio.pi.BusRead<u64, false>(paddr);
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
return mmio.Read(paddr);
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
||||||
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0}, "64-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0},
|
||||||
|
"64-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
|
void Mem::Write<u8>(u32 paddr, u32 val) {
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u8>(paddr, val); return; }
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
jit.InvalidateBlock(paddr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
|
mmio.rdp.WriteRDRAM<u8>(paddr, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
val = val << (8 * (3 - (paddr & 3)));
|
val = val << (8 * (3 - (paddr & 3)));
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
bool is_imem = paddr & 0x1000;
|
||||||
|
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
paddr = (paddr & 0xFFF) & ~3;
|
paddr = (paddr & 0xFFF) & ~3;
|
||||||
ircolib::WriteAccess<u32>(dest, paddr, val);
|
ircolib::WriteAccess<u32>(dest, paddr, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
||||||
trace("BusWrite<u8> @ {:08X} = {:02X}", paddr, val);
|
trace("BusWrite<u8> @ {:08X} = {:02X}", paddr, val);
|
||||||
mmio.pi.BusWrite<u8, false>(paddr, val);
|
mmio.pi.BusWrite<u8, false>(paddr, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) panic("MMIO Write<u8>!");
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
panic("MMIO Write<u8>!");
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
val = val << (8 * (3 - (paddr & 3)));
|
val = val << (8 * (3 - (paddr & 3)));
|
||||||
paddr = (paddr - PIF_RAM_REGION_START) & ~3;
|
paddr = (paddr - PIF_RAM_REGION_START) & ~3;
|
||||||
ircolib::WriteAccess<u32>(si.pif.ram, paddr, std::byteswap(val));
|
ircolib::WriteAccess<u32>(si.pif.ram, paddr, std::byteswap(val));
|
||||||
@@ -296,53 +337,49 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
|
||||||
template <>
|
template <>
|
||||||
void Mem::WriteJIT<u8>(const u32 paddr, const u32 val) {
|
void Mem::Write<u16>(u32 paddr, u32 val) {
|
||||||
WriteInterpreter<u8>(paddr, val);
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
if (jit)
|
|
||||||
jit->InvalidateBlock(paddr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Mem::Write<u8>(const u32 paddr, const u32 val) {
|
|
||||||
WriteInterpreter<u8>(paddr, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
|
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u16>(paddr, val); return; }
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
jit.InvalidateBlock(paddr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
|
mmio.rdp.WriteRDRAM<u16>(paddr, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
val = val << (16 * !(paddr & 2));
|
val = val << (16 * !(paddr & 2));
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
bool is_imem = paddr & 0x1000;
|
||||||
|
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
paddr = (paddr & 0xFFF) & ~3;
|
paddr = (paddr & 0xFFF) & ~3;
|
||||||
ircolib::WriteAccess<u32>(dest, paddr, val);
|
ircolib::WriteAccess<u32>(dest, paddr, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
||||||
trace("BusWrite<u8> @ {:08X} = {:04X}", paddr, val);
|
trace("BusWrite<u8> @ {:08X} = {:04X}", paddr, val);
|
||||||
mmio.pi.BusWrite<u16, false>(paddr, val);
|
mmio.pi.BusWrite<u16, false>(paddr, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) panic("MMIO Write<u16>!");
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
panic("MMIO Write<u16>!");
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
val = val << (16 * !(paddr & 2));
|
val = val << (16 * !(paddr & 2));
|
||||||
paddr &= ~3;
|
paddr &= ~3;
|
||||||
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
||||||
@@ -350,121 +387,106 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
|
||||||
template <>
|
template <>
|
||||||
void Mem::WriteJIT<u16>(const u32 paddr, const u32 val) {
|
void Mem::Write<u32>(const u32 paddr, const u32 val) {
|
||||||
WriteInterpreter<u16>(paddr, val);
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
if (jit)
|
|
||||||
jit->InvalidateBlock(paddr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Mem::Write<u16>(const u32 paddr, const u32 val) {
|
|
||||||
WriteInterpreter<u16>(paddr, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
|
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u32>(paddr, val); return; }
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
jit.InvalidateBlock(paddr);
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
#endif
|
||||||
|
|
||||||
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
|
mmio.rdp.WriteRDRAM<u32>(paddr, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
|
bool is_imem = paddr & 0x1000;
|
||||||
|
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
|
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
||||||
trace("BusWrite<u8> @ {:08X} = {:08X}", paddr, val);
|
trace("BusWrite<u8> @ {:08X} = {:08X}", paddr, val);
|
||||||
mmio.pi.BusWrite<u32, false>(paddr, val);
|
mmio.pi.BusWrite<u32, false>(paddr, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) { mmio.Write(paddr, val); return; }
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
|
||||||
|
mmio.Write(paddr, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
||||||
si.pif.ProcessCommands();
|
si.pif.ProcessCommands();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
void Mem::Write(const u32 paddr, u64 val) {
|
||||||
template <>
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
void Mem::WriteJIT<u32>(const u32 paddr, const u32 val) {
|
|
||||||
WriteInterpreter<u32>(paddr, val);
|
|
||||||
if (jit)
|
|
||||||
jit->InvalidateBlock(paddr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Mem::Write<u32>(const u32 paddr, const u32 val) {
|
|
||||||
WriteInterpreter<u32>(paddr, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __aarch64__
|
|
||||||
void Mem::WriteJIT(const u32 paddr, const u64 val) {
|
|
||||||
WriteInterpreter(paddr, val);
|
|
||||||
if (jit)
|
|
||||||
jit->InvalidateBlock(paddr);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Mem::Write(const u32 paddr, const u64 val) { WriteInterpreter(paddr, val); }
|
|
||||||
|
|
||||||
void Mem::WriteInterpreter(const u32 paddr, u64 val) {
|
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u64>(paddr, val); return; }
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
jit.InvalidateBlock(paddr);
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
#endif
|
||||||
|
|
||||||
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
|
||||||
|
mmio.rdp.WriteRDRAM<u64>(paddr, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
|
bool is_imem = paddr & 0x1000;
|
||||||
|
auto &dest = is_imem ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
val >>= 32;
|
val >>= 32;
|
||||||
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
|
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
|
||||||
trace("BusWrite<u64> @ {:08X} = {:016X}", paddr, val);
|
trace("BusWrite<u64> @ {:08X} = {:016X}", paddr, val);
|
||||||
mmio.pi.BusWrite<false>(paddr, val);
|
mmio.pi.BusWrite<false>(paddr, val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)) panic("MMIO Write<u64>!");
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
panic("MMIO Write<u64>!");
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
||||||
si.pif.ProcessCommands();
|
si.pif.ProcessCommands();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-12
@@ -74,15 +74,19 @@ struct Flash {
|
|||||||
T Read(u32 index) const;
|
T Read(u32 index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
struct JIT;
|
struct JIT;
|
||||||
|
|
||||||
struct Mem {
|
struct Mem {
|
||||||
~Mem() = default;
|
Mem(JIT &jit);
|
||||||
|
JIT &jit;
|
||||||
|
#else
|
||||||
|
struct Mem {
|
||||||
Mem();
|
Mem();
|
||||||
|
~Mem() = default;
|
||||||
void Reset();
|
void Reset();
|
||||||
void LoadSRAM(SaveType, fs::path);
|
void LoadSRAM(SaveType, fs::path);
|
||||||
void LoadROM(bool, const std::string &);
|
void LoadROM(bool, const std::string &);
|
||||||
void SetJIT(JIT* jit) { this->jit = jit; }
|
|
||||||
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
|
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
|
||||||
|
|
||||||
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
|
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
|
||||||
@@ -124,7 +128,8 @@ struct Mem {
|
|||||||
ROM rom;
|
ROM rom;
|
||||||
SaveType saveType = SAVE_NONE;
|
SaveType saveType = SAVE_NONE;
|
||||||
Flash flash;
|
Flash flash;
|
||||||
private:
|
|
||||||
|
private:
|
||||||
friend struct SI;
|
friend struct SI;
|
||||||
friend struct PI;
|
friend struct PI;
|
||||||
friend struct AI;
|
friend struct AI;
|
||||||
@@ -132,17 +137,9 @@ private:
|
|||||||
friend struct JIT;
|
friend struct JIT;
|
||||||
friend struct Core;
|
friend struct Core;
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void WriteInterpreter(u32, u32);
|
|
||||||
void WriteInterpreter(u32, u64);
|
|
||||||
template <typename T>
|
|
||||||
void WriteJIT(u32, u32);
|
|
||||||
void WriteJIT(u32, u64);
|
|
||||||
|
|
||||||
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
||||||
std::ofstream isviewer_sink{};
|
std::ofstream isviewer_sink{};
|
||||||
int mmioSize{}, flashSize{};
|
int mmioSize{}, flashSize{};
|
||||||
JIT *jit = nullptr;
|
|
||||||
std::string sramPath{};
|
std::string sramPath{};
|
||||||
mio::mmap_sink saveData{};
|
mio::mmap_sink saveData{};
|
||||||
|
|
||||||
@@ -151,4 +148,5 @@ private:
|
|||||||
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
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ void RDP::Write(const u32 addr, const u32 val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void RDP::WriteStatus(const u32 val) {
|
void RDP::WriteStatus(const u32 val) {
|
||||||
DPCStatusWrite temp{};
|
DPCStatusWrite temp{};
|
||||||
temp.raw = val;
|
temp.raw = val;
|
||||||
@@ -197,8 +196,8 @@ FORCE_INLINE void logCommand(u8 cmd) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void RDP::RunCommand() {
|
void RDP::RunCommand() {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
ParallelRDP& parallel = n64::Core::GetInstance().parallel;
|
ParallelRDP ¶llel = n64::Core::GetInstance().parallel;
|
||||||
if (dpc.status.freeze) {
|
if (dpc.status.freeze) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -285,8 +284,8 @@ void RDP::RunCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RDP::OnFullSync() {
|
void RDP::OnFullSync() {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
ParallelRDP& parallel = n64::Core::GetInstance().parallel;
|
ParallelRDP ¶llel = n64::Core::GetInstance().parallel;
|
||||||
|
|
||||||
parallel.OnFullSync();
|
parallel.OnFullSync();
|
||||||
|
|
||||||
|
|||||||
+12
-38
@@ -1,5 +1,6 @@
|
|||||||
#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(); }
|
||||||
@@ -30,39 +31,6 @@ void RSP::Reset() {
|
|||||||
steps = 0;
|
steps = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) {
|
|
||||||
debug("{:04X} {:08X} ", rsp.oldPC, instr);
|
|
||||||
for (auto gpr : rsp.gpr) {
|
|
||||||
debug("{:08X} ", gpr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto vpr : rsp.vpr) {
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
debug("{:04X}", vpr.element[i]);
|
|
||||||
}
|
|
||||||
debug(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
debug("{:04X}", rsp.acc.h.element[i]);
|
|
||||||
}
|
|
||||||
debug(" ");
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
debug("{:04X}", rsp.acc.m.element[i]);
|
|
||||||
}
|
|
||||||
debug(" ");
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
debug("{:04X}", rsp.acc.l.element[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(" {:04X} {:04X} {:02X}", rsp.GetVCC(), rsp.GetVCO(), rsp.GetVCE());
|
|
||||||
debug("DMEM: {:02X}{:02X}", rsp.dmem[0x3c4], rsp.dmem[0x3c5]);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
auto RSP::Read(const u32 addr) -> u32 {
|
auto RSP::Read(const u32 addr) -> u32 {
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x04040000:
|
case 0x04040000:
|
||||||
@@ -83,13 +51,18 @@ auto RSP::Read(const u32 addr) -> u32 {
|
|||||||
case 0x04080000:
|
case 0x04080000:
|
||||||
return pc & 0xFFC;
|
return pc & 0xFFC;
|
||||||
default:
|
default:
|
||||||
panic("Unimplemented SP register read {:08X}", addr);
|
{
|
||||||
|
auto ®s = Core::GetRegs();
|
||||||
|
|
||||||
|
panic("Unimplemented SP register read {:08X} (cpu pc: 0x{:016X}, rsp pc: 0x{:04X}, ra: 0x{:016X})", addr,
|
||||||
|
(u64)regs.oldPC, pc & 0xffc, (u64)regs.gpr[31]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RSP::WriteStatus(const u32 value) {
|
void RSP::WriteStatus(const u32 value) {
|
||||||
Mem& mem = Core::GetMem();
|
Mem &mem = Core::GetMem();
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
MI &mi = mem.mmio.mi;
|
MI &mi = mem.mmio.mi;
|
||||||
const auto write = SPStatusWrite{.raw = value};
|
const auto write = SPStatusWrite{.raw = value};
|
||||||
if (write.clearHalt && !write.setHalt) {
|
if (write.clearHalt && !write.setHalt) {
|
||||||
@@ -130,7 +103,7 @@ void RSP::WriteStatus(const u32 value) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
void RSP::DMA<true>() {
|
void RSP::DMA<true>() {
|
||||||
Mem& mem = Core::GetMem();
|
Mem &mem = Core::GetMem();
|
||||||
u32 length = spDMALen.len + 1;
|
u32 length = spDMALen.len + 1;
|
||||||
|
|
||||||
length = (length + 0x7) & ~0x7;
|
length = (length + 0x7) & ~0x7;
|
||||||
@@ -163,7 +136,7 @@ void RSP::DMA<true>() {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
void RSP::DMA<false>() {
|
void RSP::DMA<false>() {
|
||||||
Mem& mem = Core::GetMem();
|
Mem &mem = Core::GetMem();
|
||||||
u32 length = spDMALen.len + 1;
|
u32 length = spDMALen.len + 1;
|
||||||
|
|
||||||
length = (length + 0x7) & ~0x7;
|
length = (length + 0x7) & ~0x7;
|
||||||
@@ -171,6 +144,7 @@ void RSP::DMA<false>() {
|
|||||||
auto &dst = spDMASPAddr.bank ? imem : dmem;
|
auto &dst = spDMASPAddr.bank ? imem : dmem;
|
||||||
|
|
||||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||||
|
|
||||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||||
trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address);
|
trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#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))
|
||||||
@@ -376,20 +377,11 @@ struct RSP {
|
|||||||
void DMA();
|
void DMA();
|
||||||
void WriteStatus(u32 value);
|
void WriteStatus(u32 value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FORCE_INLINE void branch(const u16 address, const bool cond) {
|
FORCE_INLINE void branch(const u16 address, const bool cond) {
|
||||||
if (cond) {
|
if (cond) {
|
||||||
nextPC = address & 0xFFC;
|
nextPC = address & 0xFFC;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE void branch_likely(const u16 address, const bool cond) {
|
|
||||||
if (cond) {
|
|
||||||
nextPC = address & 0xFFC;
|
|
||||||
} else {
|
|
||||||
pc = nextPC & 0xFFC;
|
|
||||||
nextPC = pc + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -4,29 +4,29 @@
|
|||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
void Cop0::mtc0(const Instruction instr) {
|
void Cop0::mtc0(const Instruction instr) {
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
SetReg32(instr.rd(), regs.Read<u32>(instr.rt()));
|
SetReg32(instr.rd(), regs.Read<u32>(instr.rt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cop0::dmtc0(const Instruction instr) {
|
void Cop0::dmtc0(const Instruction instr) {
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
SetReg64(instr.rd(), regs.Read<u64>(instr.rt()));
|
SetReg64(instr.rd(), regs.Read<u64>(instr.rt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cop0::mfc0(const Instruction instr) {
|
void Cop0::mfc0(const Instruction instr) {
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
regs.Write(instr.rt(), s32(GetReg32(instr.rd())));
|
regs.Write(instr.rt(), s32(GetReg32(instr.rd())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cop0::dmfc0(const Instruction instr) const {
|
void Cop0::dmfc0(const Instruction instr) const {
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
regs.Write(instr.rt(), s64(GetReg64(instr.rd())));
|
regs.Write(instr.rt(), s64(GetReg64(instr.rd())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cop0::eret() {
|
void Cop0::eret() {
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
if (!regs.cop0.kernelMode) {
|
if (!regs.cop0.kernelMode) {
|
||||||
FireException(ExceptionCode::CoprocessorUnusable, 0, regs.oldPC);
|
FireException(Cop0::ExceptionCode::CoprocessorUnusable, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (status.erl) {
|
if (status.erl) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,10 +33,10 @@ void Interpreter::special(const Instruction instr) {
|
|||||||
jalr(instr);
|
jalr(instr);
|
||||||
break;
|
break;
|
||||||
case Instruction::SYSCALL:
|
case Instruction::SYSCALL:
|
||||||
regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC);
|
||||||
break;
|
break;
|
||||||
case Instruction::BREAK:
|
case Instruction::BREAK:
|
||||||
regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC);
|
||||||
break;
|
break;
|
||||||
case Instruction::SYNC:
|
case Instruction::SYNC:
|
||||||
break; // SYNC
|
break; // SYNC
|
||||||
@@ -164,8 +164,8 @@ void Interpreter::special(const Instruction instr) {
|
|||||||
dsra32(instr);
|
dsra32(instr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi, instr.instr.opcode.special_lo, instr.instr.raw,
|
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi,
|
||||||
static_cast<u64>(regs.oldPC));
|
instr.instr.opcode.special_lo, instr.instr.raw, static_cast<u64>(regs.oldPC));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ void Interpreter::regimm(const Instruction instr) {
|
|||||||
|
|
||||||
void Interpreter::cop2Decode(const Instruction instr) {
|
void Interpreter::cop2Decode(const Instruction instr) {
|
||||||
if (!regs.cop0.status.cu2) {
|
if (!regs.cop0.status.cu2) {
|
||||||
regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (instr.rs()) {
|
switch (instr.rs()) {
|
||||||
@@ -245,11 +245,11 @@ void Interpreter::cop2Decode(const Instruction instr) {
|
|||||||
ctc2(instr);
|
ctc2(instr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 2, regs.oldPC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::Exec(const Instruction instr) {
|
void Interpreter::DecodeExecute(const Instruction instr) {
|
||||||
// 00rr_rccc
|
// 00rr_rccc
|
||||||
switch (instr.opcode()) {
|
switch (instr.opcode()) {
|
||||||
case Instruction::SPECIAL:
|
case Instruction::SPECIAL:
|
||||||
@@ -304,7 +304,7 @@ void Interpreter::Exec(const Instruction instr) {
|
|||||||
regs.cop0.decode(instr);
|
regs.cop0.decode(instr);
|
||||||
break;
|
break;
|
||||||
case Instruction::COP1:
|
case Instruction::COP1:
|
||||||
if(instr.cop_rs() == 0x08) {
|
if (instr.cop_rs() == 0x08) {
|
||||||
switch (instr.cop_rt()) {
|
switch (instr.cop_rt()) {
|
||||||
case 0:
|
case 0:
|
||||||
if (!regs.cop1.CheckFPUUsable())
|
if (!regs.cop1.CheckFPUUsable())
|
||||||
@@ -361,7 +361,7 @@ void Interpreter::Exec(const Instruction instr) {
|
|||||||
ldr(instr);
|
ldr(instr);
|
||||||
break;
|
break;
|
||||||
case 0x1F:
|
case 0x1F:
|
||||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||||
break;
|
break;
|
||||||
case Instruction::LB:
|
case Instruction::LB:
|
||||||
lb(instr);
|
lb(instr);
|
||||||
@@ -409,7 +409,8 @@ void Interpreter::Exec(const Instruction instr) {
|
|||||||
swr(instr);
|
swr(instr);
|
||||||
break;
|
break;
|
||||||
case Instruction::CACHE:
|
case Instruction::CACHE:
|
||||||
break; // CACHE
|
// cache(instr);
|
||||||
|
break;
|
||||||
case Instruction::LL:
|
case Instruction::LL:
|
||||||
ll(instr);
|
ll(instr);
|
||||||
break;
|
break;
|
||||||
@@ -449,7 +450,8 @@ void Interpreter::Exec(const Instruction instr) {
|
|||||||
sd(instr);
|
sd(instr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr), static_cast<u64>(regs.oldPC));
|
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr),
|
||||||
|
static_cast<u64>(regs.oldPC));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
|
#include <Scheduler.hpp>
|
||||||
|
|
||||||
#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
||||||
#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
||||||
@@ -8,7 +9,7 @@ void Interpreter::add(const Instruction instr) {
|
|||||||
const u32 rs = regs.Read<s32>(instr.rs());
|
const u32 rs = regs.Read<s32>(instr.rs());
|
||||||
const u32 rt = regs.Read<s32>(instr.rt());
|
const u32 rt = regs.Read<s32>(instr.rt());
|
||||||
if (const u32 result = rs + rt; check_signed_overflow(rs, rt, result)) {
|
if (const u32 result = rs + rt; check_signed_overflow(rs, rt, result)) {
|
||||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rd(), static_cast<s32>(result));
|
regs.Write(instr.rd(), static_cast<s32>(result));
|
||||||
}
|
}
|
||||||
@@ -25,7 +26,7 @@ void Interpreter::addi(const Instruction instr) {
|
|||||||
const u32 rs = regs.Read<s64>(instr.rs());
|
const u32 rs = regs.Read<s64>(instr.rs());
|
||||||
const u32 imm = static_cast<s32>(static_cast<s16>(instr));
|
const u32 imm = static_cast<s32>(static_cast<s16>(instr));
|
||||||
if (const u32 result = rs + imm; check_signed_overflow(rs, imm, result)) {
|
if (const u32 result = rs + imm; check_signed_overflow(rs, imm, result)) {
|
||||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rt(), static_cast<s32>(result));
|
regs.Write(instr.rt(), static_cast<s32>(result));
|
||||||
}
|
}
|
||||||
@@ -42,7 +43,7 @@ void Interpreter::dadd(const Instruction instr) {
|
|||||||
const u64 rs = regs.Read<s64>(instr.rs());
|
const u64 rs = regs.Read<s64>(instr.rs());
|
||||||
const u64 rt = regs.Read<s64>(instr.rt());
|
const u64 rt = regs.Read<s64>(instr.rt());
|
||||||
if (const u64 result = rt + rs; check_signed_overflow(rs, rt, result)) {
|
if (const u64 result = rt + rs; check_signed_overflow(rs, rt, result)) {
|
||||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rd(), result);
|
regs.Write(instr.rd(), result);
|
||||||
}
|
}
|
||||||
@@ -58,7 +59,7 @@ void Interpreter::daddi(const Instruction instr) {
|
|||||||
const u64 imm = s64(s16(instr));
|
const u64 imm = s64(s16(instr));
|
||||||
const u64 rs = regs.Read<s64>(instr.rs());
|
const u64 rs = regs.Read<s64>(instr.rs());
|
||||||
if (const u64 result = imm + rs; check_signed_overflow(rs, imm, result)) {
|
if (const u64 result = imm + rs; check_signed_overflow(rs, imm, result)) {
|
||||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rt(), result);
|
regs.Write(instr.rt(), result);
|
||||||
}
|
}
|
||||||
@@ -144,12 +145,13 @@ void Interpreter::branch(const bool cond, const s64 address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::branch_likely(const bool cond, const s64 address) {
|
void Interpreter::branch_likely(const bool cond, const s64 address) {
|
||||||
if (cond) {
|
if (!cond) {
|
||||||
|
regs.SetPC64(regs.nextPC);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
regs.delaySlot = true;
|
regs.delaySlot = true;
|
||||||
regs.nextPC = address;
|
regs.nextPC = address;
|
||||||
} else {
|
|
||||||
regs.SetPC64(regs.nextPC);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::b(const Instruction instr, const bool cond) {
|
void Interpreter::b(const Instruction instr, const bool cond) {
|
||||||
@@ -200,9 +202,9 @@ void Interpreter::lb(const Instruction instr) {
|
|||||||
|
|
||||||
void Interpreter::lh(const Instruction instr) {
|
void Interpreter::lh(const Instruction instr) {
|
||||||
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b1, address)) {
|
if (Core::IsAddressError(0b1, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,9 +220,9 @@ void Interpreter::lh(const Instruction instr) {
|
|||||||
void Interpreter::lw(const Instruction instr) {
|
void Interpreter::lw(const Instruction instr) {
|
||||||
const s16 offset = instr;
|
const s16 offset = instr;
|
||||||
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,8 +243,8 @@ void Interpreter::ll(const Instruction instr) {
|
|||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
const s32 result = mem.Read<u32>(physical);
|
const s32 result = mem.Read<u32>(physical);
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,9 +287,9 @@ void Interpreter::lwr(const Instruction instr) {
|
|||||||
|
|
||||||
void Interpreter::ld(const Instruction instr) {
|
void Interpreter::ld(const Instruction instr) {
|
||||||
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b111, address)) {
|
if (Core::IsAddressError(0b111, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +305,7 @@ void Interpreter::ld(const Instruction instr) {
|
|||||||
|
|
||||||
void Interpreter::lld(const Instruction instr) {
|
void Interpreter::lld(const Instruction instr) {
|
||||||
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
|
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
|
||||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,8 +315,8 @@ void Interpreter::lld(const Instruction instr) {
|
|||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
if (check_address_error(0b111, address)) {
|
if (Core::IsAddressError(0b111, address)) {
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rt(), mem.Read<u64>(paddr));
|
regs.Write(instr.rt(), mem.Read<u64>(paddr));
|
||||||
regs.cop0.llbit = true;
|
regs.cop0.llbit = true;
|
||||||
@@ -367,9 +369,9 @@ void Interpreter::lbu(const Instruction instr) {
|
|||||||
|
|
||||||
void Interpreter::lhu(const Instruction instr) {
|
void Interpreter::lhu(const Instruction instr) {
|
||||||
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b1, address)) {
|
if (Core::IsAddressError(0b1, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
u32 paddr;
|
u32 paddr;
|
||||||
@@ -384,9 +386,9 @@ void Interpreter::lhu(const Instruction instr) {
|
|||||||
|
|
||||||
void Interpreter::lwu(const Instruction instr) {
|
void Interpreter::lwu(const Instruction instr) {
|
||||||
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,6 +409,7 @@ 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,10 +420,10 @@ void Interpreter::sc(const Instruction instr) {
|
|||||||
if (regs.cop0.llbit) {
|
if (regs.cop0.llbit) {
|
||||||
regs.cop0.llbit = false;
|
regs.cop0.llbit = false;
|
||||||
|
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
regs.Write(instr.rt(), 0);
|
regs.Write(instr.rt(), 0);
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,6 +433,7 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -440,7 +444,7 @@ void Interpreter::sc(const Instruction instr) {
|
|||||||
|
|
||||||
void Interpreter::scd(const Instruction instr) {
|
void Interpreter::scd(const Instruction instr) {
|
||||||
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
|
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
|
||||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,10 +453,10 @@ void Interpreter::scd(const Instruction instr) {
|
|||||||
if (regs.cop0.llbit) {
|
if (regs.cop0.llbit) {
|
||||||
regs.cop0.llbit = false;
|
regs.cop0.llbit = false;
|
||||||
|
|
||||||
if (check_address_error(0b111, address)) {
|
if (Core::IsAddressError(0b111, address)) {
|
||||||
regs.Write(instr.rt(), 0);
|
regs.Write(instr.rt(), 0);
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +466,7 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -478,6 +483,7 @@ 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,9 +491,9 @@ void Interpreter::sh(const Instruction instr) {
|
|||||||
void Interpreter::sw(const Instruction instr) {
|
void Interpreter::sw(const Instruction instr) {
|
||||||
const s16 offset = instr;
|
const s16 offset = instr;
|
||||||
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,15 +502,16 @@ 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::sd(const Instruction instr) {
|
void Interpreter::sd(const Instruction instr) {
|
||||||
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b111, address)) {
|
if (Core::IsAddressError(0b111, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,6 +520,7 @@ 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -528,6 +536,7 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,6 +552,7 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -558,6 +568,7 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -573,6 +584,7 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -583,7 +595,9 @@ void Interpreter::ori(const Instruction instr) {
|
|||||||
regs.Write(instr.rt(), result);
|
regs.Write(instr.rt(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::or_(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())); }
|
void Interpreter::or_(const Instruction instr) {
|
||||||
|
regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt()));
|
||||||
|
}
|
||||||
|
|
||||||
void Interpreter::nor(const Instruction instr) {
|
void Interpreter::nor(const Instruction instr) {
|
||||||
regs.Write(instr.rd(), ~(regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())));
|
regs.Write(instr.rd(), ~(regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())));
|
||||||
@@ -622,7 +636,9 @@ void Interpreter::sltiu(const Instruction instr) {
|
|||||||
regs.Write(instr.rt(), regs.Read<u64>(instr.rs()) < imm);
|
regs.Write(instr.rt(), regs.Read<u64>(instr.rs()) < imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::slt(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt())); }
|
void Interpreter::slt(const Instruction instr) {
|
||||||
|
regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
|
||||||
|
}
|
||||||
|
|
||||||
void Interpreter::sltu(const Instruction instr) {
|
void Interpreter::sltu(const Instruction instr) {
|
||||||
regs.Write(instr.rd(), regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
regs.Write(instr.rd(), regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
||||||
@@ -753,7 +769,7 @@ void Interpreter::dsub(const Instruction instr) {
|
|||||||
const s64 rt = regs.Read<s64>(instr.rt());
|
const s64 rt = regs.Read<s64>(instr.rt());
|
||||||
const s64 rs = regs.Read<s64>(instr.rs());
|
const s64 rs = regs.Read<s64>(instr.rs());
|
||||||
if (const s64 result = rs - rt; check_signed_underflow(rs, rt, result)) {
|
if (const s64 result = rs - rt; check_signed_underflow(rs, rt, result)) {
|
||||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rd(), result);
|
regs.Write(instr.rd(), result);
|
||||||
}
|
}
|
||||||
@@ -771,7 +787,7 @@ void Interpreter::sub(const Instruction instr) {
|
|||||||
const s32 rs = regs.Read<s64>(instr.rs());
|
const s32 rs = regs.Read<s64>(instr.rs());
|
||||||
const s32 result = rs - rt;
|
const s32 result = rs - rt;
|
||||||
if (check_signed_underflow(rs, rt, result)) {
|
if (check_signed_underflow(rs, rt, result)) {
|
||||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rd(), result);
|
regs.Write(instr.rd(), result);
|
||||||
}
|
}
|
||||||
@@ -825,9 +841,9 @@ void Interpreter::mtlo(const Instruction instr) { regs.lo = regs.Read<s64>(instr
|
|||||||
void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read<s64>(instr.rs()); }
|
void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read<s64>(instr.rs()); }
|
||||||
|
|
||||||
void Interpreter::trap(const bool cond) const {
|
void Interpreter::trap(const bool cond) const {
|
||||||
Cop0& cop0 = Core::GetRegs().cop0;
|
Cop0 &cop0 = Core::GetRegs().cop0;
|
||||||
if (cond) {
|
if (cond) {
|
||||||
cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC);
|
cop0.FireException(Cop0::ExceptionCode::Trap, 0, regs.oldPC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -845,4 +861,87 @@ void Interpreter::dmfc2(const Instruction instr) { regs.Write(instr.rt(), cop2La
|
|||||||
void Interpreter::ctc2(const Instruction) {}
|
void Interpreter::ctc2(const Instruction) {}
|
||||||
|
|
||||||
void Interpreter::cfc2(const Instruction) {}
|
void Interpreter::cfc2(const Instruction) {}
|
||||||
|
|
||||||
|
void Interpreter::cache(const Instruction instr) {
|
||||||
|
u8 type = instr.op() & 3;
|
||||||
|
const u8 op = (instr.op() >> 2) & 7;
|
||||||
|
u64 vaddr = regs.Read<u64>(instr.rs()) + (s16)instr.offset();
|
||||||
|
u32 paddr;
|
||||||
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
||||||
|
regs.cop0.HandleTLBException(vaddr);
|
||||||
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ptag = GetPhysicalAddressPTag(paddr);
|
||||||
|
|
||||||
|
if (type > 1)
|
||||||
|
panic("Unknown cache type {}", type);
|
||||||
|
|
||||||
|
if (type == 0)
|
||||||
|
return CacheTypeInstruction(op, vaddr, paddr, ptag);
|
||||||
|
|
||||||
|
return CacheTypeData(op, vaddr, paddr, ptag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interpreter::CacheTypeInstruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) {
|
||||||
|
switch (op) {
|
||||||
|
case 0:
|
||||||
|
icache.InvalidateIndex(vaddr);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
icache.LoadTag(vaddr);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
icache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
icache.InvalidateIndex(vaddr, ptag);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
icache.FillLine(vaddr, paddr);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
icache.WriteBack(vaddr, paddr, ptag);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("Unimplemented icache op 0b{:03b}", op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interpreter::CacheTypeData(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) {
|
||||||
|
switch (op) {
|
||||||
|
case 0:
|
||||||
|
dcache.WriteBack<true>(vaddr, paddr);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
dcache.LoadTag(vaddr);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
dcache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
auto &line = dcache.lines[GetDCacheLineIndex(vaddr)];
|
||||||
|
if ((line.ptag != ptag || !line.valid) && line.dirty)
|
||||||
|
dcache.WriteBack(vaddr, paddr);
|
||||||
|
|
||||||
|
line.ptag = ptag;
|
||||||
|
line.valid = true;
|
||||||
|
line.dirty = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
dcache.InvalidateIndex(vaddr, ptag);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
dcache.WriteBack<true>(vaddr, paddr, ptag);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
dcache.WriteBack(vaddr, paddr, ptag);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("Unimplemented dcache op 0b{:03b}", op);
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ void JIT::special(const Instruction instr) {
|
|||||||
jalr(instr);
|
jalr(instr);
|
||||||
break;
|
break;
|
||||||
case Instruction::SYSCALL:
|
case Instruction::SYSCALL:
|
||||||
regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC);
|
||||||
break;
|
break;
|
||||||
case Instruction::BREAK:
|
case Instruction::BREAK:
|
||||||
regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC);
|
||||||
break;
|
break;
|
||||||
case Instruction::SYNC:
|
case Instruction::SYNC:
|
||||||
break; // SYNC
|
break; // SYNC
|
||||||
@@ -311,8 +311,7 @@ void JIT::Emit(const Instruction instr) {
|
|||||||
emitMemberFunctionCall(&Cop0::eret, ®s.cop0);
|
emitMemberFunctionCall(&Cop0::eret, ®s.cop0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr),
|
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC);
|
||||||
regs.oldPC);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -378,7 +377,7 @@ void JIT::Emit(const Instruction instr) {
|
|||||||
ldr(instr);
|
ldr(instr);
|
||||||
break;
|
break;
|
||||||
case 0x1F:
|
case 0x1F:
|
||||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||||
break;
|
break;
|
||||||
case Instruction::LB:
|
case Instruction::LB:
|
||||||
lb(instr);
|
lb(instr);
|
||||||
@@ -459,7 +458,8 @@ void JIT::Emit(const Instruction instr) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DumpBlockCacheToDisk();
|
DumpBlockCacheToDisk();
|
||||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), static_cast<u64>(regs.oldPC));
|
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr),
|
||||||
|
static_cast<u64>(regs.oldPC));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -20,6 +20,34 @@ static bool SpecialEndsBlock(const Instruction instr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool InstrHasDelaySlot(const Instruction instr) {
|
||||||
|
switch (instr.opcode()) {
|
||||||
|
case Instruction::SPECIAL:
|
||||||
|
if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
case Instruction::REGIMM:
|
||||||
|
case Instruction::J:
|
||||||
|
case Instruction::JAL:
|
||||||
|
case Instruction::BEQ:
|
||||||
|
case Instruction::BNE:
|
||||||
|
case Instruction::BLEZ:
|
||||||
|
case Instruction::BGTZ:
|
||||||
|
case Instruction::BEQL:
|
||||||
|
case Instruction::BNEL:
|
||||||
|
case Instruction::BLEZL:
|
||||||
|
case Instruction::BGTZL:
|
||||||
|
return true;
|
||||||
|
case Instruction::COP1:
|
||||||
|
if (instr.cop_rs() == 8)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool InstrEndsBlock(const Instruction instr) {
|
static bool InstrEndsBlock(const Instruction instr) {
|
||||||
switch (instr.opcode()) {
|
switch (instr.opcode()) {
|
||||||
case Instruction::SPECIAL:
|
case Instruction::SPECIAL:
|
||||||
@@ -31,7 +59,28 @@ static bool InstrEndsBlock(const Instruction instr) {
|
|||||||
case Instruction::BNE:
|
case Instruction::BNE:
|
||||||
case Instruction::BLEZ:
|
case Instruction::BLEZ:
|
||||||
case Instruction::BGTZ:
|
case Instruction::BGTZ:
|
||||||
|
case Instruction::BEQL:
|
||||||
|
case Instruction::BNEL:
|
||||||
|
case Instruction::BLEZL:
|
||||||
|
case Instruction::BGTZL:
|
||||||
return true;
|
return true;
|
||||||
|
case Instruction::COP1:
|
||||||
|
if (instr.cop_rs() == 8)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case Instruction::COP0:
|
||||||
|
switch (instr.cop_rs()) {
|
||||||
|
case 0x10 ... 0x1F:
|
||||||
|
switch (instr.cop_funct()) {
|
||||||
|
case 0x18: // eret
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -55,16 +104,37 @@ static bool IsBranchLikely(const Instruction instr) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case Instruction::COP1:
|
case Instruction::COP1:
|
||||||
{
|
if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
|
||||||
if (instr.cop_rs() == 0x08) {
|
|
||||||
if (instr.cop_rt() == 2 || instr.cop_rt() == 3)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
default:
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsBranch(const Instruction instr) {
|
||||||
|
switch (instr.opcode()) {
|
||||||
|
case Instruction::BEQ:
|
||||||
|
case Instruction::BNE:
|
||||||
|
case Instruction::BLEZ:
|
||||||
|
case Instruction::BGTZ:
|
||||||
|
return true;
|
||||||
|
case Instruction::REGIMM:
|
||||||
|
switch (instr.regimm()) {
|
||||||
|
case Instruction::BLTZ:
|
||||||
|
case Instruction::BGEZ:
|
||||||
|
case Instruction::BLTZAL:
|
||||||
|
case Instruction::BGEZAL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case Instruction::COP1:
|
||||||
|
if (instr.cop_rs() == 8 && (instr.cop_rt() == 0 || instr.cop_rt() == 1))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ void JIT::add(const Instruction instr) {
|
|||||||
const u32 rt = regs.Read<s32>(instr.rt());
|
const u32 rt = regs.Read<s32>(instr.rt());
|
||||||
const u32 result = rs + rt;
|
const u32 result = rs + rt;
|
||||||
if (check_signed_overflow(rs, rt, result)) {
|
if (check_signed_overflow(rs, rt, result)) {
|
||||||
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
panic("[JIT]: Unhandled Overflow exception in ADD!");
|
panic("[JIT]: Unhandled Overflow exception in ADD!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,16 +205,12 @@ void JIT::BranchTaken(const Xbyak::Reg64 &offs) {
|
|||||||
SetPC64(code.SCR2);
|
SetPC64(code.SCR2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::BranchAbsTaken(const s64 addr) {
|
void JIT::BranchAbsTaken(const s64 addr) { SetPC64(addr); }
|
||||||
SetPC64(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) {
|
void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { SetPC64(addr); }
|
||||||
SetPC64(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JIT::branch_constant(const bool cond, const s64 offset) {
|
void JIT::branch_constant(const bool cond, const s64 offset) {
|
||||||
if(cond) {
|
if (cond) {
|
||||||
regs.delaySlot = true;
|
regs.delaySlot = true;
|
||||||
BranchTaken(offset);
|
BranchTaken(offset);
|
||||||
branch_taken = true;
|
branch_taken = true;
|
||||||
@@ -222,7 +218,7 @@ void JIT::branch_constant(const bool cond, const s64 offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JIT::branch_abs_constant(const bool cond, const s64 address) {
|
void JIT::branch_abs_constant(const bool cond, const s64 address) {
|
||||||
if(cond) {
|
if (cond) {
|
||||||
regs.delaySlot = true;
|
regs.delaySlot = true;
|
||||||
BranchAbsTaken(address);
|
BranchAbsTaken(address);
|
||||||
branch_taken = true;
|
branch_taken = true;
|
||||||
@@ -230,7 +226,7 @@ void JIT::branch_abs_constant(const bool cond, const s64 address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JIT::branch_likely_constant(const bool cond, const s64 offset) {
|
void JIT::branch_likely_constant(const bool cond, const s64 offset) {
|
||||||
if(cond) {
|
if (cond) {
|
||||||
regs.delaySlot = true;
|
regs.delaySlot = true;
|
||||||
BranchTaken(offset);
|
BranchTaken(offset);
|
||||||
branch_taken = true;
|
branch_taken = true;
|
||||||
@@ -239,29 +235,34 @@ void JIT::branch_likely_constant(const bool cond, const s64 offset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define branch(offs, cond) do { \
|
#define branch(offs, cond) \
|
||||||
|
do { \
|
||||||
Xbyak::Label taken, not_taken; \
|
Xbyak::Label taken, not_taken; \
|
||||||
code.j## cond(taken); \
|
code.j##cond(taken); \
|
||||||
code.jmp(not_taken); \
|
code.jmp(not_taken); \
|
||||||
code.L(taken); \
|
code.L(taken); \
|
||||||
BranchTaken(offs); \
|
BranchTaken(offs); \
|
||||||
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \
|
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \
|
||||||
code.L(not_taken); \
|
code.L(not_taken); \
|
||||||
} while(0)
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
#define branch_abs(addr, cond) do { \
|
#define branch_abs(addr, cond) \
|
||||||
|
do { \
|
||||||
Xbyak::Label taken, not_taken; \
|
Xbyak::Label taken, not_taken; \
|
||||||
code.j## cond(taken); \
|
code.j##cond(taken); \
|
||||||
code.jmp(not_taken); \
|
code.jmp(not_taken); \
|
||||||
code.L(taken); \
|
code.L(taken); \
|
||||||
BranchAbsTaken(addr); \
|
BranchAbsTaken(addr); \
|
||||||
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \
|
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \
|
||||||
code.L(not_taken); \
|
code.L(not_taken); \
|
||||||
} while(0)
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
#define branch_likely(offs, cond) do { \
|
#define branch_likely(offs, cond) \
|
||||||
|
do { \
|
||||||
Xbyak::Label taken, not_taken, end; \
|
Xbyak::Label taken, not_taken, end; \
|
||||||
code.j## cond(taken); \
|
code.j##cond(taken); \
|
||||||
code.jmp(not_taken); \
|
code.jmp(not_taken); \
|
||||||
code.L(taken); \
|
code.L(taken); \
|
||||||
BranchTaken(offs); \
|
BranchTaken(offs); \
|
||||||
@@ -270,7 +271,8 @@ void JIT::branch_likely_constant(const bool cond, const s64 offset) {
|
|||||||
code.L(not_taken); \
|
code.L(not_taken); \
|
||||||
SetPC64(blockNextPC); \
|
SetPC64(blockNextPC); \
|
||||||
code.L(end); \
|
code.L(end); \
|
||||||
} while(0)
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
void JIT::bltz(const Instruction instr) {
|
void JIT::bltz(const Instruction instr) {
|
||||||
const s16 imm = instr;
|
const s16 imm = instr;
|
||||||
@@ -558,7 +560,7 @@ void JIT::dadd(const Instruction instr) {
|
|||||||
auto rt = regs.Read<u64>(instr.rt());
|
auto rt = regs.Read<u64>(instr.rt());
|
||||||
u64 result = rt + rs;
|
u64 result = rt + rs;
|
||||||
if (check_signed_overflow(rs, rt, result)) {
|
if (check_signed_overflow(rs, rt, result)) {
|
||||||
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
panic("[JIT]: Unhandled Overflow exception in DADD!");
|
panic("[JIT]: Unhandled Overflow exception in DADD!");
|
||||||
}
|
}
|
||||||
regs.Write(instr.rd(), result);
|
regs.Write(instr.rd(), result);
|
||||||
@@ -598,7 +600,7 @@ void JIT::daddi(const Instruction instr) {
|
|||||||
auto rs = regs.Read<u64>(instr.rs());
|
auto rs = regs.Read<u64>(instr.rs());
|
||||||
u64 result = imm + rs;
|
u64 result = imm + rs;
|
||||||
if (check_signed_overflow(rs, imm, result)) {
|
if (check_signed_overflow(rs, imm, result)) {
|
||||||
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
panic("[JIT]: Unhandled Overflow exception in DADDI!");
|
panic("[JIT]: Unhandled Overflow exception in DADDI!");
|
||||||
}
|
}
|
||||||
regs.Write(instr.rt(), result);
|
regs.Write(instr.rt(), result);
|
||||||
@@ -842,7 +844,7 @@ void JIT::dsub(const Instruction instr) {
|
|||||||
auto rs = regs.Read<s64>(instr.rs());
|
auto rs = regs.Read<s64>(instr.rs());
|
||||||
s64 result = rs - rt;
|
s64 result = rs - rt;
|
||||||
if (check_signed_underflow(rs, rt, result)) {
|
if (check_signed_underflow(rs, rt, result)) {
|
||||||
// regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
panic("[JIT]: Unhandled Overflow exception in DSUB!");
|
panic("[JIT]: Unhandled Overflow exception in DSUB!");
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rd(), result);
|
regs.Write(instr.rd(), result);
|
||||||
@@ -928,9 +930,9 @@ void JIT::lb(const Instruction instr) {
|
|||||||
void JIT::ld(const Instruction instr) {
|
void JIT::ld(const Instruction instr) {
|
||||||
if (regs.IsRegConstant(instr.rs())) {
|
if (regs.IsRegConstant(instr.rs())) {
|
||||||
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b111, address)) {
|
if (Core::IsAddressError(0b111, address)) {
|
||||||
// regs.cop0.HandleTLBException(address);
|
// regs.cop0.HandleTLBException(address);
|
||||||
// regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
// return;
|
// return;
|
||||||
panic("[JIT]: Unhandled ADEL exception in LD!");
|
panic("[JIT]: Unhandled ADEL exception in LD!");
|
||||||
}
|
}
|
||||||
@@ -1012,9 +1014,9 @@ void JIT::ldr(const Instruction instr) {
|
|||||||
void JIT::lh(const Instruction instr) {
|
void JIT::lh(const Instruction instr) {
|
||||||
if (regs.IsRegConstant(instr.rs())) {
|
if (regs.IsRegConstant(instr.rs())) {
|
||||||
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b1, address)) {
|
if (Core::IsAddressError(0b1, address)) {
|
||||||
// regs.cop0.HandleTLBException(address);
|
// regs.cop0.HandleTLBException(address);
|
||||||
// regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
// return;
|
// return;
|
||||||
panic("[JIT]: Unhandled ADEL exception in LH!");
|
panic("[JIT]: Unhandled ADEL exception in LH!");
|
||||||
return;
|
return;
|
||||||
@@ -1041,9 +1043,9 @@ void JIT::lhu(const Instruction instr) {
|
|||||||
u32 paddr;
|
u32 paddr;
|
||||||
if (regs.IsRegConstant(instr.rs())) {
|
if (regs.IsRegConstant(instr.rs())) {
|
||||||
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
||||||
if (check_address_error(0b1, address)) {
|
if (Core::IsAddressError(0b1, address)) {
|
||||||
regs.cop0.HandleTLBException(address);
|
regs.cop0.HandleTLBException(address);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1078,9 +1080,9 @@ void JIT::lw(const Instruction instr) {
|
|||||||
u32 paddr = 0;
|
u32 paddr = 0;
|
||||||
if (regs.IsRegConstant(instr.rs())) {
|
if (regs.IsRegConstant(instr.rs())) {
|
||||||
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
// regs.cop0.HandleTLBException(address);
|
// regs.cop0.HandleTLBException(address);
|
||||||
// regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
// return;
|
// return;
|
||||||
panic("[JIT]: Unhandled ADEL exception in LW!");
|
panic("[JIT]: Unhandled ADEL exception in LW!");
|
||||||
return;
|
return;
|
||||||
@@ -1283,7 +1285,7 @@ void JIT::sub(const Instruction instr) {
|
|||||||
s32 rs = regs.Read<s64>(instr.rs());
|
s32 rs = regs.Read<s64>(instr.rs());
|
||||||
s32 result = rs - rt;
|
s32 result = rs - rt;
|
||||||
if (check_signed_underflow(rs, rt, result)) {
|
if (check_signed_underflow(rs, rt, result)) {
|
||||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC);
|
||||||
} else {
|
} else {
|
||||||
regs.Write(instr.rd(), result);
|
regs.Write(instr.rd(), result);
|
||||||
}
|
}
|
||||||
@@ -1342,9 +1344,9 @@ void JIT::sw(const Instruction instr) {
|
|||||||
if (regs.IsRegConstant(instr.rs(), instr.rt())) {
|
if (regs.IsRegConstant(instr.rs(), instr.rt())) {
|
||||||
const s16 offset = instr;
|
const s16 offset = instr;
|
||||||
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
// regs.cop0.HandleTLBException(address);
|
// regs.cop0.HandleTLBException(address);
|
||||||
// regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
panic("[JIT]: Unhandled ADES exception in SW!");
|
panic("[JIT]: Unhandled ADES exception in SW!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1365,9 +1367,9 @@ void JIT::sw(const Instruction instr) {
|
|||||||
if (regs.IsRegConstant(instr.rs())) {
|
if (regs.IsRegConstant(instr.rs())) {
|
||||||
const s16 offset = instr;
|
const s16 offset = instr;
|
||||||
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
||||||
if (check_address_error(0b11, address)) {
|
if (Core::IsAddressError(0b11, address)) {
|
||||||
// regs.cop0.HandleTLBException(address);
|
// regs.cop0.HandleTLBException(address);
|
||||||
// regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
// regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
||||||
panic("[JIT]: Unhandled ADES exception in SW!");
|
panic("[JIT]: Unhandled ADES exception in SW!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ bool PI::WriteLatch(u32 value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PI::ReadLatch() {
|
bool PI::ReadLatch() {
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
if (ioBusy) [[unlikely]] {
|
if (ioBusy) [[unlikely]] {
|
||||||
ioBusy = false;
|
ioBusy = false;
|
||||||
regs.CpuStall(Scheduler::GetInstance().Remove(PI_BUS_WRITE_COMPLETE));
|
regs.CpuStall(Scheduler::GetInstance().Remove(PI_BUS_WRITE_COMPLETE));
|
||||||
@@ -48,12 +48,13 @@ bool PI::ReadLatch() {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
auto PI::BusRead<u8, true>(u32 addr) -> u8 {
|
auto PI::BusRead<u8, true>(u32 addr) -> u8 {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case REGION_PI_UNKNOWN:
|
case REGION_PI_UNKNOWN:
|
||||||
|
mem.DumpRDRAM();
|
||||||
panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
|
panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
|
||||||
"returning FF because it is not emulated",
|
"returning FF because it is not emulated (pc: 0x{:016X})",
|
||||||
addr);
|
addr, (u64)Core::GetRegs().oldPC);
|
||||||
case REGION_PI_64DD_REG:
|
case REGION_PI_64DD_REG:
|
||||||
panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, "
|
panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, "
|
||||||
"returning FF because it is not emulated",
|
"returning FF because it is not emulated",
|
||||||
@@ -83,7 +84,7 @@ auto PI::BusRead<u8, true>(u32 addr) -> u8 {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
auto PI::BusRead<u8, false>(u32 addr) -> u8 {
|
auto PI::BusRead<u8, false>(u32 addr) -> u8 {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
if (!ReadLatch()) [[unlikely]] {
|
if (!ReadLatch()) [[unlikely]] {
|
||||||
return latch >> 24;
|
return latch >> 24;
|
||||||
}
|
}
|
||||||
@@ -123,7 +124,7 @@ auto PI::BusRead<u8, false>(u32 addr) -> u8 {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
void PI::BusWrite<u8, true>(u32 addr, u32 val) {
|
void PI::BusWrite<u8, true>(u32 addr, u32 val) {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case REGION_PI_UNKNOWN:
|
case REGION_PI_UNKNOWN:
|
||||||
panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||||
@@ -161,7 +162,7 @@ void PI::BusWrite<u8, false>(u32 addr, u32 val) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
auto PI::BusRead<u16, false>(u32 addr) -> u16 {
|
auto PI::BusRead<u16, false>(u32 addr) -> u16 {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
if (!ReadLatch()) [[unlikely]] {
|
if (!ReadLatch()) [[unlikely]] {
|
||||||
return latch >> 16;
|
return latch >> 16;
|
||||||
}
|
}
|
||||||
@@ -186,7 +187,8 @@ auto PI::BusRead<u16, false>(u32 addr) -> u16 {
|
|||||||
addr = (addr + 2) & ~3;
|
addr = (addr + 2) & ~3;
|
||||||
const u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM;
|
const u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM;
|
||||||
if (index > mem.rom.cart.size() - 1) {
|
if (index > mem.rom.cart.size() - 1) {
|
||||||
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index);
|
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index,
|
||||||
|
index);
|
||||||
}
|
}
|
||||||
return ircolib::ReadAccess<u16>(mem.rom.cart, index);
|
return ircolib::ReadAccess<u16>(mem.rom.cart, index);
|
||||||
}
|
}
|
||||||
@@ -231,7 +233,7 @@ void PI::BusWrite<u16, true>(u32 addr, u32 val) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
auto PI::BusRead<u32, false>(u32 addr) -> u32 {
|
auto PI::BusRead<u32, false>(u32 addr) -> u32 {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
if (!ReadLatch()) [[unlikely]] {
|
if (!ReadLatch()) [[unlikely]] {
|
||||||
return latch;
|
return latch;
|
||||||
}
|
}
|
||||||
@@ -260,7 +262,8 @@ auto PI::BusRead<u32, false>(u32 addr) -> u32 {
|
|||||||
if (index > mem.rom.cart.size() - 3) { // -3 because we're reading an entire word
|
if (index > mem.rom.cart.size() - 3) { // -3 because we're reading an entire word
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case REGION_CART_ISVIEWER_BUFFER:
|
case REGION_CART_ISVIEWER_BUFFER:
|
||||||
return std::byteswap<u32>(ircolib::ReadAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER));
|
return std::byteswap<u32>(
|
||||||
|
ircolib::ReadAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER));
|
||||||
case CART_ISVIEWER_FLUSH:
|
case CART_ISVIEWER_FLUSH:
|
||||||
panic("Read from ISViewer flush!");
|
panic("Read from ISViewer flush!");
|
||||||
default:
|
default:
|
||||||
@@ -284,7 +287,7 @@ auto PI::BusRead<u32, true>(u32 addr) -> u32 {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
void PI::BusWrite<u32, false>(u32 addr, u32 val) {
|
void PI::BusWrite<u32, false>(u32 addr, u32 val) {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case REGION_PI_UNKNOWN:
|
case REGION_PI_UNKNOWN:
|
||||||
if (!WriteLatch(val)) [[unlikely]] {
|
if (!WriteLatch(val)) [[unlikely]] {
|
||||||
@@ -349,7 +352,7 @@ void PI::BusWrite<u32, true>(u32 addr, u32 val) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
auto PI::BusRead<u64, false>(u32 addr) -> u64 {
|
auto PI::BusRead<u64, false>(u32 addr) -> u64 {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
if (!ReadLatch()) [[unlikely]] {
|
if (!ReadLatch()) [[unlikely]] {
|
||||||
return static_cast<u64>(latch) << 32;
|
return static_cast<u64>(latch) << 32;
|
||||||
}
|
}
|
||||||
@@ -367,7 +370,8 @@ auto PI::BusRead<u64, false>(u32 addr) -> u64 {
|
|||||||
{
|
{
|
||||||
const u32 index = addr - SREGION_PI_ROM;
|
const u32 index = addr - SREGION_PI_ROM;
|
||||||
if (index > mem.rom.cart.size() - 7) { // -7 because we're reading an entire dword
|
if (index > mem.rom.cart.size() - 7) { // -7 because we're reading an entire dword
|
||||||
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index);
|
panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index,
|
||||||
|
index);
|
||||||
}
|
}
|
||||||
return ircolib::ReadAccess<u64>(mem.rom.cart, index);
|
return ircolib::ReadAccess<u64>(mem.rom.cart, index);
|
||||||
}
|
}
|
||||||
@@ -410,7 +414,7 @@ void PI::BusWrite<true>(u32 addr, u64 val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto PI::Read(u32 addr) const -> u32 {
|
auto PI::Read(u32 addr) const -> u32 {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x04600000:
|
case 0x04600000:
|
||||||
return dramAddr & 0x00FFFFFE;
|
return dramAddr & 0x00FFFFFE;
|
||||||
@@ -429,6 +433,7 @@ auto PI::Read(u32 addr) const -> u32 {
|
|||||||
value |= (mem.mmio.mi.intr.pi << 3); // PI interrupt?
|
value |= (mem.mmio.mi.intr.pi << 3); // PI interrupt?
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
case 0x04600034:
|
||||||
case 0x04600014:
|
case 0x04600014:
|
||||||
return piBsdDom1Lat;
|
return piBsdDom1Lat;
|
||||||
case 0x04600018:
|
case 0x04600018:
|
||||||
@@ -499,7 +504,7 @@ u32 PI::AccessTiming(const u8 domain, const u32 length) const {
|
|||||||
// rdram -> cart
|
// rdram -> cart
|
||||||
template <>
|
template <>
|
||||||
void PI::DMA<false>() {
|
void PI::DMA<false>() {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
const s32 len = rdLen + 1;
|
const s32 len = rdLen + 1;
|
||||||
trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr);
|
trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr);
|
||||||
|
|
||||||
@@ -523,12 +528,12 @@ void PI::DMA<false>() {
|
|||||||
// cart -> rdram
|
// cart -> rdram
|
||||||
template <>
|
template <>
|
||||||
void PI::DMA<true>() {
|
void PI::DMA<true>() {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
const s32 len = wrLen + 1;
|
const s32 len = wrLen + 1;
|
||||||
trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr);
|
trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr);
|
||||||
|
|
||||||
if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) {
|
if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) {
|
||||||
cartAddr = SREGION_PI_SRAM | ((cartAddr & (1_mb-1)) << 1);
|
cartAddr = SREGION_PI_SRAM | ((cartAddr & (1_mb - 1)) << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u32 i = 0; i < len; i++) {
|
for (u32 i = 0; i < len; i++) {
|
||||||
@@ -547,7 +552,7 @@ void PI::DMA<true>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PI::Write(u32 addr, u32 val) {
|
void PI::Write(u32 addr, u32 val) {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
MI &mi = mem.mmio.mi;
|
MI &mi = mem.mmio.mi;
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x04600000:
|
case 0x04600000:
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ struct PI {
|
|||||||
u32 piBsdDom1Pgs{}, piBsdDom2Pgs{};
|
u32 piBsdDom1Pgs{}, piBsdDom2Pgs{};
|
||||||
u32 piBsdDom1Rls{}, piBsdDom2Rls{};
|
u32 piBsdDom1Rls{}, piBsdDom2Rls{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <bool toDram>
|
template <bool toDram>
|
||||||
void DMA();
|
void DMA();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ void SI::Reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto SI::Read(u32 addr) const -> u32 {
|
auto SI::Read(u32 addr) const -> u32 {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x04800000:
|
case 0x04800000:
|
||||||
return dramAddr;
|
return dramAddr;
|
||||||
@@ -39,7 +39,7 @@ auto SI::Read(u32 addr) const -> u32 {
|
|||||||
// pif -> rdram
|
// pif -> rdram
|
||||||
template <>
|
template <>
|
||||||
void SI::DMA<true>() {
|
void SI::DMA<true>() {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
pif.ProcessCommands();
|
pif.ProcessCommands();
|
||||||
for (int i = 0; i < 64; i++) {
|
for (int i = 0; i < 64; i++) {
|
||||||
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, pif.Read(pifAddr + i));
|
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, pif.Read(pifAddr + i));
|
||||||
@@ -50,7 +50,7 @@ void SI::DMA<true>() {
|
|||||||
// rdram -> pif
|
// rdram -> pif
|
||||||
template <>
|
template <>
|
||||||
void SI::DMA<false>() {
|
void SI::DMA<false>() {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
for (int i = 0; i < 64; i++) {
|
for (int i = 0; i < 64; i++) {
|
||||||
pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM<u8>(dramAddr + i));
|
pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM<u8>(dramAddr + i));
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ void SI::DMA<false>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SI::DMA() {
|
void SI::DMA() {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
status.dmaBusy = false;
|
status.dmaBusy = false;
|
||||||
if (toDram)
|
if (toDram)
|
||||||
DMA<true>();
|
DMA<true>();
|
||||||
@@ -68,7 +68,7 @@ void SI::DMA() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SI::Write(u32 addr, u32 val) {
|
void SI::Write(u32 addr, u32 val) {
|
||||||
n64::Mem& mem = n64::Core::GetMem();
|
n64::Mem &mem = n64::Core::GetMem();
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x04800000:
|
case 0x04800000:
|
||||||
dramAddr = val & RDRAM_DSIZE;
|
dramAddr = val & RDRAM_DSIZE;
|
||||||
|
|||||||
@@ -4,19 +4,6 @@
|
|||||||
#include <core/mmio/PIF.hpp>
|
#include <core/mmio/PIF.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
|
|
||||||
union SIStatus {
|
|
||||||
u32 raw{};
|
|
||||||
struct {
|
|
||||||
unsigned dmaBusy : 1;
|
|
||||||
unsigned ioBusy : 1;
|
|
||||||
unsigned reserved : 1;
|
|
||||||
unsigned dmaErr : 1;
|
|
||||||
unsigned : 8;
|
|
||||||
unsigned intr : 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SI {
|
struct SI {
|
||||||
SI();
|
SI();
|
||||||
void Reset();
|
void Reset();
|
||||||
@@ -26,8 +13,20 @@ struct SI {
|
|||||||
void DMA();
|
void DMA();
|
||||||
void DMA();
|
void DMA();
|
||||||
|
|
||||||
|
union Status {
|
||||||
|
u32 raw{};
|
||||||
|
struct {
|
||||||
|
unsigned dmaBusy : 1;
|
||||||
|
unsigned ioBusy : 1;
|
||||||
|
unsigned reserved : 1;
|
||||||
|
unsigned dmaErr : 1;
|
||||||
|
unsigned : 8;
|
||||||
|
unsigned intr : 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
bool toDram = false;
|
bool toDram = false;
|
||||||
SIStatus status{};
|
Status status{};
|
||||||
u32 dramAddr{};
|
u32 dramAddr{};
|
||||||
u32 pifAddr{};
|
u32 pifAddr{};
|
||||||
PIF pif;
|
PIF pif;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ void Cop0::Reset() {
|
|||||||
LLAddr = {}, WatchLo = {}, WatchHi = {};
|
LLAddr = {}, WatchLo = {}, WatchHi = {};
|
||||||
xcontext = {};
|
xcontext = {};
|
||||||
r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {};
|
r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {};
|
||||||
ParityError = {}, CacheError = {}, TagLo = {}, TagHi = {};
|
ParityError = {}, CacheError = {}, tagLo = {}, TagHi = {};
|
||||||
ErrorEPC = {};
|
ErrorEPC = {};
|
||||||
r31 = {};
|
r31 = {};
|
||||||
memset(tlb, 0, sizeof(TLBEntry) * 32);
|
memset(tlb, 0, sizeof(TLBEntry) * 32);
|
||||||
@@ -89,7 +89,7 @@ u32 Cop0::GetReg32(const u8 addr) {
|
|||||||
case COP0_REG_CACHE_ERR:
|
case COP0_REG_CACHE_ERR:
|
||||||
return CacheError;
|
return CacheError;
|
||||||
case COP0_REG_TAGLO:
|
case COP0_REG_TAGLO:
|
||||||
return TagLo;
|
return tagLo.raw;
|
||||||
case COP0_REG_TAGHI:
|
case COP0_REG_TAGHI:
|
||||||
return TagHi;
|
return TagHi;
|
||||||
case COP0_REG_ERROREPC:
|
case COP0_REG_ERROREPC:
|
||||||
@@ -186,7 +186,7 @@ void Cop0::SetReg32(const u8 addr, const u32 value) {
|
|||||||
break;
|
break;
|
||||||
case COP0_REG_CAUSE:
|
case COP0_REG_CAUSE:
|
||||||
{
|
{
|
||||||
Cop0Cause tmp{};
|
Cause tmp{};
|
||||||
tmp.raw = value;
|
tmp.raw = value;
|
||||||
cause.ip0 = tmp.ip0;
|
cause.ip0 = tmp.ip0;
|
||||||
cause.ip1 = tmp.ip1;
|
cause.ip1 = tmp.ip1;
|
||||||
@@ -219,7 +219,7 @@ void Cop0::SetReg32(const u8 addr, const u32 value) {
|
|||||||
case COP0_REG_CACHE_ERR:
|
case COP0_REG_CACHE_ERR:
|
||||||
break;
|
break;
|
||||||
case COP0_REG_TAGLO:
|
case COP0_REG_TAGLO:
|
||||||
TagLo = value;
|
tagLo.raw = value;
|
||||||
break;
|
break;
|
||||||
case COP0_REG_TAGHI:
|
case COP0_REG_TAGHI:
|
||||||
TagHi = value;
|
TagHi = value;
|
||||||
@@ -263,7 +263,7 @@ void Cop0::SetReg64(const u8 addr, const u64 value) {
|
|||||||
break;
|
break;
|
||||||
case COP0_REG_CAUSE:
|
case COP0_REG_CAUSE:
|
||||||
{
|
{
|
||||||
Cop0Cause tmp{};
|
Cause tmp{};
|
||||||
tmp.raw = value;
|
tmp.raw = value;
|
||||||
cause.ip0 = tmp.ip0;
|
cause.ip0 = tmp.ip0;
|
||||||
cause.ip1 = tmp.ip1;
|
cause.ip1 = tmp.ip1;
|
||||||
@@ -292,7 +292,7 @@ static FORCE_INLINE u64 getVPN(const u64 addr, const u64 pageMask) {
|
|||||||
return vpn & ~mask;
|
return vpn & ~mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) {
|
Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) {
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
TLBEntry *entry = &tlb[i];
|
TLBEntry *entry = &tlb[i];
|
||||||
if (!entry->initialized)
|
if (!entry->initialized)
|
||||||
@@ -304,7 +304,7 @@ TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) {
|
|||||||
const bool vpn_match = entry_vpn == vaddr_vpn;
|
const bool vpn_match = entry_vpn == vaddr_vpn;
|
||||||
const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid;
|
const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid;
|
||||||
|
|
||||||
if(!vpn_match || !asid_match)
|
if (!vpn_match || !asid_match)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
index = i;
|
index = i;
|
||||||
@@ -314,7 +314,7 @@ TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) {
|
Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) {
|
||||||
for (auto &t : tlb) {
|
for (auto &t : tlb) {
|
||||||
TLBEntry *entry = &t;
|
TLBEntry *entry = &t;
|
||||||
if (!entry->initialized)
|
if (!entry->initialized)
|
||||||
@@ -361,13 +361,16 @@ bool Cop0::ProbeTLB(const TLBAccessType accessType, const u64 vaddr, u32 &paddr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) {
|
void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) {
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
|
|
||||||
|
Core::GetInstance().interpreter.SignalException(pc);
|
||||||
|
|
||||||
u16 vectorOffset = 0x0180;
|
u16 vectorOffset = 0x0180;
|
||||||
if(tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) {
|
if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) {
|
||||||
if(!status.exl) {
|
if (!status.exl) {
|
||||||
if(is64BitAddressing) vectorOffset = 0x0080;
|
vectorOffset = 0x0000;
|
||||||
else vectorOffset = 0x0000;
|
if (is64BitAddressing)
|
||||||
|
vectorOffset = 0x0080;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +405,7 @@ void Cop0::HandleTLBException(const u64 vaddr) {
|
|||||||
entryHi.r = vaddr >> 62 & 3;
|
entryHi.r = vaddr >> 62 & 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) {
|
Cop0::ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case NONE:
|
case NONE:
|
||||||
panic("Getting TLB exception with error NONE");
|
panic("Getting TLB exception with error NONE");
|
||||||
@@ -420,22 +423,39 @@ ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessTyp
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cop0::decode(const Instruction instr) {
|
void Cop0::decode(const Instruction instr) {
|
||||||
Registers& regs = Core::GetRegs();
|
Registers ®s = Core::GetRegs();
|
||||||
switch (instr.cop_rs()) {
|
switch (instr.cop_rs()) {
|
||||||
case 0x00: mfc0(instr); break;
|
case 0x00:
|
||||||
case 0x01: dmfc0(instr); break;
|
mfc0(instr);
|
||||||
case 0x04: mtc0(instr); break;
|
break;
|
||||||
case 0x05: dmtc0(instr); break;
|
case 0x01:
|
||||||
|
dmfc0(instr);
|
||||||
|
break;
|
||||||
|
case 0x04:
|
||||||
|
mtc0(instr);
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
dmtc0(instr);
|
||||||
|
break;
|
||||||
case 0x10 ... 0x1F:
|
case 0x10 ... 0x1F:
|
||||||
switch (instr.cop_funct()) {
|
switch (instr.cop_funct()) {
|
||||||
case 0x01: tlbr(); break;
|
case 0x01:
|
||||||
case 0x02: tlbw(index.i); break;
|
tlbr();
|
||||||
case 0x06: tlbw(GetRandom()); break;
|
break;
|
||||||
case 0x08: tlbp(); break;
|
case 0x02:
|
||||||
case 0x18: eret(); break;
|
tlbw(index.i);
|
||||||
|
break;
|
||||||
|
case 0x06:
|
||||||
|
tlbw(GetRandom());
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
tlbp();
|
||||||
|
break;
|
||||||
|
case 0x18:
|
||||||
|
eret();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr),
|
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC);
|
||||||
regs.oldPC);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -446,7 +466,7 @@ void Cop0::decode(const Instruction instr) {
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool Cop0::MapVirtualAddress<u32, true>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
bool Cop0::MapVirtualAddress<u32, true>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
||||||
if(ircolib::IsInsideRange(vaddr, START_VREGION_KUSEG, END_VREGION_KUSEG))
|
if (ircolib::IsInsideRange(vaddr, START_VREGION_KUSEG, END_VREGION_KUSEG))
|
||||||
return ProbeTLB(accessType, s64(s32(vaddr)), paddr);
|
return ProbeTLB(accessType, s64(s32(vaddr)), paddr);
|
||||||
|
|
||||||
tlbError = DISALLOWED_ADDRESS;
|
tlbError = DISALLOWED_ADDRESS;
|
||||||
@@ -456,15 +476,15 @@ bool Cop0::MapVirtualAddress<u32, true>(const TLBAccessType accessType, const u6
|
|||||||
template <>
|
template <>
|
||||||
bool Cop0::MapVirtualAddress<u32, false>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
bool Cop0::MapVirtualAddress<u32, false>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
||||||
u8 segment = static_cast<u32>(vaddr) >> 29 & 7;
|
u8 segment = static_cast<u32>(vaddr) >> 29 & 7;
|
||||||
if(ircolib::IsInsideRange(segment, 0, 3) || segment == 7)
|
if (ircolib::IsInsideRange(segment, 0, 3) || segment == 7)
|
||||||
return ProbeTLB(accessType, static_cast<s32>(vaddr), paddr);
|
return ProbeTLB(accessType, static_cast<s32>(vaddr), paddr);
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(segment, 4, 5)) {
|
if (ircolib::IsInsideRange(segment, 4, 5)) {
|
||||||
paddr = vaddr & 0x1FFFFFFF;
|
paddr = vaddr & 0x1FFFFFFF;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(segment == 6)
|
if (segment == 6)
|
||||||
panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr);
|
panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr);
|
||||||
|
|
||||||
panic("Should never end up in base case in MapVirtualAddress! ({:08X})", vaddr);
|
panic("Should never end up in base case in MapVirtualAddress! ({:08X})", vaddr);
|
||||||
@@ -474,7 +494,7 @@ bool Cop0::MapVirtualAddress<u32, false>(const TLBAccessType accessType, const u
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool Cop0::MapVirtualAddress<u64, true>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
bool Cop0::MapVirtualAddress<u64, true>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
||||||
if(ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF))
|
if (ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF))
|
||||||
return ProbeTLB(accessType, vaddr, paddr);
|
return ProbeTLB(accessType, vaddr, paddr);
|
||||||
|
|
||||||
tlbError = DISALLOWED_ADDRESS;
|
tlbError = DISALLOWED_ADDRESS;
|
||||||
@@ -483,13 +503,13 @@ bool Cop0::MapVirtualAddress<u64, true>(const TLBAccessType accessType, const u6
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool Cop0::MapVirtualAddress<u64, false>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
bool Cop0::MapVirtualAddress<u64, false>(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
||||||
if(ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF) || // VREGION_XKUSEG
|
if (ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF) || // VREGION_XKUSEG
|
||||||
ircolib::IsInsideRange(vaddr, 0x4000000000000000, 0x400000FFFFFFFFFF) || // VREGION_XKSSEG
|
ircolib::IsInsideRange(vaddr, 0x4000000000000000, 0x400000FFFFFFFFFF) || // VREGION_XKSSEG
|
||||||
ircolib::IsInsideRange(vaddr, 0xC000000000000000, 0xC00000FF7FFFFFFF) || // VREGION_XKSEG
|
ircolib::IsInsideRange(vaddr, 0xC000000000000000, 0xC00000FF7FFFFFFF) || // VREGION_XKSEG
|
||||||
ircolib::IsInsideRange(vaddr, 0xFFFFFFFFE0000000, 0xFFFFFFFFFFFFFFFF)) // VREGION_CKSEG3
|
ircolib::IsInsideRange(vaddr, 0xFFFFFFFFE0000000, 0xFFFFFFFFFFFFFFFF)) // VREGION_CKSEG3
|
||||||
return ProbeTLB(accessType, vaddr, paddr);
|
return ProbeTLB(accessType, vaddr, paddr);
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(vaddr, 0x8000000000000000, 0xBFFFFFFFFFFFFFFF)) { // VREGION_XKPHYS
|
if (ircolib::IsInsideRange(vaddr, 0x8000000000000000, 0xBFFFFFFFFFFFFFFF)) { // VREGION_XKPHYS
|
||||||
if (!kernelMode)
|
if (!kernelMode)
|
||||||
panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr);
|
panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr);
|
||||||
|
|
||||||
@@ -510,7 +530,7 @@ bool Cop0::MapVirtualAddress<u64, false>(const TLBAccessType accessType, const u
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(vaddr, 0xFFFFFFFF80000000, 0xFFFFFFFF9FFFFFFF) || // VREGION_CKSEG0
|
if (ircolib::IsInsideRange(vaddr, 0xFFFFFFFF80000000, 0xFFFFFFFF9FFFFFFF) || // VREGION_CKSEG0
|
||||||
ircolib::IsInsideRange(vaddr, 0xFFFFFFFFA0000000, 0xFFFFFFFFBFFFFFFF)) { // VREGION_CKSEG1
|
ircolib::IsInsideRange(vaddr, 0xFFFFFFFFA0000000, 0xFFFFFFFFBFFFFFFF)) { // VREGION_CKSEG1
|
||||||
u32 cut = u32(vaddr) >> 28;
|
u32 cut = u32(vaddr) >> 28;
|
||||||
u32 num = cut == 0xA;
|
u32 num = cut == 0xA;
|
||||||
@@ -521,7 +541,7 @@ bool Cop0::MapVirtualAddress<u64, false>(const TLBAccessType accessType, const u
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(vaddr, 0x0000010000000000, 0x3FFFFFFFFFFFFFFF) || // VREGION_XBAD1
|
if (ircolib::IsInsideRange(vaddr, 0x0000010000000000, 0x3FFFFFFFFFFFFFFF) || // VREGION_XBAD1
|
||||||
ircolib::IsInsideRange(vaddr, 0x4000010000000000, 0x7FFFFFFFFFFFFFFF) || // VREGION_XBAD2
|
ircolib::IsInsideRange(vaddr, 0x4000010000000000, 0x7FFFFFFFFFFFFFFF) || // VREGION_XBAD2
|
||||||
ircolib::IsInsideRange(vaddr, 0xC00000FF80000000, 0xFFFFFFFF7FFFFFFF)) { // VREGION_XBAD3
|
ircolib::IsInsideRange(vaddr, 0xC00000FF80000000, 0xFFFFFFFF7FFFFFFF)) { // VREGION_XBAD3
|
||||||
tlbError = DISALLOWED_ADDRESS;
|
tlbError = DISALLOWED_ADDRESS;
|
||||||
@@ -533,18 +553,22 @@ bool Cop0::MapVirtualAddress<u64, false>(const TLBAccessType accessType, const u
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Cop0::MapVAddr(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
bool Cop0::MapVAddr(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) {
|
||||||
if(supervisorMode)
|
if (supervisorMode)
|
||||||
panic("Supervisor mode memory access");
|
panic("Supervisor mode memory access");
|
||||||
|
|
||||||
if (is64BitAddressing) [[unlikely]] {
|
if (is64BitAddressing) [[unlikely]] {
|
||||||
if (kernelMode) [[likely]] return MapVirtualAddress<u64, false>(accessType, vaddr, paddr);
|
if (kernelMode) [[likely]]
|
||||||
if (userMode) return MapVirtualAddress<u64, true>(accessType, vaddr, paddr);
|
return MapVirtualAddress<u64, false>(accessType, vaddr, paddr);
|
||||||
|
if (userMode)
|
||||||
|
return MapVirtualAddress<u64, true>(accessType, vaddr, paddr);
|
||||||
|
|
||||||
panic("Unknown mode! This should never happen!");
|
panic("Unknown mode! This should never happen!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kernelMode) [[likely]] return MapVirtualAddress<u32, false>(accessType, vaddr, paddr);
|
if (kernelMode) [[likely]]
|
||||||
if (userMode) return MapVirtualAddress<u32, true>(accessType, vaddr, paddr);
|
return MapVirtualAddress<u32, false>(accessType, vaddr, paddr);
|
||||||
|
if (userMode)
|
||||||
|
return MapVirtualAddress<u32, true>(accessType, vaddr, paddr);
|
||||||
|
|
||||||
panic("Unknown mode! This should never happen!");
|
panic("Unknown mode! This should never happen!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ namespace n64 {
|
|||||||
#define ENTRY_HI_MASK 0xC00000FFFFFFE0FF
|
#define ENTRY_HI_MASK 0xC00000FFFFFFE0FF
|
||||||
#define PAGEMASK_MASK 0x1FFE000
|
#define PAGEMASK_MASK 0x1FFE000
|
||||||
|
|
||||||
union Cop0Cause {
|
struct Cop0 {
|
||||||
|
union Cause {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
struct {
|
struct {
|
||||||
unsigned : 8;
|
unsigned : 8;
|
||||||
@@ -62,9 +63,9 @@ union Cop0Cause {
|
|||||||
unsigned : 1;
|
unsigned : 1;
|
||||||
unsigned branchDelay : 1;
|
unsigned branchDelay : 1;
|
||||||
} __attribute__((__packed__));
|
} __attribute__((__packed__));
|
||||||
};
|
};
|
||||||
|
|
||||||
union Cop0Status {
|
union Status {
|
||||||
struct {
|
struct {
|
||||||
unsigned ie : 1;
|
unsigned ie : 1;
|
||||||
unsigned exl : 1;
|
unsigned exl : 1;
|
||||||
@@ -97,9 +98,9 @@ union Cop0Status {
|
|||||||
unsigned : 7;
|
unsigned : 7;
|
||||||
} __attribute__((__packed__));
|
} __attribute__((__packed__));
|
||||||
u32 raw;
|
u32 raw;
|
||||||
} __attribute__((__packed__));
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
union EntryLo {
|
union EntryLo {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
struct {
|
struct {
|
||||||
unsigned g : 1;
|
unsigned g : 1;
|
||||||
@@ -109,9 +110,9 @@ union EntryLo {
|
|||||||
unsigned pfn : 20;
|
unsigned pfn : 20;
|
||||||
unsigned : 6;
|
unsigned : 6;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
union EntryHi {
|
union EntryHi {
|
||||||
u64 raw;
|
u64 raw;
|
||||||
struct {
|
struct {
|
||||||
u64 asid : 8;
|
u64 asid : 8;
|
||||||
@@ -120,38 +121,38 @@ union EntryHi {
|
|||||||
u64 fill : 22;
|
u64 fill : 22;
|
||||||
u64 r : 2;
|
u64 r : 2;
|
||||||
} __attribute__((__packed__));
|
} __attribute__((__packed__));
|
||||||
};
|
};
|
||||||
|
|
||||||
union PageMask {
|
union PageMask {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
struct {
|
struct {
|
||||||
unsigned : 13;
|
unsigned : 13;
|
||||||
unsigned mask : 12;
|
unsigned mask : 12;
|
||||||
unsigned : 7;
|
unsigned : 7;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
union Index {
|
union Index {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
struct {
|
struct {
|
||||||
unsigned i : 6;
|
unsigned i : 6;
|
||||||
unsigned : 25;
|
unsigned : 25;
|
||||||
unsigned p : 1;
|
unsigned p : 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TLBEntry {
|
struct TLBEntry {
|
||||||
bool initialized;
|
bool initialized;
|
||||||
EntryLo entryLo0, entryLo1;
|
EntryLo entryLo0, entryLo1;
|
||||||
EntryHi entryHi;
|
EntryHi entryHi;
|
||||||
PageMask pageMask;
|
PageMask pageMask;
|
||||||
|
|
||||||
bool global;
|
bool global;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS };
|
enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS };
|
||||||
|
|
||||||
enum class ExceptionCode : u8 {
|
enum class ExceptionCode : u8 {
|
||||||
Interrupt = 0,
|
Interrupt = 0,
|
||||||
TLBModification = 1,
|
TLBModification = 1,
|
||||||
TLBLoad = 2,
|
TLBLoad = 2,
|
||||||
@@ -168,18 +169,18 @@ enum class ExceptionCode : u8 {
|
|||||||
Trap = 13,
|
Trap = 13,
|
||||||
FloatingPointError = 15,
|
FloatingPointError = 15,
|
||||||
Watch = 23
|
Watch = 23
|
||||||
};
|
};
|
||||||
|
|
||||||
union Cop0Context {
|
union Context {
|
||||||
u64 raw;
|
u64 raw;
|
||||||
struct {
|
struct {
|
||||||
u64 : 4;
|
u64 : 4;
|
||||||
u64 badvpn2 : 19;
|
u64 badvpn2 : 19;
|
||||||
u64 ptebase : 41;
|
u64 ptebase : 41;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
union Cop0XContext {
|
union XContext {
|
||||||
u64 raw;
|
u64 raw;
|
||||||
struct {
|
struct {
|
||||||
u64 : 4;
|
u64 : 4;
|
||||||
@@ -187,9 +188,18 @@ union Cop0XContext {
|
|||||||
u64 r : 2;
|
u64 r : 2;
|
||||||
u64 ptebase : 31;
|
u64 ptebase : 31;
|
||||||
} __attribute__((__packed__));
|
} __attribute__((__packed__));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union TagLo {
|
||||||
|
u32 raw;
|
||||||
|
struct {
|
||||||
|
u64 : 4;
|
||||||
|
u64 ptaglo : 20;
|
||||||
|
u64 pstate : 2;
|
||||||
|
u64 : 6;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
};
|
||||||
|
|
||||||
struct Cop0 {
|
|
||||||
Cop0();
|
Cop0();
|
||||||
|
|
||||||
bool kernelMode{true};
|
bool kernelMode{true};
|
||||||
@@ -199,19 +209,20 @@ struct Cop0 {
|
|||||||
bool llbit{};
|
bool llbit{};
|
||||||
TLBError tlbError = NONE;
|
TLBError tlbError = NONE;
|
||||||
|
|
||||||
|
TagLo tagLo;
|
||||||
PageMask pageMask{};
|
PageMask pageMask{};
|
||||||
EntryHi entryHi{};
|
EntryHi entryHi{};
|
||||||
EntryLo entryLo0{}, entryLo1{};
|
EntryLo entryLo0{}, entryLo1{};
|
||||||
Index index{};
|
Index index{};
|
||||||
Cop0Context context{};
|
Context context{};
|
||||||
u32 wired{}, r7{};
|
u32 wired{}, r7{};
|
||||||
u32 compare{};
|
u32 compare{};
|
||||||
Cop0Status status{};
|
Status status{};
|
||||||
Cop0Cause cause{};
|
Cause cause{};
|
||||||
u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{};
|
u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{};
|
||||||
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{};
|
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagHi{};
|
||||||
u32 r31{};
|
u32 r31{};
|
||||||
Cop0XContext xcontext{};
|
XContext xcontext{};
|
||||||
u64 badVaddr{}, count{};
|
u64 badVaddr{}, count{};
|
||||||
s64 EPC{};
|
s64 EPC{};
|
||||||
s64 ErrorEPC{};
|
s64 ErrorEPC{};
|
||||||
@@ -263,7 +274,7 @@ struct Cop0 {
|
|||||||
is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux);
|
is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct JIT;
|
friend struct JIT;
|
||||||
|
|
||||||
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
||||||
|
|||||||
@@ -150,13 +150,13 @@ struct Cop1 {
|
|||||||
bool SetCauseOverflow();
|
bool SetCauseOverflow();
|
||||||
bool SetCauseInvalid();
|
bool SetCauseInvalid();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto FGR_T(const Cop0Status &, u32) -> T &;
|
auto FGR_T(const Cop0::Status &, u32) -> T &;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto FGR_S(const Cop0Status &, u32) -> T &;
|
auto FGR_S(const Cop0::Status &, u32) -> T &;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto FGR_D(const Cop0Status &, u32) -> T &;
|
auto FGR_D(const Cop0::Status &, u32) -> T &;
|
||||||
void absd(const Instruction instr);
|
void absd(const Instruction instr);
|
||||||
void abss(const Instruction instr);
|
void abss(const Instruction instr);
|
||||||
void adds(const Instruction instr);
|
void adds(const Instruction instr);
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
#include <core/JIT.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;
|
||||||
@@ -72,85 +76,85 @@ s8 Registers::Read<s8>(size_t idx) {
|
|||||||
return static_cast<s8>(Read<u8>(idx));
|
return static_cast<s8>(Read<u8>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt64(), Read<u64>(idx));
|
jit.code.mov(reg.cvt64(), Read<u64>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt64(), Read<s64>(idx));
|
jit.code.mov(reg.cvt64(), Read<s64>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt32(), Read<u32>(idx));
|
jit.code.mov(reg.cvt32(), Read<u32>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt32(), jit->GPR<u32>(idx));
|
jit.code.mov(reg.cvt32(), jit.GPR<u32>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt32(), Read<s32>(idx));
|
jit.code.mov(reg.cvt32(), Read<s32>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt32(), jit->GPR<s32>(idx));
|
jit.code.mov(reg.cvt32(), jit.GPR<s32>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt16(), Read<u16>(idx));
|
jit.code.mov(reg.cvt16(), Read<u16>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt16(), Read<s16>(idx));
|
jit.code.mov(reg.cvt16(), Read<s16>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt8(), Read<u8>(idx));
|
jit.code.mov(reg.cvt8(), Read<u8>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt8(), jit->GPR<u8>(idx));
|
jit.code.mov(reg.cvt8(), jit.GPR<u8>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
|
||||||
if(IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt8(), Read<s8>(idx));
|
jit.code.mov(reg.cvt8(), Read<s8>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt8(), jit->GPR<s8>(idx));
|
jit.code.mov(reg.cvt8(), jit.GPR<s8>(idx));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -159,7 +163,6 @@ void Registers::Write<bool>(size_t idx, bool v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
@@ -170,8 +173,8 @@ void Registers::Write<u64>(size_t idx, u64 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,8 +188,8 @@ void Registers::Write<u32>(size_t idx, u32 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,8 +199,8 @@ void Registers::Write<s32>(size_t idx, s32 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +209,8 @@ void Registers::Write<u16>(size_t idx, u16 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,8 +220,8 @@ void Registers::Write<s16>(size_t idx, s16 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,8 +230,8 @@ void Registers::Write<u8>(size_t idx, u8 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,24 +241,21 @@ void Registers::Write<s8>(size_t idx, s8 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
template <>
|
template <>
|
||||||
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
|
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
|
||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -263,13 +263,10 @@ void Registers::Write<s8>(size_t idx, Xbyak::Reg v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -277,13 +274,10 @@ void Registers::Write<u8>(size_t idx, Xbyak::Reg v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movzx(v.cvt64(), v.cvt8());
|
jit.code.movzx(v.cvt64(), v.cvt8());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -291,13 +285,10 @@ void Registers::Write<s16>(size_t idx, Xbyak::Reg v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsx(v.cvt64(), v.cvt16());
|
jit.code.movsx(v.cvt64(), v.cvt16());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -310,8 +301,8 @@ void Registers::Write<u16>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movzx(v.cvt64(), v.cvt16());
|
jit.code.movzx(v.cvt64(), v.cvt16());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -324,8 +315,8 @@ void Registers::Write<s32>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsxd(v.cvt64(), v.cvt32());
|
jit.code.movsxd(v.cvt64(), v.cvt32());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -338,8 +329,8 @@ void Registers::Write<u32>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movzx(v.cvt64(), v.cvt32());
|
jit.code.movzx(v.cvt64(), v.cvt32());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -352,7 +343,7 @@ void Registers::Write<u64>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|||||||
@@ -4,13 +4,17 @@
|
|||||||
#include <backend/core/registers/Cop1.hpp>
|
#include <backend/core/registers/Cop1.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
struct JIT;
|
struct JIT;
|
||||||
|
struct Registers {
|
||||||
|
JIT &jit;
|
||||||
|
Registers(JIT &jit);
|
||||||
|
#else
|
||||||
struct Registers {
|
struct Registers {
|
||||||
Registers();
|
Registers();
|
||||||
void Reset();
|
void Reset();
|
||||||
void SetPC64(s64);
|
void SetPC64(s64);
|
||||||
void SetPC32(s32);
|
void SetPC32(s32);
|
||||||
void SetJIT(JIT* jit) { this->jit = jit; }
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
@@ -22,31 +26,17 @@ struct Registers {
|
|||||||
return IsRegConstant(index1) && IsRegConstant(index2);
|
return IsRegConstant(index1) && IsRegConstant(index2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetLOConstant() {
|
bool GetLOConstant() { return regIsConstant & (1ull << 32); }
|
||||||
return regIsConstant & (1ull << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetHIConstant() {
|
bool GetHIConstant() { return regIsConstant & (1ull << 33); }
|
||||||
return regIsConstant & (1ull << 33);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetLOConstant() {
|
void SetLOConstant() { regIsConstant |= (1ull << 32); }
|
||||||
regIsConstant |= (1ull << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetHIConstant() {
|
void SetHIConstant() { regIsConstant |= (1ull << 33); }
|
||||||
regIsConstant |= (1ull << 33);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnsetLOConstant() {
|
void UnsetLOConstant() { regIsConstant &= ~(1ull << 32); }
|
||||||
regIsConstant &= ~(1ull << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnsetHIConstant() {
|
void UnsetHIConstant() { regIsConstant &= ~(1ull << 33); }
|
||||||
regIsConstant &= ~(1ull << 33);
|
|
||||||
}
|
|
||||||
|
|
||||||
JIT *jit = nullptr;
|
|
||||||
|
|
||||||
uint64_t regIsConstant = 0;
|
uint64_t regIsConstant = 0;
|
||||||
|
|
||||||
@@ -76,5 +66,10 @@ struct Registers {
|
|||||||
void Write(size_t, Xbyak::Reg);
|
void Write(size_t, Xbyak::Reg);
|
||||||
|
|
||||||
std::array<s64, 32> gpr{};
|
std::array<s64, 32> gpr{};
|
||||||
|
|
||||||
|
static inline const char *regNames[] = {"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2",
|
||||||
|
"t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5",
|
||||||
|
"s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"};
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
+51
-57
@@ -1,26 +1,16 @@
|
|||||||
#include <Debugger.hpp>
|
#include <Debugger.hpp>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
#include <Registers.hpp>
|
||||||
|
|
||||||
char const* regNames[] = {
|
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult &) {
|
||||||
"zero", "at", "v0", "v1",
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
"a0", "a1", "a2", "a3",
|
|
||||||
"t0", "t1", "t2", "t3",
|
|
||||||
"t4", "t5", "t6", "t7",
|
|
||||||
"s0", "s1", "s2", "s3",
|
|
||||||
"s4", "s5", "s6", "s7",
|
|
||||||
"t8", "t9", "k0", "k1",
|
|
||||||
"gp", "sp", "s8", "ra",
|
|
||||||
};
|
|
||||||
|
|
||||||
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult&) {
|
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
|
||||||
bool isBroken = core.breakpoints.contains(addr);
|
bool isBroken = core.breakpoints.contains(addr);
|
||||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff);
|
ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff);
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0);
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0);
|
||||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff);
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f);
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f);
|
||||||
if(ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) {
|
if (ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) {
|
||||||
core.ToggleBreakpoint(addr);
|
core.ToggleBreakpoint(addr);
|
||||||
}
|
}
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
@@ -30,8 +20,8 @@ void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult&) {
|
|||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
void AddressFunc(s64, Disassembler::DisassemblyResult &disasm) {
|
||||||
if(!disasm.success) {
|
if (!disasm.success) {
|
||||||
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -39,26 +29,26 @@ void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
|||||||
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
void InstructionFunc(s64, Disassembler::DisassemblyResult &disasm) {
|
||||||
if(!disasm.success) {
|
if (!disasm.success) {
|
||||||
ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful...");
|
ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str());
|
ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str());
|
||||||
ImGui::SameLine(0, 0);
|
ImGui::SameLine(0, 0);
|
||||||
for(int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
if(disasm.ops[i].str.empty())
|
if (disasm.ops[i].str.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(i >= 2) {
|
if (i >= 2) {
|
||||||
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str());
|
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str());
|
||||||
ImGui::SameLine(0, 0);
|
ImGui::SameLine(0, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string op_str = disasm.ops[i].str;
|
std::string op_str = disasm.ops[i].str;
|
||||||
if(!disasm.ops[i+1].str.empty())
|
if (!disasm.ops[i + 1].str.empty())
|
||||||
op_str += ", ";
|
op_str += ", ";
|
||||||
|
|
||||||
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str());
|
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str());
|
||||||
@@ -67,10 +57,13 @@ void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::RegisterView() {
|
void Debugger::RegisterView() {
|
||||||
if(!ImGui::BeginTabItem("Registers"))
|
if (!ImGui::BeginTabItem("Registers"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody))
|
if (!ImGui::BeginTable("##regs", 4,
|
||||||
|
ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable |
|
||||||
|
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
||||||
|
ImGuiTableFlags_ContextMenuInBody))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ImGui::TableSetupColumn("Name");
|
ImGui::TableSetupColumn("Name");
|
||||||
@@ -81,37 +74,37 @@ void Debugger::RegisterView() {
|
|||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
auto renderMemoryTable = [&](u64 vaddr) {
|
auto renderMemoryTable = [&](u64 vaddr) {
|
||||||
if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
|
if (!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!ImGui::BeginTooltip())
|
if (!ImGui::BeginTooltip())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str());
|
ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str());
|
||||||
if(!ImGui::BeginTable("##memoryContents", 16))
|
if (!ImGui::BeginTable("##memoryContents", 16))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(u32 col = 0; col < 16; col++)
|
for (u32 col = 0; col < 16; col++)
|
||||||
ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
|
ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
|
||||||
|
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
for(u32 row = 0; row < 16; row++) {
|
for (u32 row = 0; row < 16; row++) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
for(u32 col = 0; col < 16; col+=4) {
|
for (u32 col = 0; col < 16; col += 4) {
|
||||||
u32 paddr;
|
u32 paddr;
|
||||||
if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr))
|
if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const u32 val = n64::Core::GetMem().Read<u32>(paddr);
|
const u32 val = n64::Core::GetMem().Read<u32>(paddr);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(col+0);
|
ImGui::TableSetColumnIndex(col + 0);
|
||||||
ImGui::Text("%02X", (val >> 24) & 0xff);
|
ImGui::Text("%02X", (val >> 24) & 0xff);
|
||||||
ImGui::TableSetColumnIndex(col+1);
|
ImGui::TableSetColumnIndex(col + 1);
|
||||||
ImGui::Text("%02X", (val >> 16) & 0xff);
|
ImGui::Text("%02X", (val >> 16) & 0xff);
|
||||||
ImGui::TableSetColumnIndex(col+2);
|
ImGui::TableSetColumnIndex(col + 2);
|
||||||
ImGui::Text("%02X", (val >> 8) & 0xff);
|
ImGui::Text("%02X", (val >> 8) & 0xff);
|
||||||
ImGui::TableSetColumnIndex(col+3);
|
ImGui::TableSetColumnIndex(col + 3);
|
||||||
ImGui::Text("%02X", (val >> 0) & 0xff);
|
ImGui::Text("%02X", (val >> 0) & 0xff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,13 +113,13 @@ void Debugger::RegisterView() {
|
|||||||
ImGui::EndTooltip();
|
ImGui::EndTooltip();
|
||||||
};
|
};
|
||||||
|
|
||||||
n64::Registers& regs = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
|
|
||||||
for(int i = 0; i < 32; i+=2) {
|
for (int i = 0; i < 32; i += 2) {
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(0);
|
ImGui::TableSetColumnIndex(0);
|
||||||
ImGui::Text("%s", regNames[i]);
|
ImGui::Text("%s", n64::Registers::regNames[i]);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(1);
|
ImGui::TableSetColumnIndex(1);
|
||||||
auto value = regs.Read<u64>(i);
|
auto value = regs.Read<u64>(i);
|
||||||
@@ -134,10 +127,10 @@ void Debugger::RegisterView() {
|
|||||||
renderMemoryTable(value);
|
renderMemoryTable(value);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(2);
|
ImGui::TableSetColumnIndex(2);
|
||||||
ImGui::Text("%s", regNames[i+1]);
|
ImGui::Text("%s", n64::Registers::regNames[i + 1]);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(3);
|
ImGui::TableSetColumnIndex(3);
|
||||||
value = regs.Read<u64>(i+1);
|
value = regs.Read<u64>(i + 1);
|
||||||
ImGui::Text("%s", std::format("{:016X}", value).c_str());
|
ImGui::Text("%s", std::format("{:016X}", value).c_str());
|
||||||
renderMemoryTable(value);
|
renderMemoryTable(value);
|
||||||
}
|
}
|
||||||
@@ -148,41 +141,42 @@ void Debugger::RegisterView() {
|
|||||||
|
|
||||||
bool Debugger::render() {
|
bool Debugger::render() {
|
||||||
n64::Core &core = n64::Core::GetInstance();
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
const n64::Registers& regs = n64::Core::GetRegs();
|
const n64::Registers ®s = n64::Core::GetRegs();
|
||||||
|
|
||||||
if(!enabled)
|
if (!enabled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
static s64 startAddr = 0xFFFF'FFFF'8000'0000;
|
static s64 startAddr = 0xFFFF'FFFF'8000'0000;
|
||||||
constexpr int step = 4;
|
constexpr int step = 4;
|
||||||
constexpr int stepFast = 256;
|
constexpr int stepFast = 256;
|
||||||
|
|
||||||
if(!ImGui::Begin("Debugger", &enabled)) {
|
if (!ImGui::Begin("Debugger", &enabled)) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::BeginDisabled(followPC);
|
ImGui::BeginDisabled(followPC);
|
||||||
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal);
|
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX",
|
||||||
|
ImGuiInputTextFlags_CharsHexadecimal);
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::Text("Follow program counter:");
|
ImGui::Text("Follow program counter:");
|
||||||
ImGui::SameLine(0,0);
|
ImGui::SameLine(0, 0);
|
||||||
|
|
||||||
ImGui::Checkbox("##followPC", &followPC);
|
ImGui::Checkbox("##followPC", &followPC);
|
||||||
ImGui::SameLine(0,0);
|
ImGui::SameLine(0, 0);
|
||||||
|
|
||||||
ImGui::Text("Add a breakpoint");
|
ImGui::Text("Add a breakpoint");
|
||||||
ImGui::SameLine(0,0);
|
ImGui::SameLine(0, 0);
|
||||||
|
|
||||||
if(followPC)
|
if (followPC)
|
||||||
startAddr = regs.pc - 256; // TODO: arbitrary???
|
startAddr = regs.pc - 256; // TODO: arbitrary???
|
||||||
|
|
||||||
if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
|
if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
|
||||||
core.ToggleBreakpoint(startAddr);
|
core.ToggleBreakpoint(startAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ImGui::BeginTabBar("##debuggerTabs")) {
|
if (!ImGui::BeginTabBar("##debuggerTabs")) {
|
||||||
ImGui::EndTabBar();
|
ImGui::EndTabBar();
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return false;
|
return false;
|
||||||
@@ -190,38 +184,38 @@ bool Debugger::render() {
|
|||||||
|
|
||||||
RegisterView();
|
RegisterView();
|
||||||
|
|
||||||
if(!ImGui::BeginTabItem("MIPS R4300i code view")) {
|
if (!ImGui::BeginTabItem("MIPS R4300i code view")) {
|
||||||
ImGui::EndTabBar();
|
ImGui::EndTabBar();
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
|
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable |
|
||||||
ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
||||||
|
|
||||||
if(!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
||||||
ImGui::EndTabBar();
|
ImGui::EndTabBar();
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto &[name, _] : columns)
|
for (auto &[name, _] : columns)
|
||||||
ImGui::TableSetupColumn(name);
|
ImGui::TableSetupColumn(name);
|
||||||
|
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
for(auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
|
for (auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
|
||||||
auto disasm = Disassembler::GetInstance().Disassemble(addr);
|
auto disasm = Disassembler::GetInstance().Disassemble(addr);
|
||||||
const auto addrIsCurrent = addr == regs.nextPC;
|
const auto addrIsCurrent = addr == regs.nextPC;
|
||||||
const auto addrIsBreakpoint = core.breakpoints.contains(addr);
|
const auto addrIsBreakpoint = core.breakpoints.contains(addr);
|
||||||
ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg];
|
ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg];
|
||||||
ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt];
|
ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt];
|
||||||
if(addrIsCurrent) {
|
if (addrIsCurrent) {
|
||||||
colorChoice = 0x80e27fbc;
|
colorChoice = 0x80e27fbc;
|
||||||
colorChoiceAlt = 0x80e27fbc;
|
colorChoiceAlt = 0x80e27fbc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(addrIsBreakpoint) {
|
if (addrIsBreakpoint) {
|
||||||
colorChoice = 0x800000ff;
|
colorChoice = 0x800000ff;
|
||||||
colorChoiceAlt = 0x800000ff;
|
colorChoiceAlt = 0x800000ff;
|
||||||
}
|
}
|
||||||
@@ -230,7 +224,7 @@ bool Debugger::render() {
|
|||||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value);
|
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value);
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
for(int i = 0; auto &[_, func] : columns) {
|
for (int i = 0; auto &[_, func] : columns) {
|
||||||
ImGui::TableSetColumnIndex(i++);
|
ImGui::TableSetColumnIndex(i++);
|
||||||
func(addr, disasm);
|
func(addr, disasm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <backend/Core.hpp>
|
#include <backend/Core.hpp>
|
||||||
|
#include <Disassembler.hpp>
|
||||||
|
|
||||||
void BreakpointFunc(s64, Disassembler::DisassemblyResult&);
|
void BreakpointFunc(s64, Disassembler::DisassemblyResult &);
|
||||||
void AddressFunc(s64, Disassembler::DisassemblyResult&);
|
void AddressFunc(s64, Disassembler::DisassemblyResult &);
|
||||||
void InstructionFunc(s64, Disassembler::DisassemblyResult&);
|
void InstructionFunc(s64, Disassembler::DisassemblyResult &);
|
||||||
|
|
||||||
class Debugger final {
|
class Debugger final {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
static constexpr auto MAX_LINES_OF_DISASM = 150;
|
static constexpr auto MAX_LINES_OF_DISASM = 150;
|
||||||
|
|
||||||
struct Column {
|
struct Column {
|
||||||
const char* name = nullptr;
|
const char *name = nullptr;
|
||||||
void (*func)(s64, Disassembler::DisassemblyResult&) = nullptr;
|
void (*func)(s64, Disassembler::DisassemblyResult &) = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<Column, 3> columns = {
|
std::array<Column, 3> columns = {
|
||||||
@@ -19,10 +20,14 @@ class Debugger final {
|
|||||||
Column{"Address", &AddressFunc},
|
Column{"Address", &AddressFunc},
|
||||||
Column{"Instruction", &InstructionFunc},
|
Column{"Instruction", &InstructionFunc},
|
||||||
};
|
};
|
||||||
public:
|
|
||||||
|
public:
|
||||||
static void RegisterView();
|
static void RegisterView();
|
||||||
bool followPC = true;
|
bool followPC = true;
|
||||||
void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; }
|
void Open(bool wantFollowPC = true) {
|
||||||
|
enabled = true;
|
||||||
|
followPC = wantFollowPC;
|
||||||
|
}
|
||||||
void Close() { enabled = false; }
|
void Close() { enabled = false; }
|
||||||
bool render();
|
bool render();
|
||||||
};
|
};
|
||||||
+117
-89
@@ -5,7 +5,9 @@
|
|||||||
#include <ImGuiImpl/StatusBar.hpp>
|
#include <ImGuiImpl/StatusBar.hpp>
|
||||||
#include <resources/gamecontrollerdb.h>
|
#include <resources/gamecontrollerdb.h>
|
||||||
|
|
||||||
KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) {
|
KaizenGui::KaizenGui() noexcept :
|
||||||
|
window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()),
|
||||||
|
emuThread(fpsCounter, settingsWindow) {
|
||||||
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
|
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
|
||||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||||
|
|
||||||
@@ -33,23 +35,29 @@ void KaizenGui::QueryDevices(const SDL_Event &event) {
|
|||||||
if (gamepad)
|
if (gamepad)
|
||||||
SDL_CloseGamepad(gamepad);
|
SDL_CloseGamepad(gamepad);
|
||||||
break;
|
break;
|
||||||
default: break;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::HandleInput(const SDL_Event &event) {
|
void KaizenGui::HandleInput(const SDL_Event &event) {
|
||||||
const n64::Core& core = n64::Core::GetInstance();
|
const n64::Core &core = n64::Core::GetInstance();
|
||||||
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
||||||
switch(event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||||
if(!gamepad)
|
if (!gamepad)
|
||||||
break;
|
break;
|
||||||
{
|
{
|
||||||
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
|
pif.UpdateButton(0, n64::Controller::Key::Z,
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
|
pif.UpdateButton(0, n64::Controller::Key::CUp,
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
|
pif.UpdateButton(0, n64::Controller::Key::CDown,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CLeft,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CRight,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
|
||||||
|
|
||||||
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
||||||
if (xclamped < 0) {
|
if (xclamped < 0) {
|
||||||
@@ -70,12 +78,12 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
|
|||||||
yclamped *= 86;
|
yclamped *= 86;
|
||||||
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
|
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||||
if(!gamepad)
|
if (!gamepad)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||||
@@ -92,29 +100,29 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
|
|||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
{
|
{
|
||||||
const auto keys = SDL_GetKeyboardState(nullptr);
|
const auto keys = SDL_GetKeyboardState(nullptr);
|
||||||
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
||||||
fileDialogOpen = true;
|
fileDialogOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastForward = keys[SDL_SCANCODE_SPACE];
|
fastForward = keys[SDL_SCANCODE_SPACE];
|
||||||
if(!unlockFramerate)
|
if (!unlockFramerate)
|
||||||
core.parallel.SetFramerateUnlocked(fastForward);
|
core.parallel.SetFramerateUnlocked(fastForward);
|
||||||
|
|
||||||
if(core.romLoaded) {
|
if (core.romLoaded) {
|
||||||
if(keys[SDL_SCANCODE_P]) {
|
if (keys[SDL_SCANCODE_P]) {
|
||||||
emuThread.TogglePause();
|
emuThread.TogglePause();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(keys[SDL_SCANCODE_R]) {
|
if (keys[SDL_SCANCODE_R]) {
|
||||||
emuThread.Reset();
|
emuThread.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(keys[SDL_SCANCODE_Q]) {
|
if (keys[SDL_SCANCODE_Q]) {
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gamepad)
|
if (gamepad)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
||||||
@@ -134,31 +142,36 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
|
|||||||
|
|
||||||
float x = 0, y = 0;
|
float x = 0, y = 0;
|
||||||
|
|
||||||
if (keys[SDL_SCANCODE_UP]) y = 86;
|
if (keys[SDL_SCANCODE_UP])
|
||||||
if (keys[SDL_SCANCODE_DOWN]) y = -86;
|
y = 86;
|
||||||
if (keys[SDL_SCANCODE_LEFT]) x = -86;
|
if (keys[SDL_SCANCODE_DOWN])
|
||||||
if (keys[SDL_SCANCODE_RIGHT]) x = 86;
|
y = -86;
|
||||||
|
if (keys[SDL_SCANCODE_LEFT])
|
||||||
|
x = -86;
|
||||||
|
if (keys[SDL_SCANCODE_RIGHT])
|
||||||
|
x = 86;
|
||||||
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
|
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
|
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: break;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
||||||
auto lastPC = Util::Error::GetLastPC();
|
auto lastPC = Util::Error::GetLastPC();
|
||||||
if(lastPC.has_value()) {
|
if (lastPC.has_value()) {
|
||||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||||
if(memoryAccess.has_value()) {
|
if (memoryAccess.has_value()) {
|
||||||
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||||
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading",
|
ImGui::Text("%s",
|
||||||
static_cast<u8>(size), address,
|
std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
|
||||||
is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||||
.c_str());
|
.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,53 +179,53 @@ std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderEr
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::RenderUI() {
|
void KaizenGui::RenderUI() {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
gui::StartFrame();
|
gui::StartFrame();
|
||||||
|
|
||||||
if(ImGui::BeginMainMenuBar()) {
|
if (ImGui::BeginMainMenuBar()) {
|
||||||
if(ImGui::BeginMenu("File")) {
|
if (ImGui::BeginMenu("File")) {
|
||||||
if(ImGui::MenuItem("Open", "Ctrl-O")) {
|
if (ImGui::MenuItem("Open", "Ctrl-O")) {
|
||||||
fileDialogOpen = true;
|
fileDialogOpen = true;
|
||||||
}
|
}
|
||||||
if(ImGui::MenuItem("Exit")) {
|
if (ImGui::MenuItem("Exit")) {
|
||||||
quit = true;
|
quit = true;
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if(ImGui::BeginMenu("Emulation")) {
|
if (ImGui::BeginMenu("Emulation")) {
|
||||||
ImGui::BeginDisabled(!core.romLoaded);
|
ImGui::BeginDisabled(!core.romLoaded);
|
||||||
|
|
||||||
if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
||||||
emuThread.TogglePause();
|
emuThread.TogglePause();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::MenuItem("Reset", "R")) {
|
if (ImGui::MenuItem("Reset", "R")) {
|
||||||
emuThread.Reset();
|
emuThread.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::MenuItem("Stop", "Q")) {
|
if (ImGui::MenuItem("Stop", "Q")) {
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
core.romLoaded = false;
|
core.romLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
||||||
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::MenuItem("Open Debugger")) {
|
if (ImGui::MenuItem("Open Debugger")) {
|
||||||
debugger.Open();
|
debugger.Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
if(ImGui::MenuItem("Options")) {
|
if (ImGui::MenuItem("Options")) {
|
||||||
settingsWindow.isOpen = true;
|
settingsWindow.isOpen = true;
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if(ImGui::BeginMenu("Help")) {
|
if (ImGui::BeginMenu("Help")) {
|
||||||
if(ImGui::MenuItem("About")) {
|
if (ImGui::MenuItem("About")) {
|
||||||
aboutOpen = true;
|
aboutOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,15 +234,15 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Util::Error::IsHandled()) {
|
if (!Util::Error::IsHandled()) {
|
||||||
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(settingsWindow.isOpen) {
|
if (settingsWindow.isOpen) {
|
||||||
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(aboutOpen) {
|
if (aboutOpen) {
|
||||||
ImGui::OpenPopup("About Kaizen");
|
ImGui::OpenPopup("About Kaizen");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +260,7 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if(ImGui::Button("OK")) {
|
if (ImGui::Button("OK")) {
|
||||||
aboutOpen = false;
|
aboutOpen = false;
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
@@ -259,8 +272,9 @@ void KaizenGui::RenderUI() {
|
|||||||
|
|
||||||
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
emuThread.TogglePause();
|
emuThread.TogglePause();
|
||||||
switch(Util::Error::GetSeverity().as_enum) {
|
switch (Util::Error::GetSeverity().as_enum) {
|
||||||
case Util::Error::Severity::WARN: {
|
case Util::Error::Severity::WARN:
|
||||||
|
{
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
||||||
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
||||||
@@ -269,33 +283,37 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
||||||
RenderErrorMessageDetails();
|
RenderErrorMessageDetails();
|
||||||
|
|
||||||
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
const bool ignore = ImGui::Button("Try continuing");
|
||||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
const bool stop = ImGui::Button("Stop emulation");
|
||||||
|
ImGui::SameLine();
|
||||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||||
if(ignore || stop || chooseAnother) {
|
if (ignore || stop || chooseAnother) {
|
||||||
Util::Error::SetHandled();
|
Util::Error::SetHandled();
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ignore) {
|
if (ignore) {
|
||||||
emuThread.TogglePause();
|
emuThread.TogglePause();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stop || chooseAnother) {
|
if (stop || chooseAnother) {
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(chooseAnother) {
|
if (chooseAnother) {
|
||||||
fileDialogOpen = true;
|
fileDialogOpen = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::Button("OK"))
|
if (ImGui::Button("OK"))
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
} break;
|
}
|
||||||
case Util::Error::Severity::UNRECOVERABLE: {
|
break;
|
||||||
|
case Util::Error::Severity::UNRECOVERABLE:
|
||||||
|
{
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||||
@@ -305,10 +323,12 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||||
RenderErrorMessageDetails();
|
RenderErrorMessageDetails();
|
||||||
if(ImGui::Button("OK"))
|
if (ImGui::Button("OK"))
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
} break;
|
}
|
||||||
case Util::Error::Severity::NON_FATAL: {
|
break;
|
||||||
|
case Util::Error::Severity::NON_FATAL:
|
||||||
|
{
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||||
ImGui::Text("An error has occurred!");
|
ImGui::Text("An error has occurred!");
|
||||||
@@ -318,48 +338,54 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
||||||
|
|
||||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
const bool ignore = ImGui::Button("Try continuing");
|
||||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
const bool stop = ImGui::Button("Stop emulation");
|
||||||
|
ImGui::SameLine();
|
||||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
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;
|
const bool openInDebugger =
|
||||||
if(ignore || stop || chooseAnother || openInDebugger) {
|
lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
||||||
|
if (ignore || stop || chooseAnother || openInDebugger) {
|
||||||
Util::Error::SetHandled();
|
Util::Error::SetHandled();
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ignore) {
|
if (ignore) {
|
||||||
emuThread.TogglePause();
|
emuThread.TogglePause();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stop || chooseAnother) {
|
if (stop || chooseAnother) {
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(chooseAnother) {
|
if (chooseAnother) {
|
||||||
fileDialogOpen = true;
|
fileDialogOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(openInDebugger) {
|
if (openInDebugger) {
|
||||||
if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
||||||
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
||||||
|
|
||||||
debugger.Open();
|
debugger.Open();
|
||||||
emuThread.Reset();
|
emuThread.Reset();
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
default: break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldDisplaySpinner) {
|
if (shouldDisplaySpinner) {
|
||||||
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f));
|
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
|
||||||
|
ImVec2(0.5f, 0.5f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
|
||||||
@@ -384,11 +410,14 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::RenderPlatformWindowsDefault();
|
ImGui::RenderPlatformWindowsDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fileDialogOpen) {
|
if (fileDialogOpen) {
|
||||||
fileDialogOpen = false;
|
fileDialogOpen = false;
|
||||||
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
|
||||||
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) {
|
{"Nintendo 64 executable", "n64;z64;v64"},
|
||||||
auto kaizen = static_cast<KaizenGui*>(userdata);
|
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
||||||
|
SDL_ShowOpenFileDialog(
|
||||||
|
[](void *userdata, const char *const *filelist, int) {
|
||||||
|
auto kaizen = static_cast<KaizenGui *>(userdata);
|
||||||
|
|
||||||
if (!filelist) {
|
if (!filelist) {
|
||||||
panic("An error occured: {}", SDL_GetError());
|
panic("An error occured: {}", SDL_GetError());
|
||||||
@@ -402,16 +431,16 @@ void KaizenGui::RenderUI() {
|
|||||||
|
|
||||||
kaizen->fileToLoad = *filelist;
|
kaizen->fileToLoad = *filelist;
|
||||||
kaizen->shouldDisplaySpinner = true;
|
kaizen->shouldDisplaySpinner = true;
|
||||||
|
|
||||||
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
||||||
fileWorker.detach();
|
fileWorker.detach();
|
||||||
}, this, window.getHandle(), filters, 3, nullptr, false);
|
},
|
||||||
|
this, window.getHandle(), filters, 3, nullptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(minimized)
|
if (minimized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(core.romLoaded) {
|
if (core.romLoaded) {
|
||||||
core.parallel.UpdateScreen<true>();
|
core.parallel.UpdateScreen<true>();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -420,18 +449,18 @@ void KaizenGui::RenderUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
core.LoadROM(path);
|
core.LoadROM(path);
|
||||||
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::run() {
|
void KaizenGui::run() {
|
||||||
while(!quit) {
|
while (!quit) {
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
while (SDL_PollEvent(&e)) {
|
while (SDL_PollEvent(&e)) {
|
||||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||||
switch(e.type) {
|
switch (e.type) {
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
quit = true;
|
quit = true;
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
@@ -443,6 +472,7 @@ void KaizenGui::run() {
|
|||||||
minimized = false;
|
minimized = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
QueryDevices(e);
|
QueryDevices(e);
|
||||||
HandleInput(e);
|
HandleInput(e);
|
||||||
@@ -455,6 +485,4 @@ void KaizenGui::run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::LoadTAS(const std::string &path) noexcept {
|
void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
|
||||||
n64::Core::GetInstance().LoadTAS(fs::path(path));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -37,12 +37,13 @@ private:
|
|||||||
void HandleInput(const SDL_Event &event);
|
void HandleInput(const SDL_Event &event);
|
||||||
void QueryDevices(const SDL_Event &event);
|
void QueryDevices(const SDL_Event &event);
|
||||||
|
|
||||||
[[noreturn]] void FileWorker() {
|
void FileWorker() {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!fileToLoad.empty()) {
|
if (!fileToLoad.empty()) {
|
||||||
LoadROM(fileToLoad);
|
LoadROM(fileToLoad);
|
||||||
shouldDisplaySpinner = false;
|
shouldDisplaySpinner = false;
|
||||||
fileToLoad = "";
|
fileToLoad = "";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
#include <Options.hpp>
|
#include <Options.hpp>
|
||||||
#include <log.hpp>
|
#include <log.hpp>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
#include <Core.hpp>
|
||||||
|
|
||||||
CPUSettings::CPUSettings() {
|
CPUSettings::CPUSettings() {
|
||||||
if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") {
|
auto selectedCpuType = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||||
|
if (selectedCpuType == "jit") {
|
||||||
|
selectedCpuTypeIndex = 2;
|
||||||
|
} else if (selectedCpuType == "cached_interpreter") {
|
||||||
selectedCpuTypeIndex = 1;
|
selectedCpuTypeIndex = 1;
|
||||||
} else {
|
} else {
|
||||||
selectedCpuTypeIndex = 0;
|
selectedCpuTypeIndex = 0;
|
||||||
@@ -12,12 +16,13 @@ CPUSettings::CPUSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPUSettings::render() {
|
void CPUSettings::render() {
|
||||||
const char* items[] = {
|
const char *items[] = {"Interpreter", "Cached Interpreter",
|
||||||
"Interpreter",
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
"Dynamic Recompiler"
|
"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)) {
|
||||||
for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
|
for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
|
||||||
const bool is_selected = (selectedCpuTypeIndex == n);
|
const bool is_selected = (selectedCpuTypeIndex == n);
|
||||||
@@ -33,11 +38,16 @@ void CPUSettings::render() {
|
|||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
} else if (selectedCpuTypeIndex == 1) {
|
||||||
|
Options::GetInstance().SetValue<std::string>("cpu", "type", "cached_interpreter");
|
||||||
|
n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter;
|
||||||
} else {
|
} else {
|
||||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
||||||
|
n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-32
@@ -3,7 +3,9 @@
|
|||||||
#include <common.hpp>
|
#include <common.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
|
|
||||||
struct Instruction {
|
struct Instruction {
|
||||||
|
Instruction() = default;
|
||||||
Instruction(u32 v) { instr.raw = v; }
|
Instruction(u32 v) { instr.raw = v; }
|
||||||
void operator=(u32 v) { instr.raw = v; }
|
void operator=(u32 v) { instr.raw = v; }
|
||||||
operator u32() const { return instr.raw; }
|
operator u32() const { return instr.raw; }
|
||||||
@@ -21,7 +23,9 @@ struct Instruction {
|
|||||||
inline u8 vd() const { return fd(); }
|
inline u8 vd() const { return fd(); }
|
||||||
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
||||||
inline u8 e2() const { return rs() & 0x0f; }
|
inline u8 e2() const { return rs() & 0x0f; }
|
||||||
|
inline u8 op() const { return rt(); }
|
||||||
inline u16 imm() const { return instr.itype.imm; }
|
inline u16 imm() const { return instr.itype.imm; }
|
||||||
|
inline u16 offset() const { return imm(); }
|
||||||
inline u32 target() const { return instr.jtype.target; }
|
inline u32 target() const { return instr.jtype.target; }
|
||||||
inline u8 opcode() const { return instr.opcode.op; }
|
inline u8 opcode() const { return instr.opcode.op; }
|
||||||
inline u8 special() const { return instr.opcode.special; }
|
inline u8 special() const { return instr.opcode.special; }
|
||||||
@@ -32,61 +36,61 @@ struct Instruction {
|
|||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
unsigned imm:16;
|
unsigned imm : 16;
|
||||||
unsigned rt:5;
|
unsigned rt : 5;
|
||||||
unsigned rs:5;
|
unsigned rs : 5;
|
||||||
unsigned op:6;
|
unsigned op : 6;
|
||||||
} itype;
|
} itype;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned target:26;
|
unsigned target : 26;
|
||||||
unsigned op:6;
|
unsigned op : 6;
|
||||||
} jtype;
|
} jtype;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned funct:6;
|
unsigned funct : 6;
|
||||||
unsigned sa:5;
|
unsigned sa : 5;
|
||||||
unsigned rd:5;
|
unsigned rd : 5;
|
||||||
unsigned rt:5;
|
unsigned rt : 5;
|
||||||
unsigned rs:5;
|
unsigned rs : 5;
|
||||||
unsigned op:6;
|
unsigned op : 6;
|
||||||
} rtype;
|
} rtype;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
unsigned special_lo:3;
|
unsigned special_lo : 3;
|
||||||
unsigned special_hi:3;
|
unsigned special_hi : 3;
|
||||||
unsigned:26;
|
unsigned : 26;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned special:6;
|
unsigned special : 6;
|
||||||
unsigned:26;
|
unsigned : 26;
|
||||||
};
|
};
|
||||||
struct {
|
struct {
|
||||||
unsigned:16;
|
unsigned : 16;
|
||||||
unsigned regimm_lo:3;
|
unsigned regimm_lo : 3;
|
||||||
unsigned regimm_hi:2;
|
unsigned regimm_hi : 2;
|
||||||
unsigned:11;
|
unsigned : 11;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned:16;
|
unsigned : 16;
|
||||||
unsigned regimm:5;
|
unsigned regimm : 5;
|
||||||
unsigned:11;
|
unsigned : 11;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned:26;
|
unsigned : 26;
|
||||||
unsigned op:6;
|
unsigned op : 6;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned funct:6;
|
unsigned funct : 6;
|
||||||
unsigned:10;
|
unsigned : 10;
|
||||||
unsigned cop_rt:5;
|
unsigned cop_rt : 5;
|
||||||
unsigned cop_rs:5;
|
unsigned cop_rs : 5;
|
||||||
unsigned:6;
|
unsigned : 6;
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 raw;
|
u32 raw;
|
||||||
@@ -221,4 +225,4 @@ struct Instruction {
|
|||||||
static constexpr u8 BLTZALL = 0b10010;
|
static constexpr u8 BLTZALL = 0b10010;
|
||||||
static constexpr u8 BGEZALL = 0b10011;
|
static constexpr u8 BGEZALL = 0b10011;
|
||||||
};
|
};
|
||||||
}
|
} // namespace n64
|
||||||
|
|||||||
Reference in New Issue
Block a user