Compare commits
20 Commits
b89ff3d212
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1de4a8f9d | ||
|
|
8818aa163e | ||
|
|
701e5e0a85 | ||
|
|
6367524dce | ||
|
|
4b3974967f | ||
|
|
6d5389c7ab | ||
|
|
b7ca7706b7 | ||
|
|
f5bd176c6c | ||
|
|
553c6cc91a | ||
|
|
945c5f40ac | ||
|
|
817616ca88 | ||
|
|
b6426f2c8a | ||
|
|
219c46e475 | ||
|
|
b4a17416e4 | ||
|
|
c2d6d5a8a9 | ||
|
|
70edba4ced | ||
|
|
db2f37ad6d | ||
|
|
ebcafd5b86 | ||
|
|
ebeb5f2cb5 | ||
|
|
f870d8f979 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,3 +25,4 @@ compile_commands.json
|
|||||||
*.diagsession
|
*.diagsession
|
||||||
tests/
|
tests/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
resources/version.hpp
|
||||||
@@ -13,6 +13,7 @@ if(APPLE)
|
|||||||
enable_language(OBJC)
|
enable_language(OBJC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(VULKAN_VALIDATION FALSE)
|
||||||
set(SANITIZERS FALSE)
|
set(SANITIZERS FALSE)
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@@ -23,6 +24,33 @@ if(${CMAKE_BUILD_TYPE} MATCHES Release OR ${CMAKE_BUILD_TYPE} MATCHES RelWithDeb
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_program(GIT_EXECUTABLE git)
|
||||||
|
|
||||||
|
if (GIT_EXECUTABLE)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE _git_hash
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT "${KAIZEN_GIT_COMMIT_HASH}" STREQUAL "${_git_hash}")
|
||||||
|
file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
|
||||||
|
set(KAIZEN_GIT_COMMIT_HASH "${_git_hash}" CACHE STRING "" FORCE)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# Otherwise, ensure KAIZEN_GIT_COMMIT_HASH is defined
|
||||||
|
if (DEFINED KAIZEN_GIT_COMMIT_HASH)
|
||||||
|
set(_git_hash "${KAIZEN_GIT_COMMIT_HASH}" CACHE STRING "" FORCE)
|
||||||
|
file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
|
||||||
|
message("Git not found, but git commit hash was defined as ${KAIZEN_GIT_COMMIT_HASH}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Git not found, please define KAIZEN_GIT_COMMIT_HASH manually.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/version.hpp.in ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
src/frontend/Settings
|
src/frontend/Settings
|
||||||
src/frontend
|
src/frontend
|
||||||
@@ -62,6 +90,11 @@ option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF)
|
|||||||
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." OFF)
|
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." OFF)
|
||||||
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." OFF)
|
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." OFF)
|
||||||
option(BUILD_SHARED_LIBS OFF)
|
option(BUILD_SHARED_LIBS OFF)
|
||||||
|
set(BUILD_FOR_OFFICIAL_RELEASE OFF)
|
||||||
|
|
||||||
|
if(NOT ${BUILD_FOR_OFFICIAL_RELEASE})
|
||||||
|
add_compile_definitions(KAIZEN_USE_HASH)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(CheckCCompilerFlag)
|
include(CheckCCompilerFlag)
|
||||||
|
|
||||||
@@ -81,19 +114,17 @@ endif()
|
|||||||
|
|
||||||
set(SIMD_FLAG NULL)
|
set(SIMD_FLAG NULL)
|
||||||
|
|
||||||
if(ARM64 AND APPLE)
|
if(ARM64)
|
||||||
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)
|
||||||
elseif(NOT ARM64)
|
else()
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
set(SIMD_FLAG /arch:AVX)
|
set(SIMD_FLAG /arch:AVX)
|
||||||
else()
|
else()
|
||||||
set(SIMD_FLAG -msse4.1)
|
set(SIMD_FLAG -msse4.1)
|
||||||
endif()
|
endif()
|
||||||
check_c_compiler_flag(${SIMD_FLAG} HAS_SIMD)
|
check_c_compiler_flag(${SIMD_FLAG} HAS_SIMD)
|
||||||
else()
|
|
||||||
message("Compiling on an unsupported Arm device! Good Luck!")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (HAS_SIMD)
|
if (HAS_SIMD)
|
||||||
@@ -102,7 +133,8 @@ if (HAS_SIMD)
|
|||||||
add_compile_options(${SIMD_FLAG})
|
add_compile_options(${SIMD_FLAG})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (${CMAKE_BUILD_TYPE} MATCHES Debug)
|
if (${CMAKE_BUILD_TYPE} MATCHES Debug AND VULKAN_VALIDATION)
|
||||||
|
message("VALIDATION LAYERS: ON")
|
||||||
add_compile_definitions(VULKAN_DEBUG)
|
add_compile_definitions(VULKAN_DEBUG)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@@ -152,6 +184,7 @@ target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::c
|
|||||||
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
||||||
|
|
||||||
if (SANITIZERS)
|
if (SANITIZERS)
|
||||||
|
message("UBSAN AND ASAN: ON")
|
||||||
target_compile_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
target_compile_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
||||||
target_link_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
target_link_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Kaizen
|
# Kaizen
|
||||||
|
|
||||||
Rewrite of my Nintendo 64 emulator "[shibumi](https://github.com/SimoZ64/shibumi)".
|
Experimental N64 emulator
|
||||||
|
|
||||||
## Sister projects
|
## Sister projects
|
||||||
- [**n64**](https://github.com/dillonb/n64): Low-level, fast, accurate and easy to use Nintendo 64 emulator
|
- [**n64**](https://github.com/dillonb/n64): Low-level, fast, accurate and easy to use Nintendo 64 emulator
|
||||||
|
|||||||
2
cmake/version.hpp.in
Normal file
2
cmake/version.hpp.in
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
#define KAIZEN_GIT_COMMIT_HASH "${_git_hash}"
|
||||||
55
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
55
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
@@ -6,14 +6,13 @@
|
|||||||
#include <resources/frag.spv.h>
|
#include <resources/frag.spv.h>
|
||||||
#include <KaizenGui.hpp>
|
#include <KaizenGui.hpp>
|
||||||
#include <imgui_impl_vulkan.h>
|
#include <imgui_impl_vulkan.h>
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
using namespace Vulkan;
|
using namespace Vulkan;
|
||||||
using namespace RDP;
|
using namespace RDP;
|
||||||
|
|
||||||
bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; }
|
bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; }
|
||||||
|
|
||||||
void ParallelRDP::SetFramerateUnlocked(bool unlocked) const {
|
void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const {
|
||||||
if (unlocked) {
|
if (unlocked) {
|
||||||
wsi->set_present_mode(PresentMode::UnlockedForceTearing);
|
wsi->set_present_mode(PresentMode::UnlockedForceTearing);
|
||||||
} else {
|
} else {
|
||||||
@@ -24,11 +23,11 @@ void ParallelRDP::SetFramerateUnlocked(bool unlocked) const {
|
|||||||
Program *fullscreen_quad_program;
|
Program *fullscreen_quad_program;
|
||||||
|
|
||||||
// Copied and modified from WSI::init_context_from_platform
|
// Copied and modified from WSI::init_context_from_platform
|
||||||
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, unsigned num_thread_indices,
|
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices,
|
||||||
const Context::SystemHandles &system_handles) {
|
const Context::SystemHandles &system_handles) {
|
||||||
VK_ASSERT(platform);
|
VK_ASSERT(platform);
|
||||||
auto instance_ext = platform->get_instance_extensions();
|
const auto instance_ext = platform->get_instance_extensions();
|
||||||
auto device_ext = platform->get_device_extensions();
|
const auto device_ext = platform->get_device_extensions();
|
||||||
auto new_context = Util::make_handle<Context>();
|
auto new_context = Util::make_handle<Context>();
|
||||||
|
|
||||||
new_context->set_application_info(platform->get_application_info());
|
new_context->set_application_info(platform->get_application_info());
|
||||||
@@ -39,9 +38,9 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, unsigned nu
|
|||||||
panic("Failed to create Vulkan instance.\n");
|
panic("Failed to create Vulkan instance.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceKHR tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE);
|
const auto tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE);
|
||||||
|
|
||||||
bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
|
const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
|
||||||
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
|
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
|
||||||
|
|
||||||
if (tmp_surface) {
|
if (tmp_surface) {
|
||||||
@@ -61,9 +60,9 @@ void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platfo
|
|||||||
wsi->set_backbuffer_srgb(false);
|
wsi->set_backbuffer_srgb(false);
|
||||||
wsi->set_platform(wsi_platform.get());
|
wsi->set_platform(wsi_platform.get());
|
||||||
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
||||||
Context::SystemHandles handles;
|
|
||||||
|
|
||||||
if (!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
|
if (constexpr Context::SystemHandles handles;
|
||||||
|
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
|
||||||
panic("Failed to initialize WSI: init_from_existing_context() failed");
|
panic("Failed to initialize WSI: init_from_existing_context() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,8 +96,8 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
|
|||||||
fragLayout.sets[0].fp_mask = 1;
|
fragLayout.sets[0].fp_mask = 1;
|
||||||
fragLayout.sets[0].array_size[0] = 1;
|
fragLayout.sets[0].array_size[0] = 1;
|
||||||
|
|
||||||
auto sizeVert = sizeof(vertex_shader);
|
constexpr auto sizeVert = sizeof(vertex_shader);
|
||||||
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 *>(fragment_shader),
|
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
|
||||||
@@ -108,8 +107,8 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
|
|||||||
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) {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +127,7 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
|
|||||||
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
|
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
|
||||||
cmd->set_program(fullscreen_quad_program);
|
cmd->set_program(fullscreen_quad_program);
|
||||||
cmd->set_quad_state();
|
cmd->set_quad_state();
|
||||||
auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
|
const auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
|
||||||
data[0] = -1.0f;
|
data[0] = -1.0f;
|
||||||
data[1] = -3.0f;
|
data[1] = -3.0f;
|
||||||
data[2] = -1.0f;
|
data[2] = -1.0f;
|
||||||
@@ -136,15 +135,15 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
|
|||||||
data[4] = +3.0f;
|
data[4] = +3.0f;
|
||||||
data[5] = +1.0f;
|
data[5] = +1.0f;
|
||||||
|
|
||||||
auto windowSize = windowInfo->get_window_size();
|
const auto [x, y] = windowInfo->get_window_size();
|
||||||
|
|
||||||
float zoom = std::min(windowSize.x / wsi->get_platform().get_surface_width(),
|
const float zoom = std::min(x / static_cast<float>(wsi->get_platform().get_surface_width()),
|
||||||
windowSize.y / wsi->get_platform().get_surface_height());
|
y / static_cast<float>(wsi->get_platform().get_surface_height()));
|
||||||
|
|
||||||
float width = (wsi->get_platform().get_surface_width() / windowSize.x) * zoom;
|
const float width = static_cast<float>(wsi->get_platform().get_surface_width()) / x * zoom;
|
||||||
float height = (wsi->get_platform().get_surface_height() / windowSize.y) * zoom;
|
const float height = static_cast<float>(wsi->get_platform().get_surface_height()) / y * zoom;
|
||||||
|
|
||||||
float uniform_data[] = {// Size
|
const float uniform_data[] = {// Size
|
||||||
width, height,
|
width, height,
|
||||||
// Offset
|
// Offset
|
||||||
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
|
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
|
||||||
@@ -190,10 +189,9 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
|
|||||||
wsi->end_frame();
|
wsi->end_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParallelRDP::UpdateScreen(bool playing) const {
|
template <>
|
||||||
if(playing) {
|
void ParallelRDP::UpdateScreen<true>() const {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
const n64::VI& vi = n64::Core::GetMem().mmio.vi;
|
||||||
n64::VI& vi = 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);
|
||||||
@@ -217,16 +215,17 @@ void ParallelRDP::UpdateScreen(bool playing) const {
|
|||||||
opts.vi.gamma_dither = true;
|
opts.vi.gamma_dither = true;
|
||||||
opts.downscale_steps = true;
|
opts.downscale_steps = true;
|
||||||
opts.crop_overscan_pixels = true;
|
opts.crop_overscan_pixels = true;
|
||||||
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
const Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
||||||
UpdateScreen(image);
|
UpdateScreen(image);
|
||||||
command_processor->begin_frame_context();
|
command_processor->begin_frame_context();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void ParallelRDP::UpdateScreen<false>() const {
|
||||||
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParallelRDP::EnqueueCommand(int command_length, const u32 *buffer) const {
|
void ParallelRDP::EnqueueCommand(const int command_length, const u32 *buffer) const {
|
||||||
command_processor->enqueue_command(command_length, buffer);
|
command_processor->enqueue_command(command_length, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
5
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
@@ -18,10 +18,11 @@ public:
|
|||||||
void Init(const std::shared_ptr<Vulkan::WSIPlatform> &,
|
void Init(const std::shared_ptr<Vulkan::WSIPlatform> &,
|
||||||
const std::shared_ptr<WindowInfo> &, const u8 *);
|
const std::shared_ptr<WindowInfo> &, const u8 *);
|
||||||
|
|
||||||
void UpdateScreen(bool = true) const;
|
template <bool>
|
||||||
|
void UpdateScreen() const;
|
||||||
void EnqueueCommand(int, const u32 *) const;
|
void EnqueueCommand(int, const u32 *) const;
|
||||||
void OnFullSync() const;
|
void OnFullSync() const;
|
||||||
bool IsFramerateUnlocked() const;
|
[[nodiscard]] bool IsFramerateUnlocked() const;
|
||||||
void SetFramerateUnlocked(bool) const;
|
void SetFramerateUnlocked(bool) const;
|
||||||
|
|
||||||
std::shared_ptr<Vulkan::WSI> wsi;
|
std::shared_ptr<Vulkan::WSI> wsi;
|
||||||
|
|||||||
@@ -5,14 +5,14 @@
|
|||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
Core::Core() {
|
Core::Core() {
|
||||||
auto cpuType = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||||
if (cpuType == "interpreter") {
|
if (selectedCpu == "interpreter") {
|
||||||
cpuType = Interpreted;
|
cpuType = Interpreted;
|
||||||
cpu = std::make_unique<Interpreter>(parallel, *mem, regs);
|
cpu = std::make_unique<Interpreter>(*mem, regs);
|
||||||
} else if(cpuType == "jit") {
|
} else if(selectedCpu == "jit") {
|
||||||
#ifndef __aarch64__
|
#ifndef __aarch64__
|
||||||
cpuType = DynamicRecompiler;
|
cpuType = DynamicRecompiler;
|
||||||
cpu = std::make_unique<JIT>(parallel, *mem, regs);
|
cpu = std::make_unique<JIT>(*mem, regs);
|
||||||
#else
|
#else
|
||||||
panic("JIT currently unsupported on aarch64");
|
panic("JIT currently unsupported on aarch64");
|
||||||
#endif
|
#endif
|
||||||
@@ -62,11 +62,11 @@ void Core::LoadROM(const std::string &rom_) {
|
|||||||
romLoaded = true;
|
romLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Core::StepCPU() {
|
u32 Core::StepCPU() {
|
||||||
return cpu->Step() + regs.PopStalledCycles();
|
return cpu->Step() + regs.PopStalledCycles();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::StepRSP(int cpuCycles) {
|
void Core::StepRSP(const u32 cpuCycles) {
|
||||||
MMIO &mmio = mem->mmio;
|
MMIO &mmio = mem->mmio;
|
||||||
|
|
||||||
if (mmio.rsp.spStatus.halt) {
|
if (mmio.rsp.spStatus.halt) {
|
||||||
@@ -75,10 +75,10 @@ void Core::StepRSP(int cpuCycles) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int cpuRatio = 3, rspRatio = 2;
|
static constexpr u32 cpuRatio = 3, rspRatio = 2;
|
||||||
|
|
||||||
regs.steps += cpuCycles;
|
regs.steps += cpuCycles;
|
||||||
int sets = regs.steps / cpuRatio;
|
const auto sets = regs.steps / cpuRatio;
|
||||||
mmio.rsp.steps += sets * rspRatio;
|
mmio.rsp.steps += sets * rspRatio;
|
||||||
regs.steps -= sets * cpuRatio;
|
regs.steps -= sets * cpuRatio;
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ void Core::StepRSP(int cpuCycles) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Run(float volumeL, float volumeR) {
|
void Core::Run(const float volumeL, const float volumeR) {
|
||||||
MMIO &mmio = mem->mmio;
|
MMIO &mmio = mem->mmio;
|
||||||
|
|
||||||
bool broken = false;
|
bool broken = false;
|
||||||
@@ -102,7 +102,7 @@ void Core::Run(float volumeL, float volumeR) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while(cycles < mem->mmio.vi.cyclesPerHalfline) {
|
while(cycles < mem->mmio.vi.cyclesPerHalfline) {
|
||||||
u32 taken = StepCPU();
|
const u32 taken = StepCPU();
|
||||||
cycles += taken;
|
cycles += taken;
|
||||||
|
|
||||||
if((broken = breakpoints.contains(regs.nextPC)))
|
if((broken = breakpoints.contains(regs.nextPC)))
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ struct Core {
|
|||||||
return *GetInstance().mem;
|
return *GetInstance().mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
int StepCPU();
|
u32 StepCPU();
|
||||||
void StepRSP(int cpuCycles);
|
void StepRSP(u32 cpuCycles);
|
||||||
void Stop();
|
void Stop();
|
||||||
void Reset();
|
void Reset();
|
||||||
void LoadROM(const std::string &);
|
void LoadROM(const std::string &);
|
||||||
@@ -46,17 +46,17 @@ struct Core {
|
|||||||
breakpoints.insert(addr);
|
breakpoints.insert(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<s64> breakpoints{};
|
|
||||||
|
|
||||||
bool pause = true;
|
bool pause = true;
|
||||||
u32 cycles = 0;
|
|
||||||
bool romLoaded = false;
|
bool romLoaded = false;
|
||||||
|
int slot = 0;
|
||||||
|
u32 cycles = 0;
|
||||||
|
size_t memSize{}, cpuSize{}, verSize{};
|
||||||
std::string rom;
|
std::string rom;
|
||||||
Registers regs;
|
std::set<s64> breakpoints{};
|
||||||
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
|
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
|
||||||
std::unique_ptr<BaseCPU> cpu;
|
std::unique_ptr<BaseCPU> cpu;
|
||||||
size_t memSize{}, cpuSize{}, verSize{};
|
|
||||||
int slot = 0;
|
Registers regs;
|
||||||
ParallelRDP parallel;
|
ParallelRDP parallel;
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ struct Scheduler {
|
|||||||
[[nodiscard]] u64 Remove(EventType) const;
|
[[nodiscard]] u64 Remove(EventType) const;
|
||||||
void Tick(u64 t);
|
void Tick(u64 t);
|
||||||
|
|
||||||
IterableEvents events{};
|
|
||||||
u64 ticks = 0;
|
|
||||||
u8 index = 0;
|
u8 index = 0;
|
||||||
|
u64 ticks = 0;
|
||||||
|
IterableEvents events{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct BaseCPU {
|
struct BaseCPU {
|
||||||
virtual ~BaseCPU() = default;
|
virtual ~BaseCPU() = default;
|
||||||
virtual int Step() = 0;
|
virtual u32 Step() = 0;
|
||||||
virtual void Reset() = 0;
|
virtual void Reset() = 0;
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
Interpreter::Interpreter(ParallelRDP& parallel, Mem& mem, Registers& regs) : mem(mem), regs(regs) {}
|
Interpreter::Interpreter(Mem& mem, Registers& regs) : 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 +12,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() {
|
void Interpreter::CheckCompareInterrupt() 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,7 +21,7 @@ void Interpreter::CheckCompareInterrupt() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Interpreter::Step() {
|
u32 Interpreter::Step() {
|
||||||
CheckCompareInterrupt();
|
CheckCompareInterrupt();
|
||||||
|
|
||||||
regs.prevDelaySlot = regs.delaySlot;
|
regs.prevDelaySlot = regs.delaySlot;
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core;
|
struct Core;
|
||||||
|
|
||||||
struct Interpreter : BaseCPU {
|
struct Interpreter final : BaseCPU {
|
||||||
explicit Interpreter(ParallelRDP&, Mem&, Registers&);
|
explicit Interpreter(Mem&, Registers&);
|
||||||
~Interpreter() override = default;
|
~Interpreter() override = default;
|
||||||
int Step() override;
|
u32 Step() override;
|
||||||
|
|
||||||
void Reset() override {
|
void Reset() override {
|
||||||
cop2Latch = {};
|
cop2Latch = {};
|
||||||
@@ -22,8 +22,8 @@ private:
|
|||||||
friend struct Cop1;
|
friend struct Cop1;
|
||||||
#define check_address_error(mask, vaddr) \
|
#define check_address_error(mask, vaddr) \
|
||||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||||
bool ShouldServiceInterrupt() const;
|
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||||
void CheckCompareInterrupt();
|
void CheckCompareInterrupt() const;
|
||||||
|
|
||||||
void cop2Decode(Instruction);
|
void cop2Decode(Instruction);
|
||||||
void special(Instruction);
|
void special(Instruction);
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
#ifndef __aarch64__
|
#ifndef __aarch64__
|
||||||
JIT::JIT(ParallelRDP& parallel, Mem& mem, Registers& regs) : mem(mem), regs(regs) {
|
JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) {
|
||||||
regs.SetJIT(this);
|
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) {
|
||||||
@@ -25,7 +26,7 @@ bool JIT::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 JIT::CheckCompareInterrupt() {
|
void JIT::CheckCompareInterrupt() 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) {
|
||||||
@@ -39,20 +40,20 @@ void JIT::InvalidateBlock(const u32 paddr) {
|
|||||||
blockCache[index] = {};
|
blockCache[index] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 JIT::FetchInstruction() {
|
std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
||||||
u32 paddr = 0;
|
u32 paddr = 0;
|
||||||
|
|
||||||
if (check_address_error(0b11, u64(blockPC))) [[unlikely]] {
|
if (check_address_error(0b11, vaddr)) [[unlikely]] {
|
||||||
/*regs.cop0.HandleTLBException(blockPC);
|
/*regs.cop0.HandleTLBException(blockPC);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
|
regs.cop0.FireException(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}, blockPC, {},
|
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
||||||
return 0;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) {
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
||||||
/*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;*/
|
||||||
@@ -60,48 +61,54 @@ u32 JIT::FetchInstruction() {
|
|||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, 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 std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Core::GetMem().Read<u32>(paddr);
|
const u32 instr = Core::GetMem().Read<u32>(paddr);
|
||||||
|
|
||||||
|
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
|
||||||
|
|
||||||
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::SetPC32(s32 val) {
|
void JIT::SetPC32(const s32 val) {
|
||||||
code.mov(code.SCR1, REG(qword, pc));
|
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||||
code.mov(REG(qword, oldPC), code.SCR1);
|
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||||
code.mov(code.SCR1, s64(val));
|
code.mov(code.SCR1.cvt32(), val);
|
||||||
code.mov(REG(qword, pc), code.SCR1);
|
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
||||||
code.mov(code.SCR1, s64(val) + 4);
|
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||||
code.mov(REG(qword, nextPC), code.SCR1);
|
code.mov(code.SCR1.cvt32(), val + 4);
|
||||||
|
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
||||||
|
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::SetPC64(s64 val) {
|
void JIT::SetPC64(const s64 val) {
|
||||||
code.mov(code.SCR1, REG(qword, pc));
|
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||||
code.mov(REG(qword, oldPC), code.SCR1);
|
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||||
code.mov(code.SCR1, val);
|
code.mov(code.SCR1, val);
|
||||||
code.mov(REG(qword, pc), code.SCR1);
|
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||||
code.mov(code.SCR1, val + 4);
|
code.mov(code.SCR1, val + 4);
|
||||||
code.mov(REG(qword, nextPC), 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, REG(qword, pc));
|
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||||
code.mov(REG(qword, oldPC), code.SCR1);
|
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||||
code.movsxd(val.cvt64(), val);
|
code.movsxd(val.cvt64(), val);
|
||||||
code.mov(REG(qword, pc), val);
|
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||||
code.add(val, 4);
|
code.add(val, 4);
|
||||||
code.mov(REG(qword, nextPC), 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, REG(qword, pc));
|
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||||
code.mov(REG(qword, oldPC), code.SCR1);
|
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||||
code.mov(REG(qword, pc), val);
|
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||||
code.add(val, 4);
|
code.add(val, 4);
|
||||||
code.mov(REG(qword, nextPC), val);
|
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
||||||
}
|
}
|
||||||
|
|
||||||
int JIT::Step() {
|
u32 JIT::Step() {
|
||||||
blockOldPC = regs.oldPC;
|
blockOldPC = regs.oldPC;
|
||||||
blockPC = regs.pc;
|
blockPC = regs.pc;
|
||||||
blockNextPC = regs.nextPC;
|
blockNextPC = regs.nextPC;
|
||||||
@@ -111,15 +118,15 @@ int 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::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 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 upperIndex = paddr >> kUpperShift;
|
const u32 upperIndex = paddr >> kUpperShift;
|
||||||
u32 lowerIndex = paddr & kLowerMask;
|
const u32 lowerIndex = paddr & kLowerMask;
|
||||||
|
|
||||||
if (!blockCache[upperIndex].empty()) {
|
if (!blockCache[upperIndex].empty()) {
|
||||||
if (blockCache[upperIndex][lowerIndex]) {
|
if (blockCache[upperIndex][lowerIndex]) {
|
||||||
@@ -130,7 +137,7 @@ int JIT::Step() {
|
|||||||
blockCache[upperIndex].resize(kLowerSize);
|
blockCache[upperIndex].resize(kLowerSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace("[JIT]: Compiling block @ 0x{:016X}:", blockPC);
|
info("[JIT]: Compiling block @ 0x{:016X}:", static_cast<u64>(blockPC));
|
||||||
const auto blockInfo = code.getCurr();
|
const auto blockInfo = code.getCurr();
|
||||||
const auto block = code.getCurr<BlockFn>();
|
const auto block = code.getCurr<BlockFn>();
|
||||||
blockCache[upperIndex][lowerIndex] = block;
|
blockCache[upperIndex][lowerIndex] = block;
|
||||||
@@ -138,7 +145,6 @@ int JIT::Step() {
|
|||||||
code.setProtectModeRW();
|
code.setProtectModeRW();
|
||||||
|
|
||||||
u32 instructionsInBlock = 0;
|
u32 instructionsInBlock = 0;
|
||||||
u32 instruction = 0;
|
|
||||||
|
|
||||||
bool instrEndsBlock = false;
|
bool instrEndsBlock = false;
|
||||||
|
|
||||||
@@ -146,78 +152,70 @@ int JIT::Step() {
|
|||||||
code.push(code.rbp);
|
code.push(code.rbp);
|
||||||
code.mov(code.rbp, reinterpret_cast<uintptr_t>(this)); // Load context pointer
|
code.mov(code.rbp, reinterpret_cast<uintptr_t>(this)); // Load context pointer
|
||||||
|
|
||||||
//cs_insn *insn;
|
cs_insn *insn;
|
||||||
trace("\tMIPS code (guest PC = 0x{:016X}):", blockPC);
|
info("\tMIPS code (guest PC = 0x{:016X}):", static_cast<u64>(blockPC));
|
||||||
while (!instrEndsBlock) {
|
|
||||||
// CheckCompareInterrupt();
|
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
paddr = 0;
|
paddr = 0;
|
||||||
|
|
||||||
if (check_address_error(0b11, u64(blockPC))) [[unlikely]] {
|
auto instruction = FetchInstruction(blockPC);
|
||||||
/*regs.cop0.HandleTLBException(blockPC);
|
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
|
|
||||||
return 1;*/
|
|
||||||
Util::Error::GetInstance().Throw(
|
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
|
||||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) {
|
if(!instruction)
|
||||||
/*regs.cop0.HandleTLBException(blockPC);
|
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
|
||||||
return 1;*/
|
|
||||||
Util::Error::GetInstance().Throw(
|
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
|
||||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
|
||||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
instruction = Core::GetMem().Read<u32>(paddr);
|
|
||||||
instructionsInBlock++;
|
instructionsInBlock++;
|
||||||
|
|
||||||
blockOldPC = blockPC;
|
blockOldPC = blockPC;
|
||||||
blockPC = blockNextPC;
|
blockPC = blockNextPC;
|
||||||
blockNextPC += 4;
|
blockNextPC += 4;
|
||||||
|
|
||||||
if((instrEndsBlock = InstrEndsBlock(instruction)))
|
if(InstrEndsBlock(instruction.value())) {
|
||||||
continue;
|
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
||||||
|
if(!delay_instruction)
|
||||||
|
return 0;
|
||||||
|
|
||||||
trace("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instruction).full);
|
if(InstrEndsBlock(delay_instruction.value())) {
|
||||||
|
|
||||||
/*if(ShouldServiceInterrupt()) {
|
|
||||||
regs.cop0.FireException(ExceptionCode::Interrupt, 0, blockPC);
|
|
||||||
return 1;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
Emit(instruction);
|
|
||||||
}
|
|
||||||
|
|
||||||
instructionsInBlock++;
|
|
||||||
const u32 delay_instruction = FetchInstruction();
|
|
||||||
instrEndsBlock = InstrEndsBlock(delay_instruction);
|
|
||||||
if(instrEndsBlock) {
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw(
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instructionsInBlock++;
|
||||||
|
|
||||||
blockOldPC = blockPC;
|
blockOldPC = blockPC;
|
||||||
blockPC = blockNextPC;
|
blockPC = blockNextPC;
|
||||||
blockNextPC += 4;
|
blockNextPC += 4;
|
||||||
|
|
||||||
Emit(delay_instruction);
|
Emit(delay_instruction.value());
|
||||||
|
Emit(instruction.value());
|
||||||
|
|
||||||
Emit(instruction);
|
if(!branch_taken) {
|
||||||
|
Xbyak::Label runtime_branch_taken;
|
||||||
if(!regs.delaySlot) {
|
code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]);
|
||||||
|
code.cmp(code.SCR1, 0);
|
||||||
|
code.jne(runtime_branch_taken);
|
||||||
code.mov(code.SCR1, blockOldPC);
|
code.mov(code.SCR1, blockOldPC);
|
||||||
code.mov(REG(qword, oldPC), code.SCR1);
|
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||||
code.mov(code.SCR1, blockPC);
|
code.mov(code.SCR1, blockPC);
|
||||||
code.mov(REG(qword, pc), code.SCR1);
|
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||||
code.mov(code.SCR1, blockNextPC);
|
code.mov(code.SCR1, blockNextPC);
|
||||||
code.mov(REG(qword, nextPC), code.SCR1);
|
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||||
|
code.L(runtime_branch_taken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(branch_taken) branch_taken = false;
|
||||||
|
|
||||||
|
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Emit(instruction.value());
|
||||||
|
|
||||||
|
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
code.mov(code.rax, instructionsInBlock);
|
code.mov(code.rax, instructionsInBlock);
|
||||||
@@ -225,22 +223,24 @@ int JIT::Step() {
|
|||||||
code.add(code.rsp, 8);
|
code.add(code.rsp, 8);
|
||||||
code.ret();
|
code.ret();
|
||||||
code.setProtectModeRE();
|
code.setProtectModeRE();
|
||||||
//static auto blockInfoSize = 0;
|
static size_t blockInfoSize = 0;
|
||||||
//blockInfoSize = code.getSize() - blockInfoSize;
|
blockInfoSize = code.getSize() - blockInfoSize;
|
||||||
|
|
||||||
//trace("\tX86 code (block address = 0x{:016X}):", (uintptr_t)block);
|
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
|
||||||
//auto count = cs_disasm(disassemblerX86, blockInfo, blockInfoSize, (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++) {
|
||||||
// trace("\t\t0x{:016X}:\t{}\t\t{}\n", 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);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// cs_free(insn, count);
|
cs_free(insn, count);
|
||||||
//}
|
}
|
||||||
// const auto dump = code.getCode();
|
|
||||||
// Util::WriteFileBinary(dump, code.getSize(), "jit.dump");
|
|
||||||
// panic("");
|
// panic("");
|
||||||
return block();
|
return block();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JIT::DumpBlockCacheToDisk() const {
|
||||||
|
Util::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -16,15 +16,22 @@ static constexpr u32 kUpperSize = kAddressSpaceSize >> kUpperShift; // 0x800000
|
|||||||
static constexpr u32 kLowerSize = 0x100; // 0x80
|
static constexpr u32 kLowerSize = 0x100; // 0x80
|
||||||
static constexpr u32 kCodeCacheSize = 32_mb;
|
static constexpr u32 kCodeCacheSize = 32_mb;
|
||||||
static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
|
static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
|
||||||
#define REG(acc, x) code.acc[code.rbp + (reinterpret_cast<uintptr_t>(®s.x) - (uintptr_t)this)]
|
|
||||||
|
#define OLD_PC_OFFSET (reinterpret_cast<uintptr_t>(®s.oldPC) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
#define PC_OFFSET (reinterpret_cast<uintptr_t>(®s.pc) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
#define NEXT_PC_OFFSET (reinterpret_cast<uintptr_t>(®s.nextPC) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
#define GPR_OFFSET(x) (reinterpret_cast<uintptr_t>(®s.gpr[(x)]) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
#define BRANCH_TAKEN_OFFSET (reinterpret_cast<uintptr_t>(&branch_taken) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
#define HI_OFFSET (reinterpret_cast<uintptr_t>(®s.hi) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
#define LO_OFFSET (reinterpret_cast<uintptr_t>(®s.lo) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
|
||||||
#ifdef __aarch64__
|
#ifdef __aarch64__
|
||||||
struct JIT : BaseCPU {};
|
struct JIT : BaseCPU {};
|
||||||
#else
|
#else
|
||||||
struct JIT : BaseCPU {
|
struct JIT final : BaseCPU {
|
||||||
explicit JIT(ParallelRDP&, Mem&, Registers&);
|
explicit JIT(Mem&, Registers&);
|
||||||
~JIT() override = default;
|
~JIT() override = default;
|
||||||
int Step() override;
|
u32 Step() override;
|
||||||
|
|
||||||
void Reset() override {
|
void Reset() override {
|
||||||
code.reset();
|
code.reset();
|
||||||
@@ -32,30 +39,38 @@ struct JIT : BaseCPU {
|
|||||||
blockCache.resize(kUpperSize);
|
blockCache.resize(kUpperSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DumpBlockCacheToDisk() const;
|
||||||
|
|
||||||
|
void AdvanceDelaySlot() {
|
||||||
|
regs.prevDelaySlot = regs.delaySlot;
|
||||||
|
regs.delaySlot = false;
|
||||||
|
}
|
||||||
|
|
||||||
void InvalidateBlock(u32);
|
void InvalidateBlock(u32);
|
||||||
private:
|
private:
|
||||||
Registers& regs;
|
|
||||||
Mem& mem;
|
|
||||||
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
|
|
||||||
u64 cop2Latch{};
|
|
||||||
u64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
|
|
||||||
friend struct Cop1;
|
friend struct Cop1;
|
||||||
friend struct Registers;
|
friend struct Registers;
|
||||||
using BlockFn = int (*)();
|
using BlockFn = int (*)();
|
||||||
|
|
||||||
|
bool branch_taken;
|
||||||
|
Registers& regs;
|
||||||
|
Mem& mem;
|
||||||
|
u64 cop2Latch{};
|
||||||
|
s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
|
||||||
|
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
|
||||||
|
csh disassemblerMips{}, disassemblerX86{};
|
||||||
std::vector<std::vector<BlockFn>> blockCache;
|
std::vector<std::vector<BlockFn>> blockCache;
|
||||||
Xbyak::Label branch_likely_not_taken;
|
|
||||||
csh disassemblerMips, disassemblerX86;
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Xbyak::Address GPR(const size_t index) const {
|
Xbyak::Address GPR(const size_t index) {
|
||||||
if constexpr (sizeof(T) == 1) {
|
if constexpr (sizeof(T) == 1) {
|
||||||
return code.byte[code.rbp + (reinterpret_cast<uintptr_t>(®s.gpr[index]) - reinterpret_cast<uintptr_t>(this))];
|
return code.byte[code.rbp + GPR_OFFSET(index)];
|
||||||
} else if constexpr (sizeof(T) == 2) {
|
} else if constexpr (sizeof(T) == 2) {
|
||||||
return code.word[code.rbp + (reinterpret_cast<uintptr_t>(®s.gpr[index]) - reinterpret_cast<uintptr_t>(this))];
|
return code.word[code.rbp + GPR_OFFSET(index)];
|
||||||
} else if constexpr (sizeof(T) == 4) {
|
} else if constexpr (sizeof(T) == 4) {
|
||||||
return code.dword[code.rbp + (reinterpret_cast<uintptr_t>(®s.gpr[index]) - reinterpret_cast<uintptr_t>(this))];
|
return code.dword[code.rbp + GPR_OFFSET(index)];
|
||||||
} else if constexpr (sizeof(T) == 8) {
|
} else if constexpr (sizeof(T) == 8) {
|
||||||
return code.qword[code.rbp + (reinterpret_cast<uintptr_t>(®s.gpr[index]) - reinterpret_cast<uintptr_t>(this))];
|
return code.qword[code.rbp + GPR_OFFSET(index)];
|
||||||
}
|
}
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw(
|
||||||
@@ -106,89 +121,89 @@ private:
|
|||||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||||
|
|
||||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||||
void CheckCompareInterrupt();
|
void CheckCompareInterrupt() const;
|
||||||
u32 FetchInstruction();
|
std::optional<u32> FetchInstruction(s64);
|
||||||
|
|
||||||
void Emit(const Instruction);
|
void Emit(Instruction);
|
||||||
void special(const Instruction);
|
void special(Instruction);
|
||||||
void regimm(const Instruction);
|
void regimm(Instruction);
|
||||||
void add(const Instruction);
|
void add(Instruction);
|
||||||
void addu(const Instruction);
|
void addu(Instruction);
|
||||||
void addi(const Instruction);
|
void addi(Instruction);
|
||||||
void addiu(const Instruction);
|
void addiu(Instruction);
|
||||||
void andi(const Instruction);
|
void andi(Instruction);
|
||||||
void and_(const Instruction);
|
void and_(Instruction);
|
||||||
void branch_constant(bool cond, s64 offset);
|
void branch_constant(bool cond, s64 offset);
|
||||||
void branch_likely_constant(bool cond, s64 offset);
|
void branch_likely_constant(bool cond, s64 offset);
|
||||||
void branch_abs_constant(bool cond, s64 address);
|
void branch_abs_constant(bool cond, s64 address);
|
||||||
void bltz(const Instruction);
|
void bltz(Instruction);
|
||||||
void bgez(const Instruction);
|
void bgez(Instruction);
|
||||||
void bltzl(const Instruction);
|
void bltzl(Instruction);
|
||||||
void bgezl(const Instruction);
|
void bgezl(Instruction);
|
||||||
void bltzal(const Instruction);
|
void bltzal(Instruction);
|
||||||
void bgezal(const Instruction);
|
void bgezal(Instruction);
|
||||||
void bltzall(const Instruction);
|
void bltzall(Instruction);
|
||||||
void bgezall(const Instruction);
|
void bgezall(Instruction);
|
||||||
void beq(const Instruction);
|
void beq(Instruction);
|
||||||
void beql(const Instruction);
|
void beql(Instruction);
|
||||||
void bne(const Instruction);
|
void bne(Instruction);
|
||||||
void bnel(const Instruction);
|
void bnel(Instruction);
|
||||||
void blez(const Instruction);
|
void blez(Instruction);
|
||||||
void blezl(const Instruction);
|
void blezl(Instruction);
|
||||||
void bgtz(const Instruction);
|
void bgtz(Instruction);
|
||||||
void bgtzl(const Instruction);
|
void bgtzl(Instruction);
|
||||||
void bfc1(const Instruction);
|
void bfc1(Instruction);
|
||||||
void blfc1(const Instruction);
|
void blfc1(Instruction);
|
||||||
void bfc0(const Instruction);
|
void bfc0(Instruction);
|
||||||
void blfc0(const Instruction);
|
void blfc0(Instruction);
|
||||||
void dadd(const Instruction);
|
void dadd(Instruction);
|
||||||
void daddu(const Instruction);
|
void daddu(Instruction);
|
||||||
void daddi(const Instruction);
|
void daddi(Instruction);
|
||||||
void daddiu(const Instruction);
|
void daddiu(Instruction);
|
||||||
void ddiv(const Instruction);
|
void ddiv(Instruction);
|
||||||
void ddivu(const Instruction);
|
void ddivu(Instruction);
|
||||||
void div(const Instruction);
|
void div(Instruction);
|
||||||
void divu(const Instruction);
|
void divu(Instruction);
|
||||||
void dmult(const Instruction);
|
void dmult(Instruction);
|
||||||
void dmultu(const Instruction);
|
void dmultu(Instruction);
|
||||||
void dsll(const Instruction);
|
void dsll(Instruction);
|
||||||
void dsllv(const Instruction);
|
void dsllv(Instruction);
|
||||||
void dsll32(const Instruction);
|
void dsll32(Instruction);
|
||||||
void dsra(const Instruction);
|
void dsra(Instruction);
|
||||||
void dsrav(const Instruction);
|
void dsrav(Instruction);
|
||||||
void dsra32(const Instruction);
|
void dsra32(Instruction);
|
||||||
void dsrl(const Instruction);
|
void dsrl(Instruction);
|
||||||
void dsrlv(const Instruction);
|
void dsrlv(Instruction);
|
||||||
void dsrl32(const Instruction);
|
void dsrl32(Instruction);
|
||||||
void dsub(const Instruction);
|
void dsub(Instruction);
|
||||||
void dsubu(const Instruction);
|
void dsubu(Instruction);
|
||||||
void j(const Instruction);
|
void j(Instruction);
|
||||||
void jr(const Instruction);
|
void jr(Instruction);
|
||||||
void jal(const Instruction);
|
void jal(Instruction);
|
||||||
void jalr(const Instruction);
|
void jalr(Instruction);
|
||||||
void lui(const Instruction);
|
void lui(Instruction);
|
||||||
void lbu(const Instruction);
|
void lbu(Instruction);
|
||||||
void lb(const Instruction);
|
void lb(Instruction);
|
||||||
void ld(const Instruction);
|
void ld(Instruction);
|
||||||
void ldc1(const Instruction);
|
void ldc1(Instruction);
|
||||||
void ldl(const Instruction);
|
void ldl(Instruction);
|
||||||
void ldr(const Instruction);
|
void ldr(Instruction);
|
||||||
void lh(const Instruction);
|
void lh(Instruction);
|
||||||
void lhu(const Instruction);
|
void lhu(Instruction);
|
||||||
void ll(const Instruction);
|
void ll(Instruction);
|
||||||
void lld(const Instruction);
|
void lld(Instruction);
|
||||||
void lw(const Instruction);
|
void lw(Instruction);
|
||||||
void lwc1(const Instruction);
|
void lwc1(Instruction);
|
||||||
void lwl(const Instruction);
|
void lwl(Instruction);
|
||||||
void lwu(const Instruction);
|
void lwu(Instruction);
|
||||||
void lwr(const Instruction);
|
void lwr(Instruction);
|
||||||
void mfhi(const Instruction);
|
void mfhi(Instruction);
|
||||||
void mflo(const Instruction);
|
void mflo(Instruction);
|
||||||
void mult(const Instruction);
|
void mult(Instruction);
|
||||||
void multu(const Instruction);
|
void multu(Instruction);
|
||||||
void mthi(const Instruction);
|
void mthi(Instruction);
|
||||||
void mtlo(const Instruction);
|
void mtlo(Instruction);
|
||||||
void nor(const 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},
|
||||||
@@ -229,7 +244,7 @@ private:
|
|||||||
{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(const 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},
|
||||||
@@ -240,32 +255,32 @@ private:
|
|||||||
{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(const Instruction);
|
void slti(Instruction);
|
||||||
void sltiu(const Instruction);
|
void sltiu(Instruction);
|
||||||
void slt(const Instruction);
|
void slt(Instruction);
|
||||||
void sltu(const Instruction);
|
void sltu(Instruction);
|
||||||
void sll(const Instruction);
|
void sll(Instruction);
|
||||||
void sllv(const Instruction);
|
void sllv(Instruction);
|
||||||
void sub(const Instruction);
|
void sub(Instruction);
|
||||||
void subu(const 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::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
||||||
}
|
}
|
||||||
void sra(const Instruction);
|
void sra(Instruction);
|
||||||
void srav(const Instruction);
|
void srav(Instruction);
|
||||||
void srl(const Instruction);
|
void srl(Instruction);
|
||||||
void srlv(const 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::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
||||||
}
|
}
|
||||||
void or_(const Instruction);
|
void or_(Instruction);
|
||||||
void ori(const Instruction);
|
void ori(Instruction);
|
||||||
void xor_(const Instruction);
|
void xor_(Instruction);
|
||||||
void xori(const Instruction);
|
void xori(Instruction);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <Options.hpp>
|
#include <Options.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
Mem::Mem(JIT *jit) : flash(saveData), jit(jit) {
|
Mem::Mem() : flash(saveData) {
|
||||||
rom.cart.resize(CART_SIZE);
|
rom.cart.resize(CART_SIZE);
|
||||||
std::ranges::fill(rom.cart, 0);
|
std::ranges::fill(rom.cart, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,28 +12,28 @@
|
|||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct ROMHeader {
|
struct ROMHeader {
|
||||||
u8 initialValues[4];
|
u8 initialValues[4];
|
||||||
|
char imageName[20];
|
||||||
|
char countryCode[2];
|
||||||
|
u16 cartridgeId;
|
||||||
u32 clockRate;
|
u32 clockRate;
|
||||||
u32 programCounter;
|
u32 programCounter;
|
||||||
u32 release;
|
u32 release;
|
||||||
u32 crc1;
|
u32 crc1;
|
||||||
u32 crc2;
|
u32 crc2;
|
||||||
u64 unknown;
|
|
||||||
char imageName[20];
|
|
||||||
u32 unknown2;
|
u32 unknown2;
|
||||||
u32 manufacturerId;
|
u32 manufacturerId;
|
||||||
u16 cartridgeId;
|
u64 unknown;
|
||||||
char countryCode[2];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ROM {
|
struct ROM {
|
||||||
std::vector<u8> cart;
|
|
||||||
size_t mask;
|
|
||||||
ROMHeader header;
|
|
||||||
CICType cicType;
|
|
||||||
char gameNameCart[20];
|
|
||||||
std::string gameNameDB;
|
|
||||||
char code[4];
|
|
||||||
bool pal;
|
bool pal;
|
||||||
|
char gameNameCart[20];
|
||||||
|
char code[4];
|
||||||
|
ROMHeader header;
|
||||||
|
size_t mask;
|
||||||
|
CICType cicType;
|
||||||
|
std::vector<u8> cart;
|
||||||
|
std::string gameNameDB;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class FlashState : u8 { Idle, Erase, Write, Read, Status };
|
enum class FlashState : u8 { Idle, Erase, Write, Read, Status };
|
||||||
@@ -43,11 +43,11 @@ struct Flash {
|
|||||||
~Flash() = default;
|
~Flash() = default;
|
||||||
void Reset();
|
void Reset();
|
||||||
void Load(SaveType, const std::string &);
|
void Load(SaveType, const std::string &);
|
||||||
|
std::array<u8, 128> writeBuf{};
|
||||||
FlashState state{};
|
FlashState state{};
|
||||||
u64 status{};
|
u64 status{};
|
||||||
size_t eraseOffs{};
|
size_t eraseOffs{};
|
||||||
size_t writeOffs{};
|
size_t writeOffs{};
|
||||||
std::array<u8, 128> writeBuf{};
|
|
||||||
std::string flashPath{};
|
std::string flashPath{};
|
||||||
mio::mmap_sink &saveData;
|
mio::mmap_sink &saveData;
|
||||||
|
|
||||||
@@ -78,10 +78,11 @@ struct JIT;
|
|||||||
|
|
||||||
struct Mem {
|
struct Mem {
|
||||||
~Mem() = default;
|
~Mem() = default;
|
||||||
Mem(JIT * = nullptr);
|
Mem();
|
||||||
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; }
|
||||||
@@ -97,8 +98,6 @@ struct Mem {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
void BackupWrite(u32, T);
|
void BackupWrite(u32, T);
|
||||||
|
|
||||||
MMIO mmio;
|
|
||||||
|
|
||||||
FORCE_INLINE void DumpRDRAM() const {
|
FORCE_INLINE void DumpRDRAM() const {
|
||||||
std::vector<u8> temp{};
|
std::vector<u8> temp{};
|
||||||
temp.resize(RDRAM_SIZE);
|
temp.resize(RDRAM_SIZE);
|
||||||
@@ -120,11 +119,19 @@ struct Mem {
|
|||||||
Util::SwapBuffer<u32>(temp);
|
Util::SwapBuffer<u32>(temp);
|
||||||
Util::WriteFileBinary(temp, "dmem.bin");
|
Util::WriteFileBinary(temp, "dmem.bin");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MMIO mmio;
|
||||||
ROM rom;
|
ROM rom;
|
||||||
SaveType saveType = SAVE_NONE;
|
SaveType saveType = SAVE_NONE;
|
||||||
Flash flash;
|
Flash flash;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend struct SI;
|
||||||
|
friend struct PI;
|
||||||
|
friend struct AI;
|
||||||
|
friend struct RSP;
|
||||||
|
friend struct JIT;
|
||||||
|
friend struct Core;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void WriteInterpreter(u32, u32);
|
void WriteInterpreter(u32, u32);
|
||||||
void WriteInterpreter(u32, u64);
|
void WriteInterpreter(u32, u64);
|
||||||
@@ -132,17 +139,11 @@ private:
|
|||||||
void WriteJIT(u32, u32);
|
void WriteJIT(u32, u32);
|
||||||
void WriteJIT(u32, u64);
|
void WriteJIT(u32, u64);
|
||||||
|
|
||||||
JIT *jit = nullptr;
|
|
||||||
friend struct SI;
|
|
||||||
friend struct PI;
|
|
||||||
friend struct AI;
|
|
||||||
friend struct RSP;
|
|
||||||
friend struct JIT;
|
|
||||||
friend struct Core;
|
|
||||||
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
||||||
|
int mmioSize{}, flashSize{};
|
||||||
|
JIT *jit = nullptr;
|
||||||
std::string sramPath{};
|
std::string sramPath{};
|
||||||
mio::mmap_sink saveData{};
|
mio::mmap_sink saveData{};
|
||||||
int mmioSize{}, flashSize{};
|
|
||||||
|
|
||||||
[[nodiscard]] FORCE_INLINE bool IsROMPAL() const {
|
[[nodiscard]] FORCE_INLINE bool IsROMPAL() const {
|
||||||
static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'};
|
static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'};
|
||||||
|
|||||||
@@ -50,9 +50,6 @@ struct DPC {
|
|||||||
struct RDP {
|
struct RDP {
|
||||||
static constexpr auto COMMAND_BUFFER_SIZE = 0xFFFFF;
|
static constexpr auto COMMAND_BUFFER_SIZE = 0xFFFFF;
|
||||||
|
|
||||||
DPC dpc{};
|
|
||||||
std::array<u32, COMMAND_BUFFER_SIZE> cmd_buf{};
|
|
||||||
|
|
||||||
RDP();
|
RDP();
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
@@ -87,5 +84,9 @@ private:
|
|||||||
friend struct Mem;
|
friend struct Mem;
|
||||||
friend struct MMIO;
|
friend struct MMIO;
|
||||||
std::vector<u8> rdram{};
|
std::vector<u8> rdram{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
DPC dpc{};
|
||||||
|
std::array<u32, COMMAND_BUFFER_SIZE> cmd_buf{};
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -119,6 +119,32 @@ struct Registers;
|
|||||||
#define DE(x) (((x) >> 11) & 0x1F)
|
#define DE(x) (((x) >> 11) & 0x1F)
|
||||||
|
|
||||||
struct RSP {
|
struct RSP {
|
||||||
|
bool divInLoaded = false;
|
||||||
|
bool semaphore = false;
|
||||||
|
std::array<u8, DMEM_SIZE> dmem{};
|
||||||
|
std::array<u8, IMEM_SIZE> imem{};
|
||||||
|
u16 oldPC{}, pc{}, nextPC{};
|
||||||
|
s16 divIn{}, divOut{};
|
||||||
|
u32 steps = 0;
|
||||||
|
SPStatus spStatus{};
|
||||||
|
SPDMASPAddr spDMASPAddr{};
|
||||||
|
SPDMADRAMAddr spDMADRAMAddr{};
|
||||||
|
SPDMASPAddr lastSuccessfulSPAddr{};
|
||||||
|
SPDMADRAMAddr lastSuccessfulDRAMAddr{};
|
||||||
|
SPDMALen spDMALen{};
|
||||||
|
s32 gpr[32]{};
|
||||||
|
VPR vpr[32]{};
|
||||||
|
VPR vte{};
|
||||||
|
VPR vce{};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
VPR h{}, m{}, l{};
|
||||||
|
} acc;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
VPR l{}, h{};
|
||||||
|
} vcc, vco;
|
||||||
|
|
||||||
RSP();
|
RSP();
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
@@ -135,33 +161,7 @@ struct RSP {
|
|||||||
void SetVTE(const VPR &vt, u8 e);
|
void SetVTE(const VPR &vt, u8 e);
|
||||||
auto Read(u32 addr) -> u32;
|
auto Read(u32 addr) -> u32;
|
||||||
void Write(u32 addr, u32 val);
|
void Write(u32 addr, u32 val);
|
||||||
void Exec(const Instruction instr);
|
void Exec(Instruction instr);
|
||||||
SPStatus spStatus{};
|
|
||||||
u16 oldPC{}, pc{}, nextPC{};
|
|
||||||
SPDMASPAddr spDMASPAddr{};
|
|
||||||
SPDMADRAMAddr spDMADRAMAddr{};
|
|
||||||
SPDMASPAddr lastSuccessfulSPAddr{};
|
|
||||||
SPDMADRAMAddr lastSuccessfulDRAMAddr{};
|
|
||||||
SPDMALen spDMALen{};
|
|
||||||
std::array<u8, DMEM_SIZE> dmem{};
|
|
||||||
std::array<u8, IMEM_SIZE> imem{};
|
|
||||||
VPR vpr[32]{};
|
|
||||||
VPR vte{};
|
|
||||||
s32 gpr[32]{};
|
|
||||||
VPR vce{};
|
|
||||||
s16 divIn{}, divOut{};
|
|
||||||
bool divInLoaded = false;
|
|
||||||
int steps = 0;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
VPR h{}, m{}, l{};
|
|
||||||
} acc;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
VPR l{}, h{};
|
|
||||||
} vcc, vco;
|
|
||||||
|
|
||||||
bool semaphore = false;
|
|
||||||
|
|
||||||
FORCE_INLINE void SetPC(const u16 val) {
|
FORCE_INLINE void SetPC(const u16 val) {
|
||||||
oldPC = pc & 0xFFC;
|
oldPC = pc & 0xFFC;
|
||||||
@@ -257,12 +257,12 @@ struct RSP {
|
|||||||
|
|
||||||
FORCE_INLINE void ReleaseSemaphore() { semaphore = false; }
|
FORCE_INLINE void ReleaseSemaphore() { semaphore = false; }
|
||||||
|
|
||||||
void special(const Instruction instr);
|
void special(Instruction instr);
|
||||||
void regimm(const Instruction instr);
|
void regimm(Instruction instr);
|
||||||
void lwc2(const Instruction instr);
|
void lwc2(Instruction instr);
|
||||||
void swc2(const Instruction instr);
|
void swc2(Instruction instr);
|
||||||
void cop2(const Instruction instr);
|
void cop2(Instruction instr);
|
||||||
void cop0(const Instruction instr);
|
void cop0(Instruction instr);
|
||||||
|
|
||||||
void add(Instruction instr);
|
void add(Instruction instr);
|
||||||
void addi(Instruction instr);
|
void addi(Instruction instr);
|
||||||
|
|||||||
@@ -270,7 +270,54 @@ void JIT::Emit(const Instruction instr) {
|
|||||||
lui(instr);
|
lui(instr);
|
||||||
break;
|
break;
|
||||||
case Instruction::COP0:
|
case Instruction::COP0:
|
||||||
panic("[JIT]: Unimplemented Cop0 decode");
|
switch (instr.cop_rs()) {
|
||||||
|
case 0x00:
|
||||||
|
code.mov(code.ARG2, instr);
|
||||||
|
emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
code.mov(code.ARG2, instr);
|
||||||
|
emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x04:
|
||||||
|
code.mov(code.ARG2, instr);
|
||||||
|
emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
code.mov(code.ARG2, instr);
|
||||||
|
emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x10 ... 0x1F:
|
||||||
|
switch (instr.cop_funct()) {
|
||||||
|
case 0x01:
|
||||||
|
emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
code.mov(code.ARG2, COP0_REG_INDEX);
|
||||||
|
emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0);
|
||||||
|
code.mov(code.ARG2, code.rax);
|
||||||
|
code.and_(code.ARG2, 0x3F);
|
||||||
|
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x06:
|
||||||
|
emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0);
|
||||||
|
code.mov(code.ARG2, code.rax);
|
||||||
|
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0);
|
||||||
|
break;
|
||||||
|
case 0x18:
|
||||||
|
emitMemberFunctionCall(&Cop0::eret, ®s.cop0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr),
|
||||||
|
regs.oldPC);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
panic("Unimplemented COP0 instruction {}", instr.cop_rs());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Instruction::COP1:
|
case Instruction::COP1:
|
||||||
{
|
{
|
||||||
@@ -411,6 +458,7 @@ void JIT::Emit(const Instruction instr) {
|
|||||||
sd(instr);
|
sd(instr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ void JIT::bfc0(const Instruction instr) {
|
|||||||
const s16 imm = instr;
|
const s16 imm = instr;
|
||||||
const s64 offset = u64((s64)imm) << 2;
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
const s64 address = blockPC + offset;
|
const s64 address = blockPC + offset;
|
||||||
// code.mov(code.al, REG(byte, cop1.fcr31.compare));
|
// code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
|
||||||
// code.test(code.al, code.al);
|
// code.test(code.al, code.al);
|
||||||
// branch(address, z);
|
// branch(address, z);
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ void JIT::blfc0(const Instruction instr) {
|
|||||||
const s16 imm = instr;
|
const s16 imm = instr;
|
||||||
const s64 offset = u64((s64)imm) << 2;
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
const s64 address = blockPC + offset;
|
const s64 address = blockPC + offset;
|
||||||
// code.mov(code.al, REG(byte, cop1.fcr31.compare));
|
// code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
|
||||||
// code.test(code.al, code.al);
|
// code.test(code.al, code.al);
|
||||||
// branch_likely(address, z);
|
// branch_likely(address, z);
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ void JIT::bfc1(const Instruction instr) {
|
|||||||
const s16 imm = instr;
|
const s16 imm = instr;
|
||||||
const s64 offset = u64((s64)imm) << 2;
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
const s64 address = blockPC + offset;
|
const s64 address = blockPC + offset;
|
||||||
// code.mov(code.al, REG(byte, cop1.fcr31.compare));
|
// code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
|
||||||
// code.test(code.al, code.al);
|
// code.test(code.al, code.al);
|
||||||
// branch(address, nz);
|
// branch(address, nz);
|
||||||
}
|
}
|
||||||
@@ -186,87 +186,95 @@ void JIT::blfc1(const Instruction instr) {
|
|||||||
const s16 imm = instr;
|
const s16 imm = instr;
|
||||||
const s64 offset = u64((s64)imm) << 2;
|
const s64 offset = u64((s64)imm) << 2;
|
||||||
const s64 address = blockPC + offset;
|
const s64 address = blockPC + offset;
|
||||||
// code.mov(code.al, REG(byte, cop1.fcr31.compare));
|
// code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
|
||||||
// code.test(code.al, code.al);
|
// code.test(code.al, code.al);
|
||||||
// branch_likely(address, nz);
|
// branch_likely(address, nz);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::BranchNotTaken() {}
|
void JIT::BranchNotTaken() {}
|
||||||
void JIT::BranchTaken(s64 offs) {
|
|
||||||
code.mov(code.SCR1, REG(qword, pc));
|
void JIT::BranchTaken(const s64 offs) {
|
||||||
code.add(code.SCR1, offs);
|
code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]);
|
||||||
SetPC64(offs);
|
code.add(code.SCR2, offs);
|
||||||
|
SetPC64(code.SCR2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::BranchTaken(const Xbyak::Reg64 &offs) {
|
void JIT::BranchTaken(const Xbyak::Reg64 &offs) {
|
||||||
code.mov(code.SCR1, REG(qword, pc));
|
code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]);
|
||||||
code.add(code.SCR1, offs);
|
code.add(code.SCR2, offs);
|
||||||
SetPC64(offs);
|
SetPC64(code.SCR2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::BranchAbsTaken(s64 addr) {
|
void JIT::BranchAbsTaken(const s64 addr) {
|
||||||
code.add(code.SCR1, addr);
|
SetPC64(addr);
|
||||||
code.mov(REG(qword, nextPC), code.SCR1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) {
|
void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) {
|
||||||
code.mov(REG(qword, nextPC), addr);
|
SetPC64(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::branch_constant(const bool cond, 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::branch_likely_constant(bool cond, s64 offset) {
|
void JIT::branch_abs_constant(const bool cond, const s64 address) {
|
||||||
|
if(cond) {
|
||||||
|
regs.delaySlot = true;
|
||||||
|
BranchAbsTaken(address);
|
||||||
|
branch_taken = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
} else {
|
} else {
|
||||||
SetPC64(blockNextPC);
|
SetPC64(blockNextPC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::branch_abs_constant(bool cond, s64 address) {
|
|
||||||
if(cond) {
|
|
||||||
regs.delaySlot = true;
|
|
||||||
BranchAbsTaken(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#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.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.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; \
|
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); \
|
||||||
|
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \
|
||||||
|
code.jmp(end); \
|
||||||
code.L(not_taken); \
|
code.L(not_taken); \
|
||||||
SetPC64(blockNextPC); \
|
SetPC64(blockNextPC); \
|
||||||
|
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;
|
||||||
const s64 offset = u64((s64)imm) << 2;
|
const s64 offset = static_cast<s64>(imm) << 2;
|
||||||
if (regs.IsRegConstant(instr.rs())) {
|
if (regs.IsRegConstant(instr.rs())) {
|
||||||
branch_constant(regs.Read<s64>(instr.rs()) < 0, offset);
|
branch_constant(regs.Read<s64>(instr.rs()) < 0, offset);
|
||||||
return;
|
return;
|
||||||
@@ -435,7 +443,7 @@ void JIT::beql(const Instruction instr) {
|
|||||||
|
|
||||||
void JIT::bne(const Instruction instr) {
|
void JIT::bne(const Instruction instr) {
|
||||||
const s16 imm = instr;
|
const s16 imm = instr;
|
||||||
const s64 offset = u64((s64)imm) << 2;
|
const s64 offset = static_cast<s64>(imm) << 2;
|
||||||
if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) {
|
if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) {
|
||||||
branch_constant(regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()), offset);
|
branch_constant(regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()), offset);
|
||||||
return;
|
return;
|
||||||
@@ -857,13 +865,13 @@ void JIT::dsubu(const Instruction instr) {
|
|||||||
|
|
||||||
void JIT::j(const Instruction instr) {
|
void JIT::j(const Instruction instr) {
|
||||||
const s32 target = (instr & 0x3ffffff) << 2;
|
const s32 target = (instr & 0x3ffffff) << 2;
|
||||||
const s64 address = (blockOldPC & ~0xfffffff) | target;
|
const s64 address = blockOldPC & ~0xfffffff | target;
|
||||||
branch_abs_constant(true, address);
|
branch_abs_constant(true, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::jr(const Instruction instr) {
|
void JIT::jr(const Instruction instr) {
|
||||||
if (regs.IsRegConstant(instr.rs())) {
|
if (regs.IsRegConstant(instr.rs())) {
|
||||||
const u64 address = regs.Read<s64>(instr.rs());
|
const auto address = regs.Read<s64>(instr.rs());
|
||||||
branch_abs_constant(true, address);
|
branch_abs_constant(true, address);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -891,9 +899,7 @@ void JIT::lbu(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);
|
||||||
panic("[JIT]: Unhandled TLBL exception in LBU!");
|
panic("[JIT]: Unhandled TLBL exception in LBU!");
|
||||||
} else {
|
} else {
|
||||||
code.mov(code.ARG2,
|
code.mov(code.ARG2, paddr);
|
||||||
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
|
||||||
code.mov(code.ARG3, paddr);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u8>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u8>, &mem);
|
||||||
regs.Write<u8>(instr.rt(), code.rax);
|
regs.Write<u8>(instr.rt(), code.rax);
|
||||||
}
|
}
|
||||||
@@ -908,11 +914,9 @@ void JIT::lb(const Instruction instr) {
|
|||||||
if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||||
// 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);
|
||||||
panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", blockPC);
|
panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast<u64>(blockPC));
|
||||||
} else {
|
} else {
|
||||||
code.mov(code.ARG2,
|
code.mov(code.ARG2, paddr);
|
||||||
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
|
||||||
code.mov(code.ARG3, paddr);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u8>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u8>, &mem);
|
||||||
regs.Write<s8>(instr.rt(), code.rax);
|
regs.Write<s8>(instr.rt(), code.rax);
|
||||||
}
|
}
|
||||||
@@ -937,9 +941,7 @@ void JIT::ld(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);
|
||||||
panic("[JIT]: Unhandled TLBL exception in LD!");
|
panic("[JIT]: Unhandled TLBL exception in LD!");
|
||||||
} else {
|
} else {
|
||||||
code.mov(code.ARG2,
|
code.mov(code.ARG2, paddr);
|
||||||
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
|
||||||
code.mov(code.ARG3, paddr);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u64>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u64>, &mem);
|
||||||
regs.Write<u64>(instr.rt(), code.rax);
|
regs.Write<u64>(instr.rt(), code.rax);
|
||||||
}
|
}
|
||||||
@@ -1026,8 +1028,7 @@ void JIT::lh(const Instruction instr) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
code.mov(code.ARG2, paddr);
|
||||||
code.mov(code.ARG3, paddr);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u16>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u16>, &mem);
|
||||||
regs.Write<s16>(instr.rt(), code.rax);
|
regs.Write<s16>(instr.rt(), code.rax);
|
||||||
return;
|
return;
|
||||||
@@ -1052,8 +1053,7 @@ void JIT::lhu(const Instruction instr) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
code.mov(code.ARG2, paddr);
|
||||||
code.mov(code.ARG3, paddr);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u16>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u16>, &mem);
|
||||||
regs.Write<u16>(instr.rt(), code.rax);
|
regs.Write<u16>(instr.rt(), code.rax);
|
||||||
}
|
}
|
||||||
@@ -1064,8 +1064,7 @@ void JIT::lhu(const Instruction instr) {
|
|||||||
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr));
|
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr));
|
||||||
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
||||||
|
|
||||||
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
code.mov(code.ARG2, code.qword[code.ARG4]);
|
||||||
code.mov(code.ARG3, code.qword[code.ARG4]);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u16>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u16>, &mem);
|
||||||
regs.Write<u16>(instr.rt(), code.rax);
|
regs.Write<u16>(instr.rt(), code.rax);
|
||||||
}
|
}
|
||||||
@@ -1094,8 +1093,7 @@ void JIT::lw(const Instruction instr) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
code.mov(code.ARG2, paddr);
|
||||||
code.mov(code.ARG3, paddr);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u32>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u32>, &mem);
|
||||||
regs.Write<s32>(instr.rt(), code.rax);
|
regs.Write<s32>(instr.rt(), code.rax);
|
||||||
return;
|
return;
|
||||||
@@ -1109,8 +1107,7 @@ void JIT::lw(const Instruction instr) {
|
|||||||
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr));
|
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr));
|
||||||
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
||||||
|
|
||||||
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
code.mov(code.ARG2, code.qword[code.ARG4]);
|
||||||
code.mov(code.ARG3, code.qword[code.ARG4]);
|
|
||||||
emitMemberFunctionCall(&Mem::Read<u32>, &mem);
|
emitMemberFunctionCall(&Mem::Read<u32>, &mem);
|
||||||
regs.Write<s32>(instr.rt(), code.rax);
|
regs.Write<s32>(instr.rt(), code.rax);
|
||||||
}
|
}
|
||||||
@@ -1127,7 +1124,7 @@ void JIT::mfhi(const Instruction instr) {
|
|||||||
if (regs.GetHIConstant()) {
|
if (regs.GetHIConstant()) {
|
||||||
regs.Write(instr.rd(), regs.hi);
|
regs.Write(instr.rd(), regs.hi);
|
||||||
} else {
|
} else {
|
||||||
code.mov(code.SCR1, REG(qword, hi));
|
code.mov(code.SCR1, code.qword[HI_OFFSET]);
|
||||||
regs.Write<s64>(instr.rd(), code.SCR1);
|
regs.Write<s64>(instr.rd(), code.SCR1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1136,7 +1133,7 @@ void JIT::mflo(const Instruction instr) {
|
|||||||
if (regs.GetLOConstant()) {
|
if (regs.GetLOConstant()) {
|
||||||
regs.Write(instr.rd(), regs.lo);
|
regs.Write(instr.rd(), regs.lo);
|
||||||
} else {
|
} else {
|
||||||
code.mov(code.SCR1, REG(qword, lo));
|
code.mov(code.SCR1, code.qword[LO_OFFSET]);
|
||||||
regs.Write<s64>(instr.rd(), code.SCR1);
|
regs.Write<s64>(instr.rd(), code.SCR1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1175,7 +1172,7 @@ void JIT::mthi(const Instruction instr) {
|
|||||||
regs.SetHIConstant();
|
regs.SetHIConstant();
|
||||||
} else {
|
} else {
|
||||||
regs.Read<s64>(instr.rs(), code.SCR1);
|
regs.Read<s64>(instr.rs(), code.SCR1);
|
||||||
code.mov(REG(qword, hi), code.SCR1);
|
code.mov(code.qword[HI_OFFSET], code.SCR1);
|
||||||
regs.UnsetHIConstant();
|
regs.UnsetHIConstant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1186,7 +1183,7 @@ void JIT::mtlo(const Instruction instr) {
|
|||||||
regs.SetLOConstant();
|
regs.SetLOConstant();
|
||||||
} else {
|
} else {
|
||||||
regs.Read<s64>(instr.rs(), code.SCR1);
|
regs.Read<s64>(instr.rs(), code.SCR1);
|
||||||
code.mov(REG(qword, lo), code.SCR1);
|
code.mov(code.qword[LO_OFFSET], code.SCR1);
|
||||||
regs.UnsetLOConstant();
|
regs.UnsetLOConstant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1357,10 +1354,8 @@ void JIT::sw(const Instruction instr) {
|
|||||||
// 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);
|
||||||
panic("[JIT]: Unhandled TLBS exception in SW!");
|
panic("[JIT]: Unhandled TLBS exception in SW!");
|
||||||
} else {
|
} else {
|
||||||
code.lea(code.ARG2,
|
code.mov(code.ARG2, physical);
|
||||||
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
regs.Read<s64>(instr.rt(), code.ARG3);
|
||||||
code.mov(code.ARG3, physical);
|
|
||||||
regs.Read<s64>(instr.rt(), code.ARG4);
|
|
||||||
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1382,10 +1377,8 @@ void JIT::sw(const Instruction instr) {
|
|||||||
// 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);
|
||||||
panic("[JIT]: Unhandled TLBS exception in SW!");
|
panic("[JIT]: Unhandled TLBS exception in SW!");
|
||||||
} else {
|
} else {
|
||||||
code.mov(code.ARG2,
|
code.mov(code.ARG2, physical);
|
||||||
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
regs.Read<s64>(instr.rt(), code.ARG3);
|
||||||
code.mov(code.ARG3, physical);
|
|
||||||
regs.Read<s64>(instr.rt(), code.ARG4);
|
|
||||||
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1402,9 +1395,8 @@ void JIT::sw(const Instruction instr) {
|
|||||||
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical));
|
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical));
|
||||||
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
||||||
|
|
||||||
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
code.mov(code.ARG2, code.qword[code.ARG4]);
|
||||||
code.mov(code.ARG3, code.qword[code.ARG4]);
|
regs.Read<s64>(instr.rt(), code.ARG3);
|
||||||
regs.Read<s64>(instr.rt(), code.ARG4);
|
|
||||||
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1419,9 +1411,8 @@ void JIT::sw(const Instruction instr) {
|
|||||||
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical));
|
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical));
|
||||||
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0);
|
||||||
|
|
||||||
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(®s) - reinterpret_cast<uintptr_t>(this))]);
|
code.mov(code.ARG2, code.qword[code.ARG4]);
|
||||||
code.mov(code.ARG3, code.qword[code.ARG4]);
|
regs.Read<s64>(instr.rt(), code.ARG3);
|
||||||
regs.Read<s64>(instr.rt(), code.ARG4);
|
|
||||||
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,19 +10,20 @@ struct AI {
|
|||||||
void Write(u32, u32);
|
void Write(u32, u32);
|
||||||
void Step(u32, float, float);
|
void Step(u32, float, float);
|
||||||
bool dmaEnable{};
|
bool dmaEnable{};
|
||||||
u16 dacRate{};
|
bool dmaAddrCarry{};
|
||||||
u8 bitrate{};
|
u8 bitrate{};
|
||||||
|
u16 dacRate{};
|
||||||
int dmaCount{};
|
int dmaCount{};
|
||||||
|
u32 cycles{};
|
||||||
std::array<u32, 2> dmaLen{};
|
std::array<u32, 2> dmaLen{};
|
||||||
std::array<u32, 2> dmaAddr{};
|
std::array<u32, 2> dmaAddr{};
|
||||||
bool dmaAddrCarry{};
|
|
||||||
u32 cycles{};
|
|
||||||
AudioDevice device;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u32 freq{44100};
|
u32 freq{44100};
|
||||||
u32 period{N64_CPU_FREQ / freq};
|
u32 period{N64_CPU_FREQ / freq};
|
||||||
u32 precision{16};
|
u32 precision{16};
|
||||||
} dac;
|
} dac;
|
||||||
|
|
||||||
|
AudioDevice device;
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ struct AudioDevice {
|
|||||||
SDL_AudioStream *GetStream() const { return audioStream; }
|
SDL_AudioStream *GetStream() const { return audioStream; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool running = false;
|
||||||
SDL_AudioStream *audioStream;
|
SDL_AudioStream *audioStream;
|
||||||
SDL_Mutex *audioStreamMutex;
|
SDL_Mutex *audioStreamMutex;
|
||||||
SDL_AudioSpec request{};
|
SDL_AudioSpec request{};
|
||||||
bool running = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -197,11 +197,11 @@ struct PIF {
|
|||||||
void UpdateAxis(int index, Controller::Axis a, s8 state) { joybusDevices[index].controller.UpdateAxis(a, state); }
|
void UpdateAxis(int index, Controller::Axis a, s8 state) { joybusDevices[index].controller.UpdateAxis(a, state); }
|
||||||
|
|
||||||
bool mempakOpen = false;
|
bool mempakOpen = false;
|
||||||
std::array<JoybusDevice, 6> joybusDevices{};
|
|
||||||
std::array<u8, PIF_BOOTROM_SIZE> bootrom{};
|
std::array<u8, PIF_BOOTROM_SIZE> bootrom{};
|
||||||
std::array<u8, PIF_RAM_SIZE> ram{};
|
std::array<u8, PIF_RAM_SIZE> ram{};
|
||||||
mio::mmap_sink mempak, eeprom;
|
|
||||||
int channel = 0;
|
int channel = 0;
|
||||||
|
std::array<JoybusDevice, 6> joybusDevices{};
|
||||||
|
mio::mmap_sink mempak, eeprom;
|
||||||
std::string mempakPath{}, eepromPath{};
|
std::string mempakPath{}, eepromPath{};
|
||||||
size_t eepromSize{};
|
size_t eepromSize{};
|
||||||
MupenMovie movie;
|
MupenMovie movie;
|
||||||
|
|||||||
@@ -20,16 +20,16 @@ union SIStatus {
|
|||||||
struct SI {
|
struct SI {
|
||||||
SI();
|
SI();
|
||||||
void Reset();
|
void Reset();
|
||||||
SIStatus status{};
|
|
||||||
u32 dramAddr{};
|
|
||||||
u32 pifAddr{};
|
|
||||||
bool toDram = false;
|
|
||||||
|
|
||||||
[[nodiscard]] auto Read(u32) const -> u32;
|
[[nodiscard]] auto Read(u32) const -> u32;
|
||||||
void Write(u32, u32);
|
void Write(u32, u32);
|
||||||
template <bool toDram>
|
template <bool toDram>
|
||||||
void DMA();
|
void DMA();
|
||||||
void DMA();
|
void DMA();
|
||||||
|
|
||||||
|
bool toDram = false;
|
||||||
|
SIStatus status{};
|
||||||
|
u32 dramAddr{};
|
||||||
|
u32 pifAddr{};
|
||||||
PIF pif;
|
PIF pif;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ struct VI {
|
|||||||
void Reset();
|
void Reset();
|
||||||
[[nodiscard]] u32 Read(u32) const;
|
[[nodiscard]] u32 Read(u32) const;
|
||||||
void Write(u32, u32);
|
void Write(u32, u32);
|
||||||
|
|
||||||
|
bool isPal = false;
|
||||||
AxisScale xscale{}, yscale{};
|
AxisScale xscale{}, yscale{};
|
||||||
VIHsyncLeap hsyncLeap{};
|
VIHsyncLeap hsyncLeap{};
|
||||||
VIStatus status{};
|
VIStatus status{};
|
||||||
@@ -80,7 +82,6 @@ struct VI {
|
|||||||
u32 origin{}, width{}, current{};
|
u32 origin{}, width{}, current{};
|
||||||
u32 vsync{}, hsync{}, intr{};
|
u32 vsync{}, hsync{}, intr{};
|
||||||
AxisStart hstart{}, vstart{};
|
AxisStart hstart{}, vstart{};
|
||||||
bool isPal = false;
|
|
||||||
int swaps{};
|
int swaps{};
|
||||||
int numHalflines{};
|
int numHalflines{};
|
||||||
int numFields{};
|
int numFields{};
|
||||||
|
|||||||
@@ -192,6 +192,34 @@ union Cop0XContext {
|
|||||||
struct Cop0 {
|
struct Cop0 {
|
||||||
Cop0();
|
Cop0();
|
||||||
|
|
||||||
|
bool kernelMode{true};
|
||||||
|
bool supervisorMode{false};
|
||||||
|
bool userMode{false};
|
||||||
|
bool is64BitAddressing{false};
|
||||||
|
bool llbit{};
|
||||||
|
TLBError tlbError = NONE;
|
||||||
|
|
||||||
|
PageMask pageMask{};
|
||||||
|
EntryHi entryHi{};
|
||||||
|
EntryLo entryLo0{}, entryLo1{};
|
||||||
|
Index index{};
|
||||||
|
Cop0Context context{};
|
||||||
|
u32 wired{}, r7{};
|
||||||
|
u32 compare{};
|
||||||
|
Cop0Status status{};
|
||||||
|
Cop0Cause cause{};
|
||||||
|
u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{};
|
||||||
|
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{};
|
||||||
|
u32 r31{};
|
||||||
|
Cop0XContext xcontext{};
|
||||||
|
u64 badVaddr{}, count{};
|
||||||
|
s64 EPC{};
|
||||||
|
s64 ErrorEPC{};
|
||||||
|
s64 openbus{};
|
||||||
|
TLBEntry tlb[32]{};
|
||||||
|
|
||||||
|
enum TLBAccessType { LOAD, STORE };
|
||||||
|
|
||||||
u32 GetReg32(u8);
|
u32 GetReg32(u8);
|
||||||
[[nodiscard]] u64 GetReg64(u8) const;
|
[[nodiscard]] u64 GetReg64(u8) const;
|
||||||
|
|
||||||
@@ -200,31 +228,15 @@ struct Cop0 {
|
|||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
bool kernelMode{true};
|
bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||||
bool supervisorMode{false};
|
void FireException(ExceptionCode code, int cop, s64 pc);
|
||||||
bool userMode{false};
|
bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||||
bool is64BitAddressing{false};
|
|
||||||
bool llbit{};
|
|
||||||
|
|
||||||
PageMask pageMask{};
|
TLBEntry *TLBTryMatch(u64 vaddr, int &index);
|
||||||
EntryHi entryHi{};
|
TLBEntry *TLBTryMatch(u64 vaddr);
|
||||||
EntryLo entryLo0{}, entryLo1{};
|
void HandleTLBException(u64 vaddr);
|
||||||
Index index{};
|
static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType);
|
||||||
Cop0Context context{};
|
void decode(const Instruction);
|
||||||
u32 wired{}, r7{};
|
|
||||||
u64 badVaddr{}, count{};
|
|
||||||
u32 compare{};
|
|
||||||
Cop0Status status{};
|
|
||||||
Cop0Cause cause{};
|
|
||||||
s64 EPC{};
|
|
||||||
u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{};
|
|
||||||
Cop0XContext xcontext{};
|
|
||||||
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{};
|
|
||||||
s64 ErrorEPC{};
|
|
||||||
u32 r31{};
|
|
||||||
TLBEntry tlb[32]{};
|
|
||||||
TLBError tlbError = NONE;
|
|
||||||
s64 openbus{};
|
|
||||||
|
|
||||||
[[nodiscard]] FORCE_INLINE u32 GetRandom() const {
|
[[nodiscard]] FORCE_INLINE u32 GetRandom() const {
|
||||||
u32 val = rand();
|
u32 val = rand();
|
||||||
@@ -251,19 +263,9 @@ struct Cop0 {
|
|||||||
is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux);
|
is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TLBAccessType { LOAD, STORE };
|
|
||||||
|
|
||||||
bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
|
||||||
void FireException(ExceptionCode code, int cop, s64 pc);
|
|
||||||
bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
|
||||||
|
|
||||||
TLBEntry *TLBTryMatch(u64 vaddr, int &index);
|
|
||||||
TLBEntry *TLBTryMatch(u64 vaddr);
|
|
||||||
void HandleTLBException(u64 vaddr);
|
|
||||||
static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType);
|
|
||||||
void decode(const Instruction);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend struct JIT;
|
||||||
|
|
||||||
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
||||||
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }
|
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }
|
||||||
|
|
||||||
|
|||||||
@@ -112,10 +112,10 @@ union FloatingPointReg {
|
|||||||
|
|
||||||
struct Cop1 {
|
struct Cop1 {
|
||||||
explicit Cop1();
|
explicit Cop1();
|
||||||
|
bool fgrIsConstant[32]{};
|
||||||
u32 fcr0{};
|
u32 fcr0{};
|
||||||
FCR31 fcr31{};
|
FCR31 fcr31{};
|
||||||
FloatingPointReg fgr[32]{};
|
FloatingPointReg fgr[32]{};
|
||||||
bool fgrIsConstant[32]{};
|
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
void decode(const Instruction);
|
void decode(const Instruction);
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ struct Registers {
|
|||||||
return regIsConstant & (1 << index);
|
return regIsConstant & (1 << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsRegConstant(const u32 first, const u32 second) const {
|
[[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const {
|
||||||
return IsRegConstant(first) && IsRegConstant(second);
|
return IsRegConstant(index1) && IsRegConstant(index2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetLOConstant() {
|
bool GetLOConstant() {
|
||||||
@@ -50,13 +50,13 @@ struct Registers {
|
|||||||
|
|
||||||
uint64_t regIsConstant = 0;
|
uint64_t regIsConstant = 0;
|
||||||
|
|
||||||
Cop0 cop0;
|
|
||||||
Cop1 cop1;
|
|
||||||
s64 oldPC{}, pc{}, nextPC{};
|
|
||||||
s64 hi{}, lo{};
|
|
||||||
bool prevDelaySlot{}, delaySlot{};
|
bool prevDelaySlot{}, delaySlot{};
|
||||||
u32 steps = 0;
|
u32 steps = 0;
|
||||||
u32 extraCycles = 0;
|
u32 extraCycles = 0;
|
||||||
|
s64 oldPC{}, pc{}, nextPC{};
|
||||||
|
s64 hi{}, lo{};
|
||||||
|
Cop0 cop0;
|
||||||
|
Cop1 cop1;
|
||||||
|
|
||||||
void CpuStall(u32 cycles) { extraCycles += cycles; }
|
void CpuStall(u32 cycles) { extraCycles += cycles; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <bitset>
|
|
||||||
#include <types.hpp>
|
#include <types.hpp>
|
||||||
#include <ErrorData.hpp>
|
#include <ErrorData.hpp>
|
||||||
|
|
||||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||||
|
|
||||||
constexpr u32 N64_CPU_FREQ = 93750000;
|
constexpr u32 N64_CPU_FREQ = 93750000;
|
||||||
constexpr u16 KAIZEN_VERSION = 0x010;
|
#ifdef KAIZEN_USE_HASH
|
||||||
|
#include <resources/version.hpp>
|
||||||
|
#define KAIZEN_VERSION_STR KAIZEN_GIT_COMMIT_HASH
|
||||||
|
#else
|
||||||
|
#define KAIZEN_VERSION_YEAR 2026
|
||||||
|
#define KAIZEN_VERSION_MONTH 1
|
||||||
|
#define STR_HELPER(x) #x
|
||||||
|
#define STR(x) STR_HELPER(x)
|
||||||
|
#define KAIZEN_VERSION_STR STR(KAIZEN_VERSION_YEAR) "." STR(KAIZEN_VERSION_MONTH)
|
||||||
|
#endif
|
||||||
|
|
||||||
static FORCE_INLINE constexpr u32 GetCyclesPerFrame(bool pal) {
|
static FORCE_INLINE constexpr u32 GetCyclesPerFrame(bool pal) {
|
||||||
if (pal) {
|
if (pal) {
|
||||||
|
|||||||
@@ -7,44 +7,42 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
static VkAllocationCallbacks* g_Allocator = NULL;
|
static VkAllocationCallbacks *g_Allocator = NULL;
|
||||||
static VkInstance g_Instance = VK_NULL_HANDLE;
|
static VkInstance g_Instance = VK_NULL_HANDLE;
|
||||||
static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE;
|
static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE;
|
||||||
static VkDevice g_Device = VK_NULL_HANDLE;
|
static VkDevice g_Device = VK_NULL_HANDLE;
|
||||||
static uint32_t g_QueueFamily = (uint32_t)-1;
|
static uint32_t g_QueueFamily = (uint32_t)-1;
|
||||||
static VkQueue g_Queue = VK_NULL_HANDLE;
|
static VkQueue g_Queue = VK_NULL_HANDLE;
|
||||||
static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE;
|
static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE;
|
||||||
static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE;
|
static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE;
|
||||||
|
|
||||||
static ImGui_ImplVulkanH_Window g_MainWindowData;
|
static ImGui_ImplVulkanH_Window g_MainWindowData;
|
||||||
static uint32_t g_MinImageCount = 2;
|
static uint32_t g_MinImageCount = 2;
|
||||||
|
|
||||||
inline std::shared_ptr<Vulkan::WSI> g_Wsi;
|
static void CheckVkResult(VkResult err) {
|
||||||
|
|
||||||
static void CheckVkResult(VkResult err) {
|
|
||||||
if (err == VK_SUCCESS)
|
if (err == VK_SUCCESS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (err < VK_SUCCESS)
|
if (err < VK_SUCCESS)
|
||||||
panic("[vulkan] VkResult = {}", (int) err);
|
panic("[vulkan] VkResult = {}", (int)err);
|
||||||
|
|
||||||
warn("[vulkan] VkResult = {}", (int) err);
|
warn("[vulkan] VkResult = {}", (int)err);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Initialize(const std::shared_ptr<Vulkan::WSI>& wsi, SDL_Window* nativeWindow) {
|
inline void Initialize(const std::shared_ptr<Vulkan::WSI> &wsi, SDL_Window *nativeWindow) {
|
||||||
VkResult err;
|
VkResult err;
|
||||||
g_Wsi = wsi;
|
|
||||||
// Setup Dear ImGui context
|
// Setup Dear ImGui context
|
||||||
IMGUI_CHECKVERSION();
|
IMGUI_CHECKVERSION();
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
|
(void)io;
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||||
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||||
|
|
||||||
// Setup Dear ImGui style
|
// Setup Dear ImGui style
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
//ImGui::StyleColorsClassic();
|
// ImGui::StyleColorsClassic();
|
||||||
|
|
||||||
g_Instance = wsi->get_context().get_instance();
|
g_Instance = wsi->get_context().get_instance();
|
||||||
g_PhysicalDevice = wsi->get_device().get_physical_device();
|
g_PhysicalDevice = wsi->get_device().get_physical_device();
|
||||||
@@ -59,20 +57,17 @@ namespace gui {
|
|||||||
|
|
||||||
// Create Descriptor Pool
|
// Create Descriptor Pool
|
||||||
{
|
{
|
||||||
VkDescriptorPoolSize pool_sizes[] =
|
VkDescriptorPoolSize pool_sizes[] = {{VK_DESCRIPTOR_TYPE_SAMPLER, 1000},
|
||||||
{
|
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
|
{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
|
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
|
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
|
{VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
|
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
|
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
|
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
|
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000},
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
|
{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}};
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
|
|
||||||
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
|
|
||||||
};
|
|
||||||
VkDescriptorPoolCreateInfo pool_info = {};
|
VkDescriptorPoolCreateInfo pool_info = {};
|
||||||
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||||
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||||
@@ -137,23 +132,26 @@ namespace gui {
|
|||||||
init_info.RenderPass = renderPass;
|
init_info.RenderPass = renderPass;
|
||||||
init_info.ApiVersion = VK_API_VERSION_1_3;
|
init_info.ApiVersion = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_3, [](const char *function_name, void *vulkan_instance) {
|
ImGui_ImplVulkan_LoadFunctions(
|
||||||
|
VK_API_VERSION_1_3,
|
||||||
|
[](const char *function_name, void *vulkan_instance) {
|
||||||
return vkGetInstanceProcAddr((reinterpret_cast<VkInstance>(vulkan_instance)), function_name);
|
return vkGetInstanceProcAddr((reinterpret_cast<VkInstance>(vulkan_instance)), function_name);
|
||||||
}, g_Instance);
|
},
|
||||||
|
g_Instance);
|
||||||
|
|
||||||
if(!ImGui_ImplVulkan_Init(&init_info))
|
if (!ImGui_ImplVulkan_Init(&init_info))
|
||||||
panic("Failed to initialize ImGui!");
|
panic("Failed to initialize ImGui!");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void StartFrame() {
|
inline void StartFrame() {
|
||||||
ImGui_ImplVulkan_NewFrame();
|
ImGui_ImplVulkan_NewFrame();
|
||||||
ImGui_ImplSDL3_NewFrame();
|
ImGui_ImplSDL3_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Cleanup() {
|
inline void Cleanup() {
|
||||||
ImGui_ImplVulkan_Shutdown();
|
ImGui_ImplVulkan_Shutdown();
|
||||||
ImGui_ImplSDL3_Shutdown();
|
ImGui_ImplSDL3_Shutdown();
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // namespace gui
|
||||||
|
|||||||
43
src/frontend/ImGuiImpl/ProgressIndicators.hpp
Normal file
43
src/frontend/ImGuiImpl/ProgressIndicators.hpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
|
||||||
|
namespace ImGui {
|
||||||
|
inline bool Spinner(const char* label, const float radius, const int thickness, const ImU32& color) {
|
||||||
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
if (window->SkipItems)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const ImGuiContext & g = *GImGui;
|
||||||
|
const ImGuiStyle& style = g.Style;
|
||||||
|
const ImGuiID id = window->GetID(label);
|
||||||
|
|
||||||
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
|
const ImVec2 size(radius*2, (radius + style.FramePadding.y)*2);
|
||||||
|
|
||||||
|
const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y));
|
||||||
|
ItemSize(bb, style.FramePadding.y);
|
||||||
|
if (!ItemAdd(bb, id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Render
|
||||||
|
window->DrawList->PathClear();
|
||||||
|
|
||||||
|
constexpr int num_segments = 30;
|
||||||
|
const int start = abs(ImSin(g.Time*1.8f)*(num_segments-5));
|
||||||
|
|
||||||
|
const float a_min = IM_PI * 2.0f * static_cast<float>(start) / static_cast<float>(num_segments);
|
||||||
|
constexpr float a_max = IM_PI*2.0f * (static_cast<float>(num_segments) -3) / static_cast<float>(num_segments);
|
||||||
|
|
||||||
|
const auto centre = ImVec2(pos.x+radius, pos.y+radius+style.FramePadding.y);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_segments; i++) {
|
||||||
|
const float a = a_min + static_cast<float>(i) / static_cast<float>(num_segments) * (a_max - a_min);
|
||||||
|
window->DrawList->PathLineTo(ImVec2(centre.x + ImCos(a+g.Time*8) * radius,
|
||||||
|
centre.y + ImSin(a+g.Time*8) * radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
window->DrawList->PathStroke(color, false, thickness);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace ImGui {
|
namespace ImGui {
|
||||||
inline bool BeginMainStatusBar()
|
inline bool BeginMainStatusBar()
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#include <KaizenGui.hpp>
|
#include <KaizenGui.hpp>
|
||||||
#include <backend/Core.hpp>
|
#include <backend/Core.hpp>
|
||||||
#include <ImGuiImpl/GUI.hpp>
|
#include <ImGuiImpl/GUI.hpp>
|
||||||
|
#include <ImGuiImpl/ProgressIndicators.hpp>
|
||||||
#include <ImGuiImpl/StatusBar.hpp>
|
#include <ImGuiImpl/StatusBar.hpp>
|
||||||
#include <resources/gamecontrollerdb.h>
|
#include <resources/gamecontrollerdb.h>
|
||||||
|
|
||||||
KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), 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);
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ KaizenGui::~KaizenGui() {
|
|||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::QueryDevices(SDL_Event event) {
|
void KaizenGui::QueryDevices(const SDL_Event &event) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_GAMEPAD_ADDED:
|
case SDL_EVENT_GAMEPAD_ADDED:
|
||||||
if (!gamepad) {
|
if (!gamepad) {
|
||||||
@@ -36,8 +37,8 @@ void KaizenGui::QueryDevices(SDL_Event event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::HandleInput(SDL_Event event) {
|
void KaizenGui::HandleInput(const SDL_Event &event) {
|
||||||
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:
|
||||||
@@ -52,7 +53,7 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
|
|
||||||
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
||||||
if (xclamped < 0) {
|
if (xclamped < 0) {
|
||||||
xclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
||||||
} else {
|
} else {
|
||||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
@@ -61,15 +62,15 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
|
|
||||||
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
||||||
if (yclamped < 0) {
|
if (yclamped < 0) {
|
||||||
yclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||||
} else {
|
} else {
|
||||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
yclamped *= 86;
|
yclamped *= 86;
|
||||||
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, -yclamped);
|
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, 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:
|
||||||
@@ -90,7 +91,7 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -130,15 +131,23 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
||||||
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
||||||
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, keys[SDL_SCANCODE_UP] ? 86 : keys[SDL_SCANCODE_DOWN] ? -86 : 0);
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, keys[SDL_SCANCODE_LEFT] ? -86 : keys[SDL_SCANCODE_RIGHT] ? 86 : 0);
|
float x = 0, y = 0;
|
||||||
|
|
||||||
|
if (keys[SDL_SCANCODE_UP]) y = 86;
|
||||||
|
if (keys[SDL_SCANCODE_DOWN]) 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::Y, y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<std::optional<u64>, 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());
|
||||||
@@ -146,10 +155,10 @@ std::tuple<std::optional<u64>, std::optional<Util::Error::MemoryAccess>> RenderE
|
|||||||
|
|
||||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||||
if(memoryAccess.has_value()) {
|
if(memoryAccess.has_value()) {
|
||||||
auto memoryAccessV = memoryAccess.value();
|
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||||
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", memoryAccessV.is_write ? "Writing" : "Reading",
|
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading",
|
||||||
(u8)memoryAccessV.size, memoryAccessV.address,
|
static_cast<u8>(size), address,
|
||||||
memoryAccessV.is_write ? std::format(" (value = 0x{:X})", memoryAccessV.written_val) : "")
|
is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||||
.c_str());
|
.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +236,7 @@ void KaizenGui::RenderUI() {
|
|||||||
settingsWindow.render();
|
settingsWindow.render();
|
||||||
debugger.render();
|
debugger.render();
|
||||||
|
|
||||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
@@ -235,6 +244,9 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::Text("to offer a friendly user experience and compatibility.");
|
ImGui::Text("to offer a friendly user experience and compatibility.");
|
||||||
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
||||||
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
||||||
|
ImGui::Separator();
|
||||||
if(ImGui::Button("OK")) {
|
if(ImGui::Button("OK")) {
|
||||||
aboutOpen = false;
|
aboutOpen = false;
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
@@ -258,9 +270,9 @@ void KaizenGui::RenderUI() {
|
|||||||
RenderErrorMessageDetails();
|
RenderErrorMessageDetails();
|
||||||
|
|
||||||
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||||
bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||||
bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||||
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();
|
||||||
@@ -306,10 +318,10 @@ 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();
|
||||||
|
|
||||||
bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||||
bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||||
bool chooseAnother = ImGui::Button("Choose another ROM");
|
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||||
bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
||||||
if(ignore || stop || chooseAnother || openInDebugger) {
|
if(ignore || stop || chooseAnother || openInDebugger) {
|
||||||
Util::Error::SetHandled();
|
Util::Error::SetHandled();
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
@@ -346,6 +358,26 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::EndMainStatusBar();
|
ImGui::EndMainStatusBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldDisplaySpinner) {
|
||||||
|
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::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
|
||||||
|
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
||||||
|
|
||||||
|
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
|
||||||
|
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||||
ImGui::UpdatePlatformWindows();
|
ImGui::UpdatePlatformWindows();
|
||||||
@@ -354,20 +386,25 @@ void KaizenGui::RenderUI() {
|
|||||||
|
|
||||||
if(fileDialogOpen) {
|
if(fileDialogOpen) {
|
||||||
fileDialogOpen = false;
|
fileDialogOpen = false;
|
||||||
const 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", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
||||||
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int filter) {
|
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) {
|
||||||
auto kaizen = (KaizenGui*)userdata;
|
auto kaizen = static_cast<KaizenGui*>(userdata);
|
||||||
|
|
||||||
if (!filelist) {
|
if (!filelist) {
|
||||||
panic("An error occured: {}", SDL_GetError());
|
panic("An error occured: {}", SDL_GetError());
|
||||||
return;
|
}
|
||||||
} else if (!*filelist) {
|
|
||||||
|
if (!*filelist) {
|
||||||
warn("The user did not select any file.");
|
warn("The user did not select any file.");
|
||||||
warn("Most likely, the dialog was canceled.");
|
warn("Most likely, the dialog was canceled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
kaizen->LoadROM(*filelist);
|
kaizen->fileToLoad = *filelist;
|
||||||
|
kaizen->shouldDisplaySpinner = true;
|
||||||
|
|
||||||
|
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
||||||
|
fileWorker.detach();
|
||||||
}, this, window.getHandle(), filters, 3, nullptr, false);
|
}, this, window.getHandle(), filters, 3, nullptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,18 +412,18 @@ void KaizenGui::RenderUI() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(core.romLoaded) {
|
if(core.romLoaded) {
|
||||||
core.parallel.UpdateScreen();
|
core.parallel.UpdateScreen<true>();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
core.parallel.UpdateScreen(false);
|
core.parallel.UpdateScreen<false>();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = core.GetMem().rom.gameNameDB;
|
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen - " + gameNameDB).c_str());
|
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::run() {
|
void KaizenGui::run() {
|
||||||
@@ -405,11 +442,14 @@ void KaizenGui::run() {
|
|||||||
case SDL_EVENT_WINDOW_RESTORED:
|
case SDL_EVENT_WINDOW_RESTORED:
|
||||||
minimized = false;
|
minimized = false;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
QueryDevices(e);
|
QueryDevices(e);
|
||||||
HandleInput(e);
|
HandleInput(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||||
|
|
||||||
emuThread.run();
|
emuThread.run();
|
||||||
RenderUI();
|
RenderUI();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,23 @@ public:
|
|||||||
static void LoadTAS(const std::string &path) noexcept;
|
static void LoadTAS(const std::string &path) noexcept;
|
||||||
void LoadROM(const std::string &path) noexcept;
|
void LoadROM(const std::string &path) noexcept;
|
||||||
private:
|
private:
|
||||||
|
int width{}, height{};
|
||||||
bool aboutOpen = false;
|
bool aboutOpen = false;
|
||||||
bool fileDialogOpen = false;
|
bool fileDialogOpen = false;
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
|
bool shouldDisplaySpinner = false;
|
||||||
|
std::string fileToLoad = "";
|
||||||
void RenderUI();
|
void RenderUI();
|
||||||
void HandleInput(SDL_Event event);
|
void HandleInput(const SDL_Event &event);
|
||||||
void QueryDevices(SDL_Event event);
|
void QueryDevices(const SDL_Event &event);
|
||||||
|
|
||||||
|
[[noreturn]] void FileWorker() {
|
||||||
|
while (true) {
|
||||||
|
if (!fileToLoad.empty()) {
|
||||||
|
LoadROM(fileToLoad);
|
||||||
|
shouldDisplaySpinner = false;
|
||||||
|
fileToLoad = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ namespace gui {
|
|||||||
struct NativeWindow {
|
struct NativeWindow {
|
||||||
NativeWindow(const std::string& title, int w, int h, int posX = SDL_WINDOWPOS_CENTERED, int posY = SDL_WINDOWPOS_CENTERED) {
|
NativeWindow(const std::string& title, int w, int h, int posX = SDL_WINDOWPOS_CENTERED, int posY = SDL_WINDOWPOS_CENTERED) {
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
window = SDL_CreateWindow(title.c_str(), w, h, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY);
|
float scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
|
||||||
|
window = SDL_CreateWindow(title.c_str(), w * scale, h * scale, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY);
|
||||||
|
|
||||||
if(volkInitialize() != VK_SUCCESS) {
|
if(volkInitialize() != VK_SUCCESS) {
|
||||||
panic("Failed to initialize Volk!");
|
panic("Failed to initialize Volk!");
|
||||||
@@ -19,10 +20,9 @@ struct NativeWindow {
|
|||||||
~NativeWindow() {
|
~NativeWindow() {
|
||||||
SDL_DestroyWindow(window);
|
SDL_DestroyWindow(window);
|
||||||
}
|
}
|
||||||
void setTitle(const std::string& v) { title = v; }
|
|
||||||
SDL_Window* getHandle() { return window; }
|
SDL_Window* getHandle() { return window; }
|
||||||
private:
|
private:
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
std::string title;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ void GeneralSettings::render() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
general->savesPath = *filelist;
|
general->savesPath = fs::absolute(*filelist).string();
|
||||||
Options::GetInstance().SetValue<std::string>("general", "savePath", general->savesPath);
|
Options::GetInstance().SetValue<std::string>("general", "savePath", general->savesPath);
|
||||||
general->modified = true;
|
general->modified = true;
|
||||||
}, this, window.getHandle(), nullptr, false);
|
}, this, window.getHandle(), nullptr, false);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ bool SettingsWindow::render() {
|
|||||||
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
|
|
||||||
if(!ImGui::BeginPopupModal("Settings", &isOpen))
|
if(!ImGui::BeginPopupModal("Settings", &isOpen, ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!ImGui::BeginTabBar("SettingsTabBar"))
|
if(!ImGui::BeginTabBar("SettingsTabBar"))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <KaizenGui.hpp>
|
#include <KaizenGui.hpp>
|
||||||
#include <cflags.hpp>
|
#include <cflags.hpp>
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(const int argc, char **argv) {
|
||||||
KaizenGui kaizenGui;
|
KaizenGui kaizenGui;
|
||||||
cflags::cflags flags;
|
cflags::cflags flags;
|
||||||
flags.add_string_callback('\0', "rom", [&kaizenGui](const std::string& v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line");
|
flags.add_string_callback('\0', "rom", [&kaizenGui](const std::string& v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line");
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct Error {
|
|||||||
UNRECOVERABLE,
|
UNRECOVERABLE,
|
||||||
} as_enum;
|
} as_enum;
|
||||||
|
|
||||||
const char* as_c_str() {
|
[[nodiscard]] const char* as_c_str() const {
|
||||||
switch(as_enum) {
|
switch(as_enum) {
|
||||||
case NONE: return "";
|
case NONE: return "";
|
||||||
case WARN: return "Warning";
|
case WARN: return "Warning";
|
||||||
@@ -70,7 +70,7 @@ struct Error {
|
|||||||
} as_enum;
|
} as_enum;
|
||||||
|
|
||||||
|
|
||||||
const char* as_c_str() {
|
[[nodiscard]] const char* as_c_str() const {
|
||||||
switch(as_enum) {
|
switch(as_enum) {
|
||||||
case SCHEDULER_EOL: return "SCHEDULER_EOL";
|
case SCHEDULER_EOL: return "SCHEDULER_EOL";
|
||||||
case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN";
|
case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN";
|
||||||
@@ -108,10 +108,8 @@ struct Error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <class... Args>
|
template <class... Args>
|
||||||
void Throw (Severity severity,
|
void Throw (const Severity severity, const Type type, const std::optional<u64> lastPC,
|
||||||
Type type,
|
const std::optional<MemoryAccess> memoryAccess,
|
||||||
std::optional<u64> lastPC,
|
|
||||||
std::optional<MemoryAccess> memoryAccess,
|
|
||||||
const std::format_string<Args...> fmt, Args... args) {
|
const std::format_string<Args...> fmt, Args... args) {
|
||||||
this->severity = severity;
|
this->severity = severity;
|
||||||
this->lastPC = lastPC;
|
this->lastPC = lastPC;
|
||||||
@@ -128,7 +126,7 @@ struct Error {
|
|||||||
static std::string& GetError() { return GetInstance().err; }
|
static std::string& GetError() { return GetInstance().err; }
|
||||||
static Severity& GetSeverity() { return GetInstance().severity; }
|
static Severity& GetSeverity() { return GetInstance().severity; }
|
||||||
static Type& GetType() { return GetInstance().type; }
|
static Type& GetType() { return GetInstance().type; }
|
||||||
static std::optional<u64>& GetLastPC() { return GetInstance().lastPC; }
|
static std::optional<s64>& GetLastPC() { return GetInstance().lastPC; }
|
||||||
static std::optional<MemoryAccess>& GetMemoryAccess() { return GetInstance().memoryAccess; }
|
static std::optional<MemoryAccess>& GetMemoryAccess() { return GetInstance().memoryAccess; }
|
||||||
static bool IsHandled() {
|
static bool IsHandled() {
|
||||||
return GetSeverity().as_enum == Severity::NONE;
|
return GetSeverity().as_enum == Severity::NONE;
|
||||||
@@ -145,7 +143,7 @@ private:
|
|||||||
std::string err;
|
std::string err;
|
||||||
Severity severity = {};
|
Severity severity = {};
|
||||||
Type type = {};
|
Type type = {};
|
||||||
std::optional<u64> lastPC = {};
|
std::optional<s64> lastPC = {};
|
||||||
std::optional<MemoryAccess> memoryAccess = {};
|
std::optional<MemoryAccess> memoryAccess = {};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user