JIT optimizations and refactoring (#675)
* CPU/Recompiler: Use rel32 call where possible for no-args * JitCodeBuffer: Support using preallocated buffer * CPU/Recompiler/AArch64: Use bl instead of blr for short branches * CPU/CodeCache: Allocate recompiler buffer in program space This means we don't need 64-bit moves for every call out of the recompiler. * GTE: Don't store as u16 and load as u32 * CPU/Recompiler: Add methods to emit global load/stores * GTE: Convert class to namespace * CPU/Recompiler: Call GTE functions directly * Settings: Turn into a global variable * GPU: Replace local pointers with global * InterruptController: Turn into a global pointer * System: Replace local pointers with global * Timers: Turn into a global instance * DMA: Turn into a global instance * SPU: Turn into a global instance * CDROM: Turn into a global instance * MDEC: Turn into a global instance * Pad: Turn into a global instance * SIO: Turn into a global instance * CDROM: Move audio FIFO to the heap * CPU/Recompiler: Drop ASMFunctions No longer needed since we have code in the same 4GB window. * CPUCodeCache: Turn class into namespace * Bus: Local pointer -> global pointers * CPU: Turn class into namespace * Bus: Turn into namespace * GTE: Store registers in CPU state struct Allows relative addressing on ARM. * CPU/Recompiler: Align code storage to page size * CPU/Recompiler: Fix relative branches on A64 * HostInterface: Local references to global * System: Turn into a namespace, move events out * Add guard pages * Android: Fix build
This commit is contained in:
parent
1f9fc6ab74
commit
b6f871d2b9
|
@ -9,9 +9,9 @@
|
|||
#include "core/gpu.h"
|
||||
#include "core/host_display.h"
|
||||
#include "core/system.h"
|
||||
#include "frontend-common/imgui_styles.h"
|
||||
#include "frontend-common/opengl_host_display.h"
|
||||
#include "frontend-common/vulkan_host_display.h"
|
||||
#include "frontend-common/imgui_styles.h"
|
||||
#include <android/native_window_jni.h>
|
||||
#include <cmath>
|
||||
#include <imgui.h>
|
||||
|
@ -242,27 +242,23 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf
|
|||
m_callback_mutex.unlock();
|
||||
|
||||
// simulate the system if not paused
|
||||
if (m_system && !m_paused)
|
||||
m_system->RunFrame();
|
||||
if (System::IsRunning())
|
||||
System::RunFrame();
|
||||
|
||||
// rendering
|
||||
{
|
||||
DrawImGuiWindows();
|
||||
|
||||
if (m_system)
|
||||
m_system->GetGPU()->ResetGraphicsAPIState();
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
|
||||
m_display->Render();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (m_system)
|
||||
{
|
||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||
m_system->UpdatePerformanceCounters();
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
System::UpdatePerformanceCounters();
|
||||
|
||||
if (m_speed_limiter_enabled)
|
||||
m_system->Throttle();
|
||||
}
|
||||
if (m_speed_limiter_enabled)
|
||||
System::Throttle();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +276,7 @@ bool AndroidHostInterface::AcquireHostDisplay()
|
|||
wi.surface_height = ANativeWindow_getHeight(m_surface);
|
||||
|
||||
std::unique_ptr<HostDisplay> display;
|
||||
switch (m_settings.gpu_renderer)
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
case GPURenderer::HardwareVulkan:
|
||||
display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
|
||||
|
@ -292,8 +288,8 @@ bool AndroidHostInterface::AcquireHostDisplay()
|
|||
break;
|
||||
}
|
||||
|
||||
if (!display->CreateRenderDevice(wi, {}, m_settings.gpu_use_debug_device) ||
|
||||
!display->InitializeRenderDevice(GetShaderCacheBasePath(), m_settings.gpu_use_debug_device))
|
||||
if (!display->CreateRenderDevice(wi, {}, g_settings.gpu_use_debug_device) ||
|
||||
!display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device))
|
||||
{
|
||||
ReportError("Failed to acquire host display.");
|
||||
return false;
|
||||
|
@ -363,15 +359,15 @@ void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_na
|
|||
|
||||
if (!IsEmulationThreadRunning())
|
||||
{
|
||||
m_settings.controller_types[index] = type;
|
||||
g_settings.controller_types[index] = type;
|
||||
return;
|
||||
}
|
||||
|
||||
RunOnEmulationThread(
|
||||
[this, index, type]() {
|
||||
Log_InfoPrintf("Changing controller slot %d to %s", index, Settings::GetControllerTypeName(type));
|
||||
m_settings.controller_types[index] = type;
|
||||
m_system->UpdateControllers();
|
||||
g_settings.controller_types[index] = type;
|
||||
System::UpdateControllers();
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
@ -383,7 +379,7 @@ void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code,
|
|||
|
||||
RunOnEmulationThread(
|
||||
[this, index, button_code, pressed]() {
|
||||
Controller* controller = m_system->GetController(index);
|
||||
Controller* controller = System::GetController(index);
|
||||
if (!controller)
|
||||
return;
|
||||
|
||||
|
@ -398,14 +394,14 @@ void AndroidHostInterface::SetControllerAxisState(u32 index, s32 button_code, fl
|
|||
return;
|
||||
|
||||
RunOnEmulationThread(
|
||||
[this, index, button_code, value]() {
|
||||
Controller* controller = m_system->GetController(index);
|
||||
if (!controller)
|
||||
return;
|
||||
[this, index, button_code, value]() {
|
||||
Controller* controller = System::GetController(index);
|
||||
if (!controller)
|
||||
return;
|
||||
|
||||
controller->SetAxisState(button_code, value);
|
||||
},
|
||||
false);
|
||||
controller->SetAxisState(button_code, value);
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
void AndroidHostInterface::RefreshGameList(bool invalidate_cache, bool invalidate_database)
|
||||
|
@ -416,7 +412,7 @@ void AndroidHostInterface::RefreshGameList(bool invalidate_cache, bool invalidat
|
|||
|
||||
void AndroidHostInterface::ApplySettings()
|
||||
{
|
||||
Settings old_settings = std::move(m_settings);
|
||||
Settings old_settings = std::move(g_settings);
|
||||
CommonHostInterface::LoadSettings(m_settings_interface);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
}
|
||||
|
@ -565,21 +561,23 @@ DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerAxisCode, jobject
|
|||
jstring axis_name)
|
||||
{
|
||||
std::optional<ControllerType> type =
|
||||
Settings::ParseControllerTypeName(AndroidHelpers::JStringToString(env, controller_type).c_str());
|
||||
Settings::ParseControllerTypeName(AndroidHelpers::JStringToString(env, controller_type).c_str());
|
||||
if (!type)
|
||||
return -1;
|
||||
|
||||
std::optional<s32> code =
|
||||
Controller::GetAxisCodeByName(type.value(), AndroidHelpers::JStringToString(env, axis_name));
|
||||
Controller::GetAxisCodeByName(type.value(), AndroidHelpers::JStringToString(env, axis_name));
|
||||
return code.value_or(-1);
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_refreshGameList, jobject obj, jboolean invalidate_cache, jboolean invalidate_database)
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_refreshGameList, jobject obj, jboolean invalidate_cache,
|
||||
jboolean invalidate_database)
|
||||
{
|
||||
AndroidHelpers::GetNativeClass(env, obj)->RefreshGameList(invalidate_cache, invalidate_database);
|
||||
}
|
||||
|
||||
static const char* DiscRegionToString(DiscRegion region) {
|
||||
static const char* DiscRegionToString(DiscRegion region)
|
||||
{
|
||||
static std::array<const char*, 4> names = {{"NTSC_J", "NTSC_U", "PAL", "Other"}};
|
||||
return names[static_cast<int>(region)];
|
||||
}
|
||||
|
@ -628,9 +626,7 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_applySettings, jobject obj)
|
|||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
if (hi->IsEmulationThreadRunning())
|
||||
{
|
||||
hi->RunOnEmulationThread([hi]() {
|
||||
hi->ApplySettings();
|
||||
});
|
||||
hi->RunOnEmulationThread([hi]() { hi->ApplySettings(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -641,23 +637,17 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_applySettings, jobject obj)
|
|||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_resetSystem, jobject obj, jboolean global, jint slot)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->RunOnEmulationThread([hi]() {
|
||||
hi->ResetSystem();
|
||||
});
|
||||
hi->RunOnEmulationThread([hi]() { hi->ResetSystem(); });
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_loadState, jobject obj, jboolean global, jint slot)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->RunOnEmulationThread([hi, global, slot]() {
|
||||
hi->LoadState(global, slot);
|
||||
});
|
||||
hi->RunOnEmulationThread([hi, global, slot]() { hi->LoadState(global, slot); });
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_saveState, jobject obj, jboolean global, jint slot)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->RunOnEmulationThread([hi, global, slot]() {
|
||||
hi->SaveState(global, slot);
|
||||
});
|
||||
hi->RunOnEmulationThread([hi, global, slot]() { hi->SaveState(global, slot); });
|
||||
}
|
||||
|
|
|
@ -10,8 +10,29 @@
|
|||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_size /* = 0 */)
|
||||
JitCodeBuffer::JitCodeBuffer() = default;
|
||||
|
||||
JitCodeBuffer::JitCodeBuffer(u32 size, u32 far_code_size)
|
||||
{
|
||||
if (!Allocate(size, far_code_size))
|
||||
Panic("Failed to allocate code space");
|
||||
}
|
||||
|
||||
JitCodeBuffer::JitCodeBuffer(void* buffer, u32 size, u32 far_code_size, u32 guard_pages)
|
||||
{
|
||||
if (!Initialize(buffer, size, far_code_size))
|
||||
Panic("Failed to initialize code space");
|
||||
}
|
||||
|
||||
JitCodeBuffer::~JitCodeBuffer()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool JitCodeBuffer::Allocate(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_size /* = 0 */)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
m_total_size = size + far_code_size;
|
||||
|
||||
#if defined(WIN32)
|
||||
|
@ -22,6 +43,10 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz
|
|||
#else
|
||||
m_code_ptr = nullptr;
|
||||
#endif
|
||||
|
||||
if (!m_code_ptr)
|
||||
return false;
|
||||
|
||||
m_free_code_ptr = m_code_ptr;
|
||||
m_code_size = size;
|
||||
m_code_used = 0;
|
||||
|
@ -31,17 +56,91 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz
|
|||
m_far_code_size = far_code_size;
|
||||
m_far_code_used = 0;
|
||||
|
||||
if (!m_code_ptr)
|
||||
Panic("Failed to allocate code space.");
|
||||
m_old_protection = 0;
|
||||
m_owns_buffer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
JitCodeBuffer::~JitCodeBuffer()
|
||||
bool JitCodeBuffer::Initialize(void* buffer, u32 size, u32 far_code_size /* = 0 */, u32 guard_size /* = 0 */)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
if ((far_code_size > 0 && guard_size >= far_code_size) || (far_code_size + (guard_size * 2)) > size)
|
||||
return false;
|
||||
|
||||
#if defined(WIN32)
|
||||
VirtualFree(m_code_ptr, 0, MEM_RELEASE);
|
||||
DWORD old_protect = 0;
|
||||
if (!VirtualProtect(buffer, size, PAGE_EXECUTE_READWRITE, &old_protect))
|
||||
return false;
|
||||
|
||||
if (guard_size > 0)
|
||||
{
|
||||
DWORD old_guard_protect = 0;
|
||||
u8* guard_at_end = (static_cast<u8*>(buffer) + size) - guard_size;
|
||||
if (!VirtualProtect(buffer, guard_size, PAGE_NOACCESS, &old_guard_protect) ||
|
||||
!VirtualProtect(guard_at_end, guard_size, PAGE_NOACCESS, &old_guard_protect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_code_ptr = static_cast<u8*>(buffer);
|
||||
m_old_protection = static_cast<u32>(old_protect);
|
||||
#elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
|
||||
munmap(m_code_ptr, m_total_size);
|
||||
if (mprotect(buffer, size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
|
||||
return false;
|
||||
|
||||
if (guard_size > 0)
|
||||
{
|
||||
u8* guard_at_end = (static_cast<u8*>(buffer) + size) - guard_size;
|
||||
if (mprotect(buffer, guard_size, PROT_NONE) != 0 || mprotect(guard_at_end, guard_size, PROT_NONE) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// reasonable default?
|
||||
m_code_ptr = static_cast<u8*>(buffer);
|
||||
m_old_protection = PROT_READ | PROT_WRITE;
|
||||
#else
|
||||
m_code_ptr = nullptr;
|
||||
#endif
|
||||
|
||||
if (!m_code_ptr)
|
||||
return false;
|
||||
|
||||
m_total_size = size;
|
||||
m_free_code_ptr = m_code_ptr + guard_size;
|
||||
m_code_size = size - far_code_size - (guard_size * 2);
|
||||
m_code_used = 0;
|
||||
|
||||
m_far_code_ptr = static_cast<u8*>(m_code_ptr) + m_code_size;
|
||||
m_free_far_code_ptr = m_far_code_ptr;
|
||||
m_far_code_size = far_code_size - guard_size;
|
||||
m_far_code_used = 0;
|
||||
|
||||
m_guard_size = guard_size;
|
||||
m_owns_buffer = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void JitCodeBuffer::Destroy()
|
||||
{
|
||||
if (m_owns_buffer)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
VirtualFree(m_code_ptr, 0, MEM_RELEASE);
|
||||
#elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
|
||||
munmap(m_code_ptr, m_total_size);
|
||||
#endif
|
||||
}
|
||||
else if (m_code_ptr)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
DWORD old_protect = 0;
|
||||
VirtualProtect(m_code_ptr, m_total_size, m_old_protection, &old_protect);
|
||||
#else
|
||||
mprotect(m_code_ptr, m_total_size, m_old_protection);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void JitCodeBuffer::CommitCode(u32 length)
|
||||
|
@ -76,18 +175,18 @@ void JitCodeBuffer::CommitFarCode(u32 length)
|
|||
|
||||
void JitCodeBuffer::Reset()
|
||||
{
|
||||
std::memset(m_code_ptr, 0, m_code_size);
|
||||
FlushInstructionCache(m_code_ptr, m_code_size);
|
||||
m_free_code_ptr = m_code_ptr + m_guard_size;
|
||||
m_code_used = 0;
|
||||
std::memset(m_free_code_ptr, 0, m_code_size);
|
||||
FlushInstructionCache(m_free_code_ptr, m_code_size);
|
||||
|
||||
if (m_far_code_size > 0)
|
||||
{
|
||||
std::memset(m_far_code_ptr, 0, m_far_code_size);
|
||||
FlushInstructionCache(m_far_code_ptr, m_far_code_size);
|
||||
m_free_far_code_ptr = m_far_code_ptr;
|
||||
m_far_code_used = 0;
|
||||
std::memset(m_free_far_code_ptr, 0, m_far_code_size);
|
||||
FlushInstructionCache(m_free_far_code_ptr, m_far_code_size);
|
||||
}
|
||||
m_free_code_ptr = m_code_ptr;
|
||||
m_code_used = 0;
|
||||
|
||||
m_free_far_code_ptr = m_far_code_ptr;
|
||||
m_far_code_used = 0;
|
||||
}
|
||||
|
||||
void JitCodeBuffer::Align(u32 alignment, u8 padding_value)
|
||||
|
|
|
@ -4,9 +4,14 @@
|
|||
class JitCodeBuffer
|
||||
{
|
||||
public:
|
||||
JitCodeBuffer(u32 size = 64 * 1024 * 1024, u32 far_code_size = 0);
|
||||
JitCodeBuffer();
|
||||
JitCodeBuffer(u32 size, u32 far_code_size);
|
||||
JitCodeBuffer(void* buffer, u32 size, u32 far_code_size, u32 guard_size);
|
||||
~JitCodeBuffer();
|
||||
|
||||
bool Allocate(u32 size = 64 * 1024 * 1024, u32 far_code_size = 0);
|
||||
bool Initialize(void* buffer, u32 size, u32 far_code_size = 0, u32 guard_size = 0);
|
||||
void Destroy();
|
||||
void Reset();
|
||||
|
||||
u8* GetFreeCodePointer() const { return m_free_code_ptr; }
|
||||
|
@ -25,16 +30,19 @@ public:
|
|||
static void FlushInstructionCache(void* address, u32 size);
|
||||
|
||||
private:
|
||||
u8* m_code_ptr;
|
||||
u8* m_free_code_ptr;
|
||||
u32 m_code_size;
|
||||
u32 m_code_used;
|
||||
u8* m_code_ptr = nullptr;
|
||||
u8* m_free_code_ptr = nullptr;
|
||||
u32 m_code_size = 0;
|
||||
u32 m_code_used = 0;
|
||||
|
||||
u8* m_far_code_ptr;
|
||||
u8* m_free_far_code_ptr;
|
||||
u32 m_far_code_size;
|
||||
u32 m_far_code_used;
|
||||
u8* m_far_code_ptr = nullptr;
|
||||
u8* m_free_far_code_ptr = nullptr;
|
||||
u32 m_far_code_size = 0;
|
||||
u32 m_far_code_used = 0;
|
||||
|
||||
u32 m_total_size;
|
||||
u32 m_total_size = 0;
|
||||
u32 m_guard_size = 0;
|
||||
u32 m_old_protection = 0;
|
||||
bool m_owns_buffer = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ add_library(core
|
|||
bios.h
|
||||
bus.cpp
|
||||
bus.h
|
||||
bus.inl
|
||||
cdrom.cpp
|
||||
cdrom.h
|
||||
cdrom_async_reader.cpp
|
||||
|
@ -16,7 +15,6 @@ add_library(core
|
|||
cpu_code_cache.h
|
||||
cpu_core.cpp
|
||||
cpu_core.h
|
||||
cpu_core.inl
|
||||
cpu_disasm.cpp
|
||||
cpu_disasm.h
|
||||
cpu_types.cpp
|
||||
|
@ -42,7 +40,6 @@ add_library(core
|
|||
gpu_sw.h
|
||||
gte.cpp
|
||||
gte.h
|
||||
gte.inl
|
||||
gte_types.h
|
||||
host_display.cpp
|
||||
host_display.h
|
||||
|
@ -90,7 +87,6 @@ set(RECOMPILER_SRCS
|
|||
cpu_recompiler_code_generator_generic.cpp
|
||||
cpu_recompiler_register_cache.cpp
|
||||
cpu_recompiler_register_cache.h
|
||||
cpu_recompiler_thunks.cpp
|
||||
cpu_recompiler_thunks.h
|
||||
cpu_recompiler_types.h
|
||||
)
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
#include "system.h"
|
||||
Log_SetChannel(AnalogController);
|
||||
|
||||
AnalogController::AnalogController(System* system, u32 index) : m_system(system), m_index(index)
|
||||
AnalogController::AnalogController(u32 index) : m_index(index)
|
||||
{
|
||||
m_axis_state.fill(0x80);
|
||||
Reset();
|
||||
}
|
||||
|
||||
AnalogController::~AnalogController() = default;
|
||||
|
@ -52,8 +53,8 @@ bool AnalogController::DoState(StateWrapper& sw)
|
|||
|
||||
if (old_analog_mode != m_analog_mode)
|
||||
{
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(2.0f, "Controller %u switched to %s mode.", m_index + 1u,
|
||||
m_analog_mode ? "analog" : "digital");
|
||||
g_host_interface->AddFormattedOSDMessage(2.0f, "Controller %u switched to %s mode.", m_index + 1u,
|
||||
m_analog_mode ? "analog" : "digital");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -94,8 +95,8 @@ void AnalogController::SetButtonState(Button button, bool pressed)
|
|||
{
|
||||
if (m_analog_locked)
|
||||
{
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(2.0f, "Controller %u is locked to %s mode by the game.",
|
||||
m_index + 1u, m_analog_mode ? "analog" : "digital");
|
||||
g_host_interface->AddFormattedOSDMessage(2.0f, "Controller %u is locked to %s mode by the game.", m_index + 1u,
|
||||
m_analog_mode ? "analog" : "digital");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -154,8 +155,8 @@ void AnalogController::SetAnalogMode(bool enabled)
|
|||
return;
|
||||
|
||||
Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital");
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(2.0f, "Controller %u switched to %s mode.", m_index + 1u,
|
||||
enabled ? "analog" : "digital");
|
||||
g_host_interface->AddFormattedOSDMessage(2.0f, "Controller %u switched to %s mode.", m_index + 1u,
|
||||
enabled ? "analog" : "digital");
|
||||
m_analog_mode = enabled;
|
||||
}
|
||||
|
||||
|
@ -397,9 +398,9 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
|
|||
return ack;
|
||||
}
|
||||
|
||||
std::unique_ptr<AnalogController> AnalogController::Create(System* system, u32 index)
|
||||
std::unique_ptr<AnalogController> AnalogController::Create(u32 index)
|
||||
{
|
||||
return std::make_unique<AnalogController>(system, index);
|
||||
return std::make_unique<AnalogController>(index);
|
||||
}
|
||||
|
||||
std::optional<s32> AnalogController::StaticGetAxisCodeByName(std::string_view axis_name)
|
||||
|
@ -492,8 +493,8 @@ Controller::SettingList AnalogController::StaticGetSettings()
|
|||
return SettingList(settings.begin(), settings.end());
|
||||
}
|
||||
|
||||
void AnalogController::LoadSettings(HostInterface* host_interface, const char* section)
|
||||
void AnalogController::LoadSettings(const char* section)
|
||||
{
|
||||
Controller::LoadSettings(host_interface, section);
|
||||
m_auto_enable_analog = host_interface->GetBoolSettingValue(section, "AutoEnableAnalog", false);
|
||||
Controller::LoadSettings(section);
|
||||
m_auto_enable_analog = g_host_interface->GetBoolSettingValue(section, "AutoEnableAnalog", false);
|
||||
}
|
||||
|
|
|
@ -41,10 +41,10 @@ public:
|
|||
|
||||
static constexpr u8 NUM_MOTORS = 2;
|
||||
|
||||
AnalogController(System* system, u32 index);
|
||||
AnalogController(u32 index);
|
||||
~AnalogController() override;
|
||||
|
||||
static std::unique_ptr<AnalogController> Create(System* system, u32 index);
|
||||
static std::unique_ptr<AnalogController> Create(u32 index);
|
||||
static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name);
|
||||
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
|
||||
static AxisList StaticGetAxisNames();
|
||||
|
@ -71,7 +71,7 @@ public:
|
|||
u32 GetVibrationMotorCount() const override;
|
||||
float GetVibrationMotorStrength(u32 motor) override;
|
||||
|
||||
void LoadSettings(HostInterface* host_interface, const char* section) override;
|
||||
void LoadSettings(const char* section) override;
|
||||
|
||||
private:
|
||||
using MotorState = std::array<u8, NUM_MOTORS>;
|
||||
|
@ -132,7 +132,6 @@ private:
|
|||
void SetAnalogMode(bool enabled);
|
||||
void SetMotorState(u8 motor, u8 value);
|
||||
|
||||
System* m_system;
|
||||
u32 m_index;
|
||||
|
||||
bool m_auto_enable_analog = false;
|
||||
|
|
1470
src/core/bus.cpp
1470
src/core/bus.cpp
File diff suppressed because it is too large
Load Diff
414
src/core/bus.h
414
src/core/bus.h
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "common/bitfield.h"
|
||||
#include "cpu_code_cache.h"
|
||||
#include "types.h"
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
|
@ -8,288 +9,139 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
namespace CPU {
|
||||
class Core;
|
||||
class CodeCache;
|
||||
} // namespace CPU
|
||||
namespace Bus {
|
||||
|
||||
class DMA;
|
||||
class InterruptController;
|
||||
class GPU;
|
||||
class CDROM;
|
||||
class Pad;
|
||||
class Timers;
|
||||
class SPU;
|
||||
class MDEC;
|
||||
class SIO;
|
||||
class System;
|
||||
|
||||
class Bus
|
||||
enum : u32
|
||||
{
|
||||
friend DMA;
|
||||
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
RAM_BASE = 0x00000000,
|
||||
RAM_SIZE = 0x200000,
|
||||
RAM_MASK = RAM_SIZE - 1,
|
||||
RAM_MIRROR_END = 0x800000,
|
||||
EXP1_BASE = 0x1F000000,
|
||||
EXP1_SIZE = 0x800000,
|
||||
EXP1_MASK = EXP1_SIZE - 1,
|
||||
MEMCTRL_BASE = 0x1F801000,
|
||||
MEMCTRL_SIZE = 0x40,
|
||||
MEMCTRL_MASK = MEMCTRL_SIZE - 1,
|
||||
PAD_BASE = 0x1F801040,
|
||||
PAD_SIZE = 0x10,
|
||||
PAD_MASK = PAD_SIZE - 1,
|
||||
SIO_BASE = 0x1F801050,
|
||||
SIO_SIZE = 0x10,
|
||||
SIO_MASK = SIO_SIZE - 1,
|
||||
MEMCTRL2_BASE = 0x1F801060,
|
||||
MEMCTRL2_SIZE = 0x10,
|
||||
MEMCTRL2_MASK = MEMCTRL_SIZE - 1,
|
||||
INTERRUPT_CONTROLLER_BASE = 0x1F801070,
|
||||
INTERRUPT_CONTROLLER_SIZE = 0x10,
|
||||
INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1,
|
||||
DMA_BASE = 0x1F801080,
|
||||
DMA_SIZE = 0x80,
|
||||
DMA_MASK = DMA_SIZE - 1,
|
||||
TIMERS_BASE = 0x1F801100,
|
||||
TIMERS_SIZE = 0x40,
|
||||
TIMERS_MASK = TIMERS_SIZE - 1,
|
||||
CDROM_BASE = 0x1F801800,
|
||||
CDROM_SIZE = 0x10,
|
||||
CDROM_MASK = CDROM_SIZE - 1,
|
||||
GPU_BASE = 0x1F801810,
|
||||
GPU_SIZE = 0x10,
|
||||
GPU_MASK = GPU_SIZE - 1,
|
||||
MDEC_BASE = 0x1F801820,
|
||||
MDEC_SIZE = 0x10,
|
||||
MDEC_MASK = MDEC_SIZE - 1,
|
||||
SPU_BASE = 0x1F801C00,
|
||||
SPU_SIZE = 0x400,
|
||||
SPU_MASK = 0x3FF,
|
||||
EXP2_BASE = 0x1F802000,
|
||||
EXP2_SIZE = 0x2000,
|
||||
EXP2_MASK = EXP2_SIZE - 1,
|
||||
BIOS_BASE = 0x1FC00000,
|
||||
BIOS_SIZE = 0x80000
|
||||
};
|
||||
|
||||
Bus();
|
||||
~Bus();
|
||||
|
||||
void Initialize(CPU::Core* cpu, CPU::CodeCache* cpu_code_cache, DMA* dma, InterruptController* interrupt_controller,
|
||||
GPU* gpu, CDROM* cdrom, Pad* pad, Timers* timers, SPU* spu, MDEC* mdec, SIO* sio);
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
bool ReadByte(PhysicalMemoryAddress address, u8* value);
|
||||
bool ReadHalfWord(PhysicalMemoryAddress address, u16* value);
|
||||
bool ReadWord(PhysicalMemoryAddress address, u32* value);
|
||||
bool WriteByte(PhysicalMemoryAddress address, u8 value);
|
||||
bool WriteHalfWord(PhysicalMemoryAddress address, u16 value);
|
||||
bool WriteWord(PhysicalMemoryAddress address, u32 value);
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount DispatchAccess(PhysicalMemoryAddress address, u32& value);
|
||||
|
||||
// Optimized variant for burst/multi-word read/writing.
|
||||
TickCount ReadWords(PhysicalMemoryAddress address, u32* words, u32 word_count);
|
||||
TickCount WriteWords(PhysicalMemoryAddress address, const u32* words, u32 word_count);
|
||||
|
||||
void SetExpansionROM(std::vector<u8> data);
|
||||
void SetBIOS(const std::vector<u8>& image);
|
||||
|
||||
// changing interfaces
|
||||
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
||||
|
||||
/// Returns the address which should be used for code caching (i.e. removes mirrors).
|
||||
ALWAYS_INLINE static PhysicalMemoryAddress UnmirrorAddress(PhysicalMemoryAddress address)
|
||||
{
|
||||
// RAM
|
||||
if (address < 0x800000)
|
||||
return address & UINT32_C(0x1FFFFF);
|
||||
else
|
||||
return address;
|
||||
}
|
||||
|
||||
/// Returns true if the address specified is cacheable (RAM or BIOS).
|
||||
ALWAYS_INLINE static bool IsCacheableAddress(PhysicalMemoryAddress address)
|
||||
{
|
||||
return (address < RAM_MIRROR_END) || (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE));
|
||||
}
|
||||
|
||||
/// Returns true if the address specified is writable (RAM).
|
||||
ALWAYS_INLINE static bool IsRAMAddress(PhysicalMemoryAddress address) { return address < RAM_MIRROR_END; }
|
||||
|
||||
/// Flags a RAM region as code, so we know when to invalidate blocks.
|
||||
ALWAYS_INLINE void SetRAMCodePage(u32 index) { m_ram_code_bits[index] = true; }
|
||||
|
||||
/// Unflags a RAM region as code, the code cache will no longer be notified when writes occur.
|
||||
ALWAYS_INLINE void ClearRAMCodePage(u32 index) { m_ram_code_bits[index] = false; }
|
||||
|
||||
/// Clears all code bits for RAM regions.
|
||||
ALWAYS_INLINE void ClearRAMCodePageFlags() { m_ram_code_bits.reset(); }
|
||||
|
||||
/// Direct access to RAM - used by DMA.
|
||||
ALWAYS_INLINE u8* GetRAM() { return m_ram; }
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
MEMCTRL_REG_COUNT = 9
|
||||
};
|
||||
|
||||
union MEMDELAY
|
||||
{
|
||||
u32 bits;
|
||||
|
||||
BitField<u32, u8, 4, 4> access_time; // cycles
|
||||
BitField<u32, bool, 8, 1> use_com0_time;
|
||||
BitField<u32, bool, 9, 1> use_com1_time;
|
||||
BitField<u32, bool, 10, 1> use_com2_time;
|
||||
BitField<u32, bool, 11, 1> use_com3_time;
|
||||
BitField<u32, bool, 12, 1> data_bus_16bit;
|
||||
BitField<u32, u8, 16, 5> memory_window_size;
|
||||
|
||||
static constexpr u32 WRITE_MASK = 0b10101111'00011111'11111111'11111111;
|
||||
};
|
||||
|
||||
union COMDELAY
|
||||
{
|
||||
u32 bits;
|
||||
|
||||
BitField<u32, u8, 0, 4> com0;
|
||||
BitField<u32, u8, 4, 4> com1;
|
||||
BitField<u32, u8, 8, 4> com2;
|
||||
BitField<u32, u8, 12, 4> com3;
|
||||
BitField<u32, u8, 16, 2> comunk;
|
||||
|
||||
static constexpr u32 WRITE_MASK = 0b00000000'00000011'11111111'11111111;
|
||||
};
|
||||
|
||||
union MEMCTRL
|
||||
{
|
||||
u32 regs[MEMCTRL_REG_COUNT];
|
||||
|
||||
struct
|
||||
{
|
||||
u32 exp1_base;
|
||||
u32 exp2_base;
|
||||
MEMDELAY exp1_delay_size;
|
||||
MEMDELAY exp3_delay_size;
|
||||
MEMDELAY bios_delay_size;
|
||||
MEMDELAY spu_delay_size;
|
||||
MEMDELAY cdrom_delay_size;
|
||||
MEMDELAY exp2_delay_size;
|
||||
COMDELAY common_delay;
|
||||
};
|
||||
};
|
||||
|
||||
static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
|
||||
void RecalculateMemoryTimings();
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount DoRAMAccess(u32 offset, u32& value);
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount DoBIOSAccess(u32 offset, u32& value);
|
||||
|
||||
TickCount DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress address, u32& value);
|
||||
|
||||
u32 DoReadEXP1(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteEXP1(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadEXP2(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadMemoryControl(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadMemoryControl2(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteMemoryControl2(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadPad(MemoryAccessSize size, u32 offset);
|
||||
void DoWritePad(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadSIO(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteSIO(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadCDROM(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadGPU(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadMDEC(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadInterruptController(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadDMA(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadTimers(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
u32 DoReadSPU(MemoryAccessSize size, u32 offset);
|
||||
void DoWriteSPU(MemoryAccessSize size, u32 offset, u32 value);
|
||||
|
||||
void DoInvalidateCodeCache(u32 page_index);
|
||||
|
||||
/// Returns the number of cycles stolen by DMA RAM access.
|
||||
ALWAYS_INLINE static TickCount GetDMARAMTickCount(u32 word_count)
|
||||
{
|
||||
// DMA is using DRAM Hyper Page mode, allowing it to access DRAM rows at 1 clock cycle per word (effectively around
|
||||
// 17 clks per 16 words, due to required row address loading, probably plus some further minimal overload due to
|
||||
// refresh cycles). This is making DMA much faster than CPU memory accesses (CPU DRAM access takes 1 opcode cycle
|
||||
// plus 6 waitstates, ie. 7 cycles in total).
|
||||
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
|
||||
}
|
||||
|
||||
/// Invalidates any code pages which overlap the specified range.
|
||||
ALWAYS_INLINE void InvalidateCodePages(PhysicalMemoryAddress address, u32 word_count)
|
||||
{
|
||||
const u32 start_page = address / CPU_CODE_CACHE_PAGE_SIZE;
|
||||
const u32 end_page = (address + word_count * sizeof(u32)) / CPU_CODE_CACHE_PAGE_SIZE;
|
||||
for (u32 page = start_page; page <= end_page; page++)
|
||||
{
|
||||
if (m_ram_code_bits[page])
|
||||
DoInvalidateCodeCache(page);
|
||||
}
|
||||
}
|
||||
|
||||
CPU::Core* m_cpu = nullptr;
|
||||
CPU::CodeCache* m_cpu_code_cache = nullptr;
|
||||
DMA* m_dma = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
GPU* m_gpu = nullptr;
|
||||
CDROM* m_cdrom = nullptr;
|
||||
Pad* m_pad = nullptr;
|
||||
Timers* m_timers = nullptr;
|
||||
SPU* m_spu = nullptr;
|
||||
MDEC* m_mdec = nullptr;
|
||||
SIO* m_sio = nullptr;
|
||||
|
||||
std::array<TickCount, 3> m_exp1_access_time = {};
|
||||
std::array<TickCount, 3> m_exp2_access_time = {};
|
||||
std::array<TickCount, 3> m_bios_access_time = {};
|
||||
std::array<TickCount, 3> m_cdrom_access_time = {};
|
||||
std::array<TickCount, 3> m_spu_access_time = {};
|
||||
|
||||
std::bitset<CPU_CODE_CACHE_PAGE_COUNT> m_ram_code_bits{};
|
||||
u8 m_ram[RAM_SIZE]{}; // 2MB RAM
|
||||
u8 m_bios[BIOS_SIZE]{}; // 512K BIOS ROM
|
||||
std::vector<u8> m_exp1_rom;
|
||||
|
||||
MEMCTRL m_MEMCTRL = {};
|
||||
u32 m_ram_size_reg = 0;
|
||||
|
||||
std::string m_tty_line_buffer;
|
||||
RAM_BASE = 0x00000000,
|
||||
RAM_SIZE = 0x200000,
|
||||
RAM_MASK = RAM_SIZE - 1,
|
||||
RAM_MIRROR_END = 0x800000,
|
||||
EXP1_BASE = 0x1F000000,
|
||||
EXP1_SIZE = 0x800000,
|
||||
EXP1_MASK = EXP1_SIZE - 1,
|
||||
MEMCTRL_BASE = 0x1F801000,
|
||||
MEMCTRL_SIZE = 0x40,
|
||||
MEMCTRL_MASK = MEMCTRL_SIZE - 1,
|
||||
PAD_BASE = 0x1F801040,
|
||||
PAD_SIZE = 0x10,
|
||||
PAD_MASK = PAD_SIZE - 1,
|
||||
SIO_BASE = 0x1F801050,
|
||||
SIO_SIZE = 0x10,
|
||||
SIO_MASK = SIO_SIZE - 1,
|
||||
MEMCTRL2_BASE = 0x1F801060,
|
||||
MEMCTRL2_SIZE = 0x10,
|
||||
MEMCTRL2_MASK = MEMCTRL2_SIZE - 1,
|
||||
INTERRUPT_CONTROLLER_BASE = 0x1F801070,
|
||||
INTERRUPT_CONTROLLER_SIZE = 0x10,
|
||||
INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1,
|
||||
DMA_BASE = 0x1F801080,
|
||||
DMA_SIZE = 0x80,
|
||||
DMA_MASK = DMA_SIZE - 1,
|
||||
TIMERS_BASE = 0x1F801100,
|
||||
TIMERS_SIZE = 0x40,
|
||||
TIMERS_MASK = TIMERS_SIZE - 1,
|
||||
CDROM_BASE = 0x1F801800,
|
||||
CDROM_SIZE = 0x10,
|
||||
CDROM_MASK = CDROM_SIZE - 1,
|
||||
GPU_BASE = 0x1F801810,
|
||||
GPU_SIZE = 0x10,
|
||||
GPU_MASK = GPU_SIZE - 1,
|
||||
MDEC_BASE = 0x1F801820,
|
||||
MDEC_SIZE = 0x10,
|
||||
MDEC_MASK = MDEC_SIZE - 1,
|
||||
SPU_BASE = 0x1F801C00,
|
||||
SPU_SIZE = 0x400,
|
||||
SPU_MASK = 0x3FF,
|
||||
EXP2_BASE = 0x1F802000,
|
||||
EXP2_SIZE = 0x2000,
|
||||
EXP2_MASK = EXP2_SIZE - 1,
|
||||
BIOS_BASE = 0x1FC00000,
|
||||
BIOS_SIZE = 0x80000,
|
||||
BIOS_MASK = 0x7FFFF,
|
||||
};
|
||||
|
||||
#include "bus.inl"
|
||||
enum : u32
|
||||
{
|
||||
MEMCTRL_REG_COUNT = 9
|
||||
};
|
||||
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
void SetExpansionROM(std::vector<u8> data);
|
||||
void SetBIOS(const std::vector<u8>& image);
|
||||
|
||||
extern std::bitset<CPU_CODE_CACHE_PAGE_COUNT> m_ram_code_bits;
|
||||
extern u8 g_ram[RAM_SIZE]; // 2MB RAM
|
||||
extern u8 g_bios[BIOS_SIZE]; // 512K BIOS ROM
|
||||
|
||||
/// Returns the address which should be used for code caching (i.e. removes mirrors).
|
||||
ALWAYS_INLINE PhysicalMemoryAddress UnmirrorAddress(PhysicalMemoryAddress address)
|
||||
{
|
||||
// RAM
|
||||
if (address < 0x800000)
|
||||
return address & UINT32_C(0x1FFFFF);
|
||||
else
|
||||
return address;
|
||||
}
|
||||
|
||||
/// Returns true if the address specified is cacheable (RAM or BIOS).
|
||||
ALWAYS_INLINE bool IsCacheableAddress(PhysicalMemoryAddress address)
|
||||
{
|
||||
return (address < RAM_MIRROR_END) || (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE));
|
||||
}
|
||||
|
||||
/// Reads a cachable address (RAM or BIOS).
|
||||
ALWAYS_INLINE u32 ReadCacheableAddress(PhysicalMemoryAddress address)
|
||||
{
|
||||
u32 value;
|
||||
if (address < RAM_MIRROR_END)
|
||||
{
|
||||
std::memcpy(&value, &g_ram[address & RAM_MASK], sizeof(value));
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(&value, &g_bios[address & BIOS_MASK], sizeof(value));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the address specified is writable (RAM).
|
||||
ALWAYS_INLINE bool IsRAMAddress(PhysicalMemoryAddress address) { return address < RAM_MIRROR_END; }
|
||||
|
||||
/// Flags a RAM region as code, so we know when to invalidate blocks.
|
||||
ALWAYS_INLINE void SetRAMCodePage(u32 index) { m_ram_code_bits[index] = true; }
|
||||
|
||||
/// Unflags a RAM region as code, the code cache will no longer be notified when writes occur.
|
||||
ALWAYS_INLINE void ClearRAMCodePage(u32 index) { m_ram_code_bits[index] = false; }
|
||||
|
||||
/// Clears all code bits for RAM regions.
|
||||
ALWAYS_INLINE void ClearRAMCodePageFlags() { m_ram_code_bits.reset(); }
|
||||
|
||||
/// Returns the number of cycles stolen by DMA RAM access.
|
||||
ALWAYS_INLINE TickCount GetDMARAMTickCount(u32 word_count)
|
||||
{
|
||||
// DMA is using DRAM Hyper Page mode, allowing it to access DRAM rows at 1 clock cycle per word (effectively around
|
||||
// 17 clks per 16 words, due to required row address loading, probably plus some further minimal overload due to
|
||||
// refresh cycles). This is making DMA much faster than CPU memory accesses (CPU DRAM access takes 1 opcode cycle
|
||||
// plus 6 waitstates, ie. 7 cycles in total).
|
||||
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
|
||||
}
|
||||
|
||||
/// Invalidates any code pages which overlap the specified range.
|
||||
ALWAYS_INLINE void InvalidateCodePages(PhysicalMemoryAddress address, u32 word_count)
|
||||
{
|
||||
const u32 start_page = address / CPU_CODE_CACHE_PAGE_SIZE;
|
||||
const u32 end_page = (address + word_count * sizeof(u32)) / CPU_CODE_CACHE_PAGE_SIZE;
|
||||
for (u32 page = start_page; page <= end_page; page++)
|
||||
{
|
||||
if (m_ram_code_bits[page])
|
||||
CPU::CodeCache::InvalidateBlocksWithPageIndex(page);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Bus
|
||||
|
|
288
src/core/bus.inl
288
src/core/bus.inl
|
@ -1,288 +0,0 @@
|
|||
#pragma once
|
||||
#include "bus.h"
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount Bus::DoRAMAccess(u32 offset, u32& value)
|
||||
{
|
||||
// TODO: Configurable mirroring.
|
||||
offset &= UINT32_C(0x1FFFFF);
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
if constexpr (size == MemoryAccessSize::Byte)
|
||||
{
|
||||
value = ZeroExtend32(m_ram[offset]);
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::HalfWord)
|
||||
{
|
||||
u16 temp;
|
||||
std::memcpy(&temp, &m_ram[offset], sizeof(u16));
|
||||
value = ZeroExtend32(temp);
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::Word)
|
||||
{
|
||||
std::memcpy(&value, &m_ram[offset], sizeof(u32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 page_index = offset / CPU_CODE_CACHE_PAGE_SIZE;
|
||||
if (m_ram_code_bits[page_index])
|
||||
DoInvalidateCodeCache(page_index);
|
||||
|
||||
if constexpr (size == MemoryAccessSize::Byte)
|
||||
{
|
||||
m_ram[offset] = Truncate8(value);
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::HalfWord)
|
||||
{
|
||||
const u16 temp = Truncate16(value);
|
||||
std::memcpy(&m_ram[offset], &temp, sizeof(u16));
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::Word)
|
||||
{
|
||||
std::memcpy(&m_ram[offset], &value, sizeof(u32));
|
||||
}
|
||||
}
|
||||
|
||||
return (type == MemoryAccessType::Read) ? 4 : 0;
|
||||
}
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount Bus::DoBIOSAccess(u32 offset, u32& value)
|
||||
{
|
||||
// TODO: Configurable mirroring.
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
offset &= UINT32_C(0x7FFFF);
|
||||
if constexpr (size == MemoryAccessSize::Byte)
|
||||
{
|
||||
value = ZeroExtend32(m_bios[offset]);
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::HalfWord)
|
||||
{
|
||||
u16 temp;
|
||||
std::memcpy(&temp, &m_bios[offset], sizeof(u16));
|
||||
value = ZeroExtend32(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(&value, &m_bios[offset], sizeof(u32));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Writes are ignored.
|
||||
}
|
||||
|
||||
return m_bios_access_time[static_cast<u32>(size)];
|
||||
}
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount Bus::DispatchAccess(PhysicalMemoryAddress address, u32& value)
|
||||
{
|
||||
if (address < 0x800000)
|
||||
{
|
||||
return DoRAMAccess<type, size>(address, value);
|
||||
}
|
||||
else if (address < EXP1_BASE)
|
||||
{
|
||||
return DoInvalidAccess(type, size, address, value);
|
||||
}
|
||||
else if (address < (EXP1_BASE + EXP1_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadEXP1(size, address & EXP1_MASK);
|
||||
return m_exp1_access_time[static_cast<u32>(size)];
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteEXP1(size, address & EXP1_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < MEMCTRL_BASE)
|
||||
{
|
||||
return DoInvalidAccess(type, size, address, value);
|
||||
}
|
||||
else if (address < (MEMCTRL_BASE + MEMCTRL_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadMemoryControl(size, address & PAD_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteMemoryControl(size, address & PAD_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (PAD_BASE + PAD_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadPad(size, address & PAD_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWritePad(size, address & PAD_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (SIO_BASE + SIO_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadSIO(size, address & SIO_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteSIO(size, address & SIO_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (MEMCTRL2_BASE + MEMCTRL2_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadMemoryControl2(size, address & PAD_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteMemoryControl2(size, address & PAD_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (INTERRUPT_CONTROLLER_BASE + INTERRUPT_CONTROLLER_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadInterruptController(size, address & INTERRUPT_CONTROLLER_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteInterruptController(size, address & INTERRUPT_CONTROLLER_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (DMA_BASE + DMA_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadDMA(size, address & DMA_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteDMA(size, address & DMA_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (TIMERS_BASE + TIMERS_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadTimers(size, address & TIMERS_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteTimers(size, address & TIMERS_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < CDROM_BASE)
|
||||
{
|
||||
return DoInvalidAccess(type, size, address, value);
|
||||
}
|
||||
else if (address < (CDROM_BASE + GPU_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadCDROM(size, address & CDROM_MASK);
|
||||
return m_cdrom_access_time[static_cast<u32>(size)];
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteCDROM(size, address & CDROM_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (GPU_BASE + GPU_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadGPU(size, address & GPU_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteGPU(size, address & GPU_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < (MDEC_BASE + MDEC_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadMDEC(size, address & MDEC_MASK);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteMDEC(size, address & MDEC_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < SPU_BASE)
|
||||
{
|
||||
return DoInvalidAccess(type, size, address, value);
|
||||
}
|
||||
else if (address < (SPU_BASE + SPU_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadSPU(size, address & SPU_MASK);
|
||||
return m_spu_access_time[static_cast<u32>(size)];
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteSPU(size, address & SPU_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < EXP2_BASE)
|
||||
{
|
||||
return DoInvalidAccess(type, size, address, value);
|
||||
}
|
||||
else if (address < (EXP2_BASE + EXP2_SIZE))
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
value = DoReadEXP2(size, address & EXP2_MASK);
|
||||
return m_exp2_access_time[static_cast<u32>(size)];
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteEXP2(size, address & EXP2_MASK, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (address < BIOS_BASE)
|
||||
{
|
||||
return DoInvalidAccess(type, size, address, value);
|
||||
}
|
||||
else if (address < (BIOS_BASE + BIOS_SIZE))
|
||||
{
|
||||
return DoBIOSAccess<type, size>(static_cast<u32>(address - BIOS_BASE), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DoInvalidAccess(type, size, address, value);
|
||||
}
|
||||
}
|
|
@ -275,23 +275,31 @@ static std::array<CommandInfo, 255> s_command_info = {{
|
|||
0 // Unknown
|
||||
}};
|
||||
|
||||
CDROM g_cdrom;
|
||||
|
||||
CDROM::CDROM() = default;
|
||||
|
||||
CDROM::~CDROM() = default;
|
||||
|
||||
void CDROM::Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, SPU* spu)
|
||||
void CDROM::Initialize()
|
||||
{
|
||||
m_system = system;
|
||||
m_dma = dma;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_spu = spu;
|
||||
m_command_event =
|
||||
m_system->CreateTimingEvent("CDROM Command Event", 1, 1, std::bind(&CDROM::ExecuteCommand, this), false);
|
||||
m_drive_event = m_system->CreateTimingEvent("CDROM Drive Event", 1, 1,
|
||||
std::bind(&CDROM::ExecuteDrive, this, std::placeholders::_2), false);
|
||||
TimingEvents::CreateTimingEvent("CDROM Command Event", 1, 1, std::bind(&CDROM::ExecuteCommand, this), false);
|
||||
m_drive_event = TimingEvents::CreateTimingEvent("CDROM Drive Event", 1, 1,
|
||||
std::bind(&CDROM::ExecuteDrive, this, std::placeholders::_2), false);
|
||||
|
||||
if (m_system->GetSettings().cdrom_read_thread)
|
||||
if (g_settings.cdrom_read_thread)
|
||||
m_reader.StartThread();
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void CDROM::Shutdown()
|
||||
{
|
||||
m_command_event.reset();
|
||||
m_drive_event.reset();
|
||||
m_reader.StopThread();
|
||||
m_reader.RemoveMedia();
|
||||
}
|
||||
|
||||
void CDROM::Reset()
|
||||
|
@ -438,7 +446,7 @@ void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
|||
// set the region from the system area of the disc
|
||||
m_disc_region = GameList::GetRegionForImage(media.get());
|
||||
Log_InfoPrintf("Inserting new media, disc region: %s, console region: %s", Settings::GetDiscRegionName(m_disc_region),
|
||||
Settings::GetConsoleRegionName(m_system->GetRegion()));
|
||||
Settings::GetConsoleRegionName(System::GetRegion()));
|
||||
|
||||
// motor automatically spins up
|
||||
if (m_drive_state != DriveState::ShellOpening)
|
||||
|
@ -787,7 +795,7 @@ void CDROM::UpdateStatusRegister()
|
|||
m_status.DRQSTS = !m_data_fifo.IsEmpty();
|
||||
m_status.BUSYSTS = HasPendingCommand();
|
||||
|
||||
m_dma->SetRequest(DMA::Channel::CDROM, m_status.DRQSTS);
|
||||
g_dma.SetRequest(DMA::Channel::CDROM, m_status.DRQSTS);
|
||||
}
|
||||
|
||||
void CDROM::UpdateInterruptRequest()
|
||||
|
@ -795,7 +803,7 @@ void CDROM::UpdateInterruptRequest()
|
|||
if ((m_interrupt_flag_register & m_interrupt_enable_register) == 0)
|
||||
return;
|
||||
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::CDROM);
|
||||
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::CDROM);
|
||||
}
|
||||
|
||||
TickCount CDROM::GetAckDelayForCommand(Command command)
|
||||
|
@ -1421,7 +1429,7 @@ void CDROM::ExecuteTestCommand(u8 subcommand)
|
|||
{
|
||||
Log_DebugPrintf("Get CDROM region ID string");
|
||||
|
||||
switch (m_system->GetRegion())
|
||||
switch (System::GetRegion())
|
||||
{
|
||||
case ConsoleRegion::NTSC_J:
|
||||
{
|
||||
|
@ -1860,9 +1868,9 @@ void CDROM::DoIDRead()
|
|||
m_current_lba = 0;
|
||||
m_reader.QueueReadSector(0);
|
||||
|
||||
if (m_system->GetSettings().cdrom_region_check &&
|
||||
if (g_settings.cdrom_region_check &&
|
||||
(m_disc_region == DiscRegion::Other ||
|
||||
m_system->GetRegion() != System::GetConsoleRegionForDiscRegion(m_disc_region)))
|
||||
System::GetRegion() != System::GetConsoleRegionForDiscRegion(m_disc_region)))
|
||||
{
|
||||
stat_byte |= STAT_ID_ERROR;
|
||||
flags_byte |= (1 << 7); // Unlicensed
|
||||
|
@ -2214,7 +2222,7 @@ void CDROM::ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannel
|
|||
if (m_muted || m_adpcm_muted)
|
||||
return;
|
||||
|
||||
m_spu->GeneratePendingSamples();
|
||||
g_spu.GeneratePendingSamples();
|
||||
|
||||
if (m_last_sector_subheader.codinginfo.IsStereo())
|
||||
{
|
||||
|
@ -2282,7 +2290,7 @@ void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ&
|
|||
if (m_muted)
|
||||
return;
|
||||
|
||||
m_spu->GeneratePendingSamples();
|
||||
g_spu.GeneratePendingSamples();
|
||||
|
||||
constexpr bool is_stereo = true;
|
||||
constexpr u32 num_samples = CDImage::RAW_SECTOR_SIZE / sizeof(s16) / (is_stereo ? 2 : 1);
|
||||
|
@ -2347,7 +2355,7 @@ void CDROM::DrawDebugWindow()
|
|||
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("CDROM State", &m_system->GetSettings().debugging.show_cdrom_state))
|
||||
if (!ImGui::Begin("CDROM State", &g_settings.debugging.show_cdrom_state))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
|
|
|
@ -12,20 +12,16 @@
|
|||
#include <vector>
|
||||
|
||||
class StateWrapper;
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class DMA;
|
||||
class InterruptController;
|
||||
class SPU;
|
||||
|
||||
class CDROM
|
||||
class CDROM final
|
||||
{
|
||||
public:
|
||||
CDROM();
|
||||
~CDROM();
|
||||
|
||||
void Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, SPU* spu);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -276,10 +272,6 @@ private:
|
|||
template<bool STEREO, bool SAMPLE_RATE>
|
||||
void ResampleXAADPCM(const s16* frames_in, u32 num_frames_in);
|
||||
|
||||
System* m_system = nullptr;
|
||||
DMA* m_dma = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
SPU* m_spu = nullptr;
|
||||
std::unique_ptr<TimingEvent> m_command_event;
|
||||
std::unique_ptr<TimingEvent> m_drive_event;
|
||||
|
||||
|
@ -347,5 +339,7 @@ private:
|
|||
CDROMAsyncReader m_reader;
|
||||
|
||||
// two 16-bit samples packed in 32-bits
|
||||
InlineFIFOQueue<u32, AUDIO_FIFO_SIZE> m_audio_fifo;
|
||||
HeapFIFOQueue<u32, AUDIO_FIFO_SIZE> m_audio_fifo;
|
||||
};
|
||||
|
||||
extern CDROM g_cdrom;
|
||||
|
|
|
@ -39,14 +39,14 @@ float Controller::GetVibrationMotorStrength(u32 motor)
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
void Controller::LoadSettings(HostInterface* host_interface, const char* section) {}
|
||||
void Controller::LoadSettings(const char* section) {}
|
||||
|
||||
bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Controller> Controller::Create(System* system, ControllerType type, u32 index)
|
||||
std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
@ -54,13 +54,13 @@ std::unique_ptr<Controller> Controller::Create(System* system, ControllerType ty
|
|||
return DigitalController::Create();
|
||||
|
||||
case ControllerType::AnalogController:
|
||||
return AnalogController::Create(system, index);
|
||||
return AnalogController::Create(index);
|
||||
|
||||
case ControllerType::NamcoGunCon:
|
||||
return NamcoGunCon::Create(system);
|
||||
return NamcoGunCon::Create();
|
||||
|
||||
case ControllerType::PlayStationMouse:
|
||||
return PlayStationMouse::Create(system);
|
||||
return PlayStationMouse::Create();
|
||||
|
||||
case ControllerType::NeGcon:
|
||||
return NeGcon::Create();
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <vector>
|
||||
|
||||
class StateWrapper;
|
||||
class System;
|
||||
class HostInterface;
|
||||
|
||||
class Controller
|
||||
|
@ -53,13 +52,13 @@ public:
|
|||
virtual float GetVibrationMotorStrength(u32 motor);
|
||||
|
||||
/// Loads/refreshes any per-controller settings.
|
||||
virtual void LoadSettings(HostInterface* host_interface, const char* section);
|
||||
virtual void LoadSettings(const char* section);
|
||||
|
||||
/// Returns the software cursor to use for this controller, if any.
|
||||
virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale);
|
||||
|
||||
/// Creates a new controller of the specified type.
|
||||
static std::unique_ptr<Controller> Create(System* system, ControllerType type, u32 index);
|
||||
static std::unique_ptr<Controller> Create(ControllerType type, u32 index);
|
||||
|
||||
/// Gets the integer code for an axis in the specified controller type.
|
||||
static std::optional<s32> GetAxisCodeByName(ControllerType type, std::string_view axis_name);
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
<ClCompile Include="cpu_recompiler_code_generator_generic.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_code_generator_x64.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_register_cache.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_thunks.cpp" />
|
||||
<ClCompile Include="cpu_types.cpp" />
|
||||
<ClCompile Include="digital_controller.cpp" />
|
||||
<ClCompile Include="game_list.cpp" />
|
||||
|
@ -150,11 +149,6 @@
|
|||
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpu_core.inl" />
|
||||
<None Include="bus.inl" />
|
||||
<None Include="gte.inl" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{868B98C8-65A1-494B-8346-250A73A48C0A}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
<ClCompile Include="bios.cpp" />
|
||||
<ClCompile Include="cpu_code_cache.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_register_cache.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_thunks.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_code_generator_x64.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_code_generator.cpp" />
|
||||
<ClCompile Include="cpu_recompiler_code_generator_generic.cpp" />
|
||||
|
@ -64,7 +63,6 @@
|
|||
<ClInclude Include="interrupt_controller.h" />
|
||||
<ClInclude Include="cdrom.h" />
|
||||
<ClInclude Include="gte.h" />
|
||||
<ClInclude Include="gte_types.h" />
|
||||
<ClInclude Include="pad.h" />
|
||||
<ClInclude Include="digital_controller.h" />
|
||||
<ClInclude Include="timers.h" />
|
||||
|
@ -95,10 +93,6 @@
|
|||
<ClInclude Include="gpu_hw_vulkan.h" />
|
||||
<ClInclude Include="resources.h" />
|
||||
<ClInclude Include="host_interface_progress_callback.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpu_core.inl" />
|
||||
<None Include="bus.inl" />
|
||||
<None Include="gte.inl" />
|
||||
<ClInclude Include="gte_types.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,187 +1,229 @@
|
|||
#include "cpu_code_cache.h"
|
||||
#include "bus.h"
|
||||
#include "common/log.h"
|
||||
#include "cpu_core.h"
|
||||
#include "cpu_disasm.h"
|
||||
#include "system.h"
|
||||
#include "timing_event.h"
|
||||
Log_SetChannel(CPU::CodeCache);
|
||||
|
||||
#ifdef WITH_RECOMPILER
|
||||
#include "cpu_recompiler_code_generator.h"
|
||||
#include "cpu_recompiler_thunks.h"
|
||||
#endif
|
||||
|
||||
namespace CPU {
|
||||
namespace CPU::CodeCache {
|
||||
|
||||
constexpr bool USE_BLOCK_LINKING = true;
|
||||
|
||||
#ifdef WITH_RECOMPILER
|
||||
static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 32 * 1024 * 1024;
|
||||
static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 32 * 1024 * 1024;
|
||||
static constexpr u32 RECOMPILER_GUARD_SIZE = 4096;
|
||||
alignas(Recompiler::CODE_STORAGE_ALIGNMENT) static u8
|
||||
s_code_storage[RECOMPILER_CODE_CACHE_SIZE + RECOMPILER_FAR_CODE_CACHE_SIZE];
|
||||
static JitCodeBuffer s_code_buffer;
|
||||
#endif
|
||||
|
||||
CodeCache::CodeCache() = default;
|
||||
using BlockMap = std::unordered_map<u32, CodeBlock*>;
|
||||
|
||||
CodeCache::~CodeCache()
|
||||
void LogCurrentState();
|
||||
|
||||
/// Returns the block key for the current execution state.
|
||||
static CodeBlockKey GetNextBlockKey();
|
||||
|
||||
/// Looks up the block in the cache if it's already been compiled.
|
||||
static CodeBlock* LookupBlock(CodeBlockKey key);
|
||||
|
||||
/// Can the current block execute? This will re-validate the block if necessary.
|
||||
/// The block can also be flushed if recompilation failed, so ignore the pointer if false is returned.
|
||||
static bool RevalidateBlock(CodeBlock* block);
|
||||
|
||||
static bool CompileBlock(CodeBlock* block);
|
||||
static void FlushBlock(CodeBlock* block);
|
||||
static void AddBlockToPageMap(CodeBlock* block);
|
||||
static void RemoveBlockFromPageMap(CodeBlock* block);
|
||||
|
||||
/// Link block from to to.
|
||||
static void LinkBlock(CodeBlock* from, CodeBlock* to);
|
||||
|
||||
/// Unlink all blocks which point to this block, and any that this block links to.
|
||||
static void UnlinkBlock(CodeBlock* block);
|
||||
|
||||
static bool s_use_recompiler = false;
|
||||
static BlockMap s_blocks;
|
||||
static std::array<std::vector<CodeBlock*>, CPU_CODE_CACHE_PAGE_COUNT> m_ram_block_map;
|
||||
|
||||
void Initialize(bool use_recompiler)
|
||||
{
|
||||
if (m_system)
|
||||
Flush();
|
||||
}
|
||||
|
||||
void CodeCache::Initialize(System* system, Core* core, Bus* bus, bool use_recompiler)
|
||||
{
|
||||
m_system = system;
|
||||
m_core = core;
|
||||
m_bus = bus;
|
||||
Assert(s_blocks.empty());
|
||||
|
||||
#ifdef WITH_RECOMPILER
|
||||
m_use_recompiler = use_recompiler;
|
||||
m_code_buffer = std::make_unique<JitCodeBuffer>(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE);
|
||||
m_asm_functions = std::make_unique<Recompiler::ASMFunctions>();
|
||||
m_asm_functions->Generate(m_code_buffer.get());
|
||||
s_use_recompiler = use_recompiler;
|
||||
if (!s_code_buffer.Initialize(s_code_storage, sizeof(s_code_storage), RECOMPILER_FAR_CODE_CACHE_SIZE,
|
||||
RECOMPILER_GUARD_SIZE))
|
||||
{
|
||||
Panic("Failed to initialize code space");
|
||||
}
|
||||
#else
|
||||
m_use_recompiler = false;
|
||||
s_use_recompiler = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CodeCache::Execute()
|
||||
void Shutdown()
|
||||
{
|
||||
CodeBlockKey next_block_key = GetNextBlockKey();
|
||||
Flush();
|
||||
s_code_buffer.Destroy();
|
||||
}
|
||||
|
||||
while (m_core->m_pending_ticks < m_core->m_downcount)
|
||||
void Execute()
|
||||
{
|
||||
CodeBlockKey next_block_key;
|
||||
|
||||
g_state.frame_done = false;
|
||||
while (!g_state.frame_done)
|
||||
{
|
||||
if (m_core->HasPendingInterrupt())
|
||||
{
|
||||
// TODO: Fill in m_next_instruction...
|
||||
m_core->SafeReadMemoryWord(m_core->m_regs.pc, &m_core->m_next_instruction.bits);
|
||||
m_core->DispatchInterrupt();
|
||||
next_block_key = GetNextBlockKey();
|
||||
}
|
||||
|
||||
CodeBlock* block = LookupBlock(next_block_key);
|
||||
if (!block)
|
||||
{
|
||||
Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", m_core->GetRegs().pc);
|
||||
InterpretUncachedBlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
reexecute_block:
|
||||
|
||||
#if 0
|
||||
const u32 tick = m_system->GetGlobalTickCounter() + m_core->GetPendingTicks();
|
||||
if (tick == 61033207)
|
||||
__debugbreak();
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
LogCurrentState();
|
||||
#endif
|
||||
|
||||
if (m_use_recompiler)
|
||||
block->host_code(m_core);
|
||||
else
|
||||
InterpretCachedBlock(*block);
|
||||
|
||||
if (m_core->m_pending_ticks >= m_core->m_downcount)
|
||||
break;
|
||||
else if (m_core->HasPendingInterrupt() || !USE_BLOCK_LINKING)
|
||||
continue;
|
||||
TimingEvents::UpdateCPUDowncount();
|
||||
|
||||
next_block_key = GetNextBlockKey();
|
||||
if (next_block_key.bits == block->key.bits)
|
||||
while (g_state.pending_ticks < g_state.downcount)
|
||||
{
|
||||
// we can jump straight to it if there's no pending interrupts
|
||||
// ensure it's not a self-modifying block
|
||||
if (!block->invalidated || RevalidateBlock(block))
|
||||
goto reexecute_block;
|
||||
}
|
||||
else if (!block->invalidated)
|
||||
{
|
||||
// Try to find an already-linked block.
|
||||
// TODO: Don't need to dereference the block, just store a pointer to the code.
|
||||
for (CodeBlock* linked_block : block->link_successors)
|
||||
if (HasPendingInterrupt())
|
||||
{
|
||||
if (linked_block->key.bits == next_block_key.bits)
|
||||
{
|
||||
if (linked_block->invalidated && !RevalidateBlock(linked_block))
|
||||
{
|
||||
// CanExecuteBlock can result in a block flush, so stop iterating here.
|
||||
break;
|
||||
}
|
||||
// TODO: Fill in m_next_instruction...
|
||||
SafeReadMemoryWord(g_state.regs.pc, &g_state.next_instruction.bits);
|
||||
DispatchInterrupt();
|
||||
next_block_key = GetNextBlockKey();
|
||||
}
|
||||
|
||||
// Execute the linked block
|
||||
block = linked_block;
|
||||
CodeBlock* block = LookupBlock(next_block_key);
|
||||
if (!block)
|
||||
{
|
||||
Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", g_state.regs.pc);
|
||||
InterpretUncachedBlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
reexecute_block:
|
||||
|
||||
#if 0
|
||||
const u32 tick = g_system->GetGlobalTickCounter() + m_core->GetPendingTicks();
|
||||
if (tick == 61033207)
|
||||
__debugbreak();
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
LogCurrentState();
|
||||
#endif
|
||||
|
||||
if (s_use_recompiler)
|
||||
block->host_code();
|
||||
else
|
||||
InterpretCachedBlock(*block);
|
||||
|
||||
if (g_state.pending_ticks >= g_state.downcount)
|
||||
break;
|
||||
else if (HasPendingInterrupt() || !USE_BLOCK_LINKING)
|
||||
continue;
|
||||
|
||||
next_block_key = GetNextBlockKey();
|
||||
if (next_block_key.bits == block->key.bits)
|
||||
{
|
||||
// we can jump straight to it if there's no pending interrupts
|
||||
// ensure it's not a self-modifying block
|
||||
if (!block->invalidated || RevalidateBlock(block))
|
||||
goto reexecute_block;
|
||||
}
|
||||
else if (!block->invalidated)
|
||||
{
|
||||
// Try to find an already-linked block.
|
||||
// TODO: Don't need to dereference the block, just store a pointer to the code.
|
||||
for (CodeBlock* linked_block : block->link_successors)
|
||||
{
|
||||
if (linked_block->key.bits == next_block_key.bits)
|
||||
{
|
||||
if (linked_block->invalidated && !RevalidateBlock(linked_block))
|
||||
{
|
||||
// CanExecuteBlock can result in a block flush, so stop iterating here.
|
||||
break;
|
||||
}
|
||||
|
||||
// Execute the linked block
|
||||
block = linked_block;
|
||||
goto reexecute_block;
|
||||
}
|
||||
}
|
||||
|
||||
// No acceptable blocks found in the successor list, try a new one.
|
||||
CodeBlock* next_block = LookupBlock(next_block_key);
|
||||
if (next_block)
|
||||
{
|
||||
// Link the previous block to this new block if we find a new block.
|
||||
LinkBlock(block, next_block);
|
||||
block = next_block;
|
||||
goto reexecute_block;
|
||||
}
|
||||
}
|
||||
|
||||
// No acceptable blocks found in the successor list, try a new one.
|
||||
CodeBlock* next_block = LookupBlock(next_block_key);
|
||||
if (next_block)
|
||||
{
|
||||
// Link the previous block to this new block if we find a new block.
|
||||
LinkBlock(block, next_block);
|
||||
block = next_block;
|
||||
goto reexecute_block;
|
||||
}
|
||||
}
|
||||
|
||||
TimingEvents::RunEvents();
|
||||
}
|
||||
|
||||
// in case we switch to interpreter...
|
||||
m_core->m_regs.npc = m_core->m_regs.pc;
|
||||
g_state.regs.npc = g_state.regs.pc;
|
||||
}
|
||||
|
||||
void CodeCache::SetUseRecompiler(bool enable)
|
||||
void SetUseRecompiler(bool enable)
|
||||
{
|
||||
#ifdef WITH_RECOMPILER
|
||||
if (m_use_recompiler == enable)
|
||||
if (s_use_recompiler == enable)
|
||||
return;
|
||||
|
||||
m_use_recompiler = enable;
|
||||
s_use_recompiler = enable;
|
||||
Flush();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CodeCache::Flush()
|
||||
void Flush()
|
||||
{
|
||||
m_bus->ClearRAMCodePageFlags();
|
||||
Bus::ClearRAMCodePageFlags();
|
||||
for (auto& it : m_ram_block_map)
|
||||
it.clear();
|
||||
|
||||
for (const auto& it : m_blocks)
|
||||
for (const auto& it : s_blocks)
|
||||
delete it.second;
|
||||
m_blocks.clear();
|
||||
s_blocks.clear();
|
||||
#ifdef WITH_RECOMPILER
|
||||
m_code_buffer->Reset();
|
||||
s_code_buffer.Reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CodeCache::LogCurrentState()
|
||||
void LogCurrentState()
|
||||
{
|
||||
const auto& regs = m_core->m_regs;
|
||||
const auto& regs = g_state.regs;
|
||||
WriteToExecutionLog("tick=%u pc=%08X zero=%08X at=%08X v0=%08X v1=%08X a0=%08X a1=%08X a2=%08X a3=%08X t0=%08X "
|
||||
"t1=%08X t2=%08X t3=%08X t4=%08X t5=%08X t6=%08X t7=%08X s0=%08X s1=%08X s2=%08X s3=%08X s4=%08X "
|
||||
"s5=%08X s6=%08X s7=%08X t8=%08X t9=%08X k0=%08X k1=%08X gp=%08X sp=%08X fp=%08X ra=%08X ldr=%s "
|
||||
"ldv=%08X\n",
|
||||
m_system->GetGlobalTickCounter() + m_core->GetPendingTicks(), regs.pc, regs.zero, regs.at,
|
||||
regs.v0, regs.v1, regs.a0, regs.a1, regs.a2, regs.a3, regs.t0, regs.t1, regs.t2, regs.t3, regs.t4,
|
||||
regs.t5, regs.t6, regs.t7, regs.s0, regs.s1, regs.s2, regs.s3, regs.s4, regs.s5, regs.s6, regs.s7,
|
||||
regs.t8, regs.t9, regs.k0, regs.k1, regs.gp, regs.sp, regs.fp, regs.ra,
|
||||
(m_core->m_next_load_delay_reg == Reg::count) ? "NONE" :
|
||||
GetRegName(m_core->m_next_load_delay_reg),
|
||||
(m_core->m_next_load_delay_reg == Reg::count) ? 0 : m_core->m_next_load_delay_value);
|
||||
TimingEvents::GetGlobalTickCounter() + GetPendingTicks(), regs.pc, regs.zero, regs.at, regs.v0,
|
||||
regs.v1, regs.a0, regs.a1, regs.a2, regs.a3, regs.t0, regs.t1, regs.t2, regs.t3, regs.t4, regs.t5,
|
||||
regs.t6, regs.t7, regs.s0, regs.s1, regs.s2, regs.s3, regs.s4, regs.s5, regs.s6, regs.s7, regs.t8,
|
||||
regs.t9, regs.k0, regs.k1, regs.gp, regs.sp, regs.fp, regs.ra,
|
||||
(g_state.next_load_delay_reg == Reg::count) ? "NONE" : GetRegName(g_state.next_load_delay_reg),
|
||||
(g_state.next_load_delay_reg == Reg::count) ? 0 : g_state.next_load_delay_value);
|
||||
}
|
||||
|
||||
CodeBlockKey CodeCache::GetNextBlockKey() const
|
||||
CodeBlockKey GetNextBlockKey()
|
||||
{
|
||||
CodeBlockKey key = {};
|
||||
key.SetPC(m_core->GetRegs().pc);
|
||||
key.user_mode = m_core->InUserMode();
|
||||
key.SetPC(g_state.regs.pc);
|
||||
key.user_mode = InUserMode();
|
||||
return key;
|
||||
}
|
||||
|
||||
CodeBlock* CodeCache::LookupBlock(CodeBlockKey key)
|
||||
CodeBlock* LookupBlock(CodeBlockKey key)
|
||||
{
|
||||
BlockMap::iterator iter = m_blocks.find(key.bits);
|
||||
if (iter != m_blocks.end())
|
||||
BlockMap::iterator iter = s_blocks.find(key.bits);
|
||||
if (iter != s_blocks.end())
|
||||
{
|
||||
// ensure it hasn't been invalidated
|
||||
CodeBlock* existing_block = iter->second;
|
||||
|
@ -202,17 +244,15 @@ CodeBlock* CodeCache::LookupBlock(CodeBlockKey key)
|
|||
block = nullptr;
|
||||
}
|
||||
|
||||
iter = m_blocks.emplace(key.bits, block).first;
|
||||
iter = s_blocks.emplace(key.bits, block).first;
|
||||
return block;
|
||||
}
|
||||
|
||||
bool CodeCache::RevalidateBlock(CodeBlock* block)
|
||||
bool RevalidateBlock(CodeBlock* block)
|
||||
{
|
||||
for (const CodeBlockInstruction& cbi : block->instructions)
|
||||
{
|
||||
u32 new_code = 0;
|
||||
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(cbi.pc & PHYSICAL_MEMORY_ADDRESS_MASK,
|
||||
new_code);
|
||||
u32 new_code = Bus::ReadCacheableAddress(cbi.pc & PHYSICAL_MEMORY_ADDRESS_MASK);
|
||||
if (cbi.instruction.bits != new_code)
|
||||
{
|
||||
Log_DebugPrintf("Block 0x%08X changed at PC 0x%08X - %08X to %08X - recompiling.", block->GetPC(), cbi.pc,
|
||||
|
@ -242,7 +282,7 @@ recompile:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CodeCache::CompileBlock(CodeBlock* block)
|
||||
bool CompileBlock(CodeBlock* block)
|
||||
{
|
||||
u32 pc = block->GetPC();
|
||||
bool is_branch_delay_slot = false;
|
||||
|
@ -258,12 +298,12 @@ bool CodeCache::CompileBlock(CodeBlock* block)
|
|||
CodeBlockInstruction cbi = {};
|
||||
|
||||
const PhysicalMemoryAddress phys_addr = pc & PHYSICAL_MEMORY_ADDRESS_MASK;
|
||||
if (!m_bus->IsCacheableAddress(phys_addr) ||
|
||||
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(phys_addr, cbi.instruction.bits) < 0 ||
|
||||
!IsInvalidInstruction(cbi.instruction))
|
||||
{
|
||||
if (!Bus::IsCacheableAddress(phys_addr))
|
||||
break;
|
||||
|
||||
cbi.instruction.bits = Bus::ReadCacheableAddress(phys_addr);
|
||||
if (!IsInvalidInstruction(cbi.instruction))
|
||||
break;
|
||||
}
|
||||
|
||||
cbi.pc = pc;
|
||||
cbi.is_branch_delay_slot = is_branch_delay_slot;
|
||||
|
@ -272,7 +312,7 @@ bool CodeCache::CompileBlock(CodeBlock* block)
|
|||
cbi.is_load_instruction = IsMemoryLoadInstruction(cbi.instruction);
|
||||
cbi.is_store_instruction = IsMemoryStoreInstruction(cbi.instruction);
|
||||
cbi.has_load_delay = InstructionHasLoadDelay(cbi.instruction);
|
||||
cbi.can_trap = CanInstructionTrap(cbi.instruction, m_core->InUserMode());
|
||||
cbi.can_trap = CanInstructionTrap(cbi.instruction, InUserMode());
|
||||
|
||||
// instruction is decoded now
|
||||
block->instructions.push_back(cbi);
|
||||
|
@ -316,19 +356,19 @@ bool CodeCache::CompileBlock(CodeBlock* block)
|
|||
}
|
||||
|
||||
#ifdef WITH_RECOMPILER
|
||||
if (m_use_recompiler)
|
||||
if (s_use_recompiler)
|
||||
{
|
||||
// Ensure we're not going to run out of space while compiling this block.
|
||||
if (m_code_buffer->GetFreeCodeSpace() <
|
||||
if (s_code_buffer.GetFreeCodeSpace() <
|
||||
(block->instructions.size() * Recompiler::MAX_NEAR_HOST_BYTES_PER_INSTRUCTION) ||
|
||||
m_code_buffer->GetFreeFarCodeSpace() <
|
||||
s_code_buffer.GetFreeFarCodeSpace() <
|
||||
(block->instructions.size() * Recompiler::MAX_FAR_HOST_BYTES_PER_INSTRUCTION))
|
||||
{
|
||||
Log_WarningPrintf("Out of code space, flushing all blocks.");
|
||||
Flush();
|
||||
}
|
||||
|
||||
Recompiler::CodeGenerator codegen(m_core, m_code_buffer.get(), *m_asm_functions.get());
|
||||
Recompiler::CodeGenerator codegen(&s_code_buffer);
|
||||
if (!codegen.CompileBlock(block, &block->host_code, &block->host_code_size))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to compile host code for block at 0x%08X", block->key.GetPC());
|
||||
|
@ -340,7 +380,7 @@ bool CodeCache::CompileBlock(CodeBlock* block)
|
|||
return true;
|
||||
}
|
||||
|
||||
void CodeCache::InvalidateBlocksWithPageIndex(u32 page_index)
|
||||
void InvalidateBlocksWithPageIndex(u32 page_index)
|
||||
{
|
||||
DebugAssert(page_index < CPU_CODE_CACHE_PAGE_COUNT);
|
||||
auto& blocks = m_ram_block_map[page_index];
|
||||
|
@ -353,24 +393,26 @@ void CodeCache::InvalidateBlocksWithPageIndex(u32 page_index)
|
|||
|
||||
// Block will be re-added next execution.
|
||||
blocks.clear();
|
||||
m_bus->ClearRAMCodePage(page_index);
|
||||
Bus::ClearRAMCodePage(page_index);
|
||||
}
|
||||
|
||||
void CodeCache::FlushBlock(CodeBlock* block)
|
||||
void FlushBlock(CodeBlock* block)
|
||||
{
|
||||
BlockMap::iterator iter = m_blocks.find(block->key.GetPC());
|
||||
Assert(iter != m_blocks.end() && iter->second == block);
|
||||
BlockMap::iterator iter = s_blocks.find(block->key.GetPC());
|
||||
Assert(iter != s_blocks.end() && iter->second == block);
|
||||
Log_DevPrintf("Flushing block at address 0x%08X", block->GetPC());
|
||||
|
||||
// if it's been invalidated it won't be in the page map
|
||||
if (block->invalidated)
|
||||
RemoveBlockFromPageMap(block);
|
||||
|
||||
m_blocks.erase(iter);
|
||||
UnlinkBlock(block);
|
||||
|
||||
s_blocks.erase(iter);
|
||||
delete block;
|
||||
}
|
||||
|
||||
void CodeCache::AddBlockToPageMap(CodeBlock* block)
|
||||
void AddBlockToPageMap(CodeBlock* block)
|
||||
{
|
||||
if (!block->IsInRAM())
|
||||
return;
|
||||
|
@ -380,11 +422,11 @@ void CodeCache::AddBlockToPageMap(CodeBlock* block)
|
|||
for (u32 page = start_page; page <= end_page; page++)
|
||||
{
|
||||
m_ram_block_map[page].push_back(block);
|
||||
m_bus->SetRAMCodePage(page);
|
||||
Bus::SetRAMCodePage(page);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeCache::RemoveBlockFromPageMap(CodeBlock* block)
|
||||
void RemoveBlockFromPageMap(CodeBlock* block)
|
||||
{
|
||||
if (!block->IsInRAM())
|
||||
return;
|
||||
|
@ -400,14 +442,14 @@ void CodeCache::RemoveBlockFromPageMap(CodeBlock* block)
|
|||
}
|
||||
}
|
||||
|
||||
void CodeCache::LinkBlock(CodeBlock* from, CodeBlock* to)
|
||||
void LinkBlock(CodeBlock* from, CodeBlock* to)
|
||||
{
|
||||
Log_DebugPrintf("Linking block %p(%08x) to %p(%08x)", from, from->GetPC(), to, to->GetPC());
|
||||
from->link_successors.push_back(to);
|
||||
to->link_predecessors.push_back(from);
|
||||
}
|
||||
|
||||
void CodeCache::UnlinkBlock(CodeBlock* block)
|
||||
void UnlinkBlock(CodeBlock* block)
|
||||
{
|
||||
for (CodeBlock* predecessor : block->link_predecessors)
|
||||
{
|
||||
|
@ -426,82 +468,4 @@ void CodeCache::UnlinkBlock(CodeBlock* block)
|
|||
block->link_successors.clear();
|
||||
}
|
||||
|
||||
void CodeCache::InterpretCachedBlock(const CodeBlock& block)
|
||||
{
|
||||
// set up the state so we've already fetched the instruction
|
||||
DebugAssert(m_core->m_regs.pc == block.GetPC());
|
||||
|
||||
m_core->m_regs.npc = block.GetPC() + 4;
|
||||
|
||||
for (const CodeBlockInstruction& cbi : block.instructions)
|
||||
{
|
||||
m_core->m_pending_ticks++;
|
||||
|
||||
// now executing the instruction we previously fetched
|
||||
m_core->m_current_instruction.bits = cbi.instruction.bits;
|
||||
m_core->m_current_instruction_pc = cbi.pc;
|
||||
m_core->m_current_instruction_in_branch_delay_slot = cbi.is_branch_delay_slot;
|
||||
m_core->m_current_instruction_was_branch_taken = m_core->m_branch_was_taken;
|
||||
m_core->m_branch_was_taken = false;
|
||||
m_core->m_exception_raised = false;
|
||||
|
||||
// update pc
|
||||
m_core->m_regs.pc = m_core->m_regs.npc;
|
||||
m_core->m_regs.npc += 4;
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
m_core->ExecuteInstruction();
|
||||
|
||||
// next load delay
|
||||
m_core->UpdateLoadDelay();
|
||||
|
||||
if (m_core->m_exception_raised)
|
||||
break;
|
||||
}
|
||||
|
||||
// cleanup so the interpreter can kick in if needed
|
||||
m_core->m_next_instruction_is_branch_delay_slot = false;
|
||||
}
|
||||
|
||||
void CodeCache::InterpretUncachedBlock()
|
||||
{
|
||||
Panic("Fixme with regards to re-fetching PC");
|
||||
|
||||
// At this point, pc contains the last address executed (in the previous block). The instruction has not been fetched
|
||||
// yet. pc shouldn't be updated until the fetch occurs, that way the exception occurs in the delay slot.
|
||||
bool in_branch_delay_slot = false;
|
||||
for (;;)
|
||||
{
|
||||
m_core->m_pending_ticks++;
|
||||
|
||||
// now executing the instruction we previously fetched
|
||||
m_core->m_current_instruction.bits = m_core->m_next_instruction.bits;
|
||||
m_core->m_current_instruction_pc = m_core->m_regs.pc;
|
||||
m_core->m_current_instruction_in_branch_delay_slot = m_core->m_next_instruction_is_branch_delay_slot;
|
||||
m_core->m_current_instruction_was_branch_taken = m_core->m_branch_was_taken;
|
||||
m_core->m_next_instruction_is_branch_delay_slot = false;
|
||||
m_core->m_branch_was_taken = false;
|
||||
m_core->m_exception_raised = false;
|
||||
|
||||
// Fetch the next instruction, except if we're in a branch delay slot. The "fetch" is done in the next block.
|
||||
if (!m_core->FetchInstruction())
|
||||
break;
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
m_core->ExecuteInstruction();
|
||||
|
||||
// next load delay
|
||||
m_core->UpdateLoadDelay();
|
||||
|
||||
const bool branch = IsBranchInstruction(m_core->m_current_instruction);
|
||||
if (m_core->m_exception_raised || (!branch && in_branch_delay_slot) ||
|
||||
IsExitBlockInstruction(m_core->m_current_instruction))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
in_branch_delay_slot = branch;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CPU
|
||||
} // namespace CPU::CodeCache
|
||||
|
|
|
@ -1,22 +1,13 @@
|
|||
#pragma once
|
||||
#include "common/bitfield.h"
|
||||
#include "common/jit_code_buffer.h"
|
||||
#include "cpu_types.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class JitCodeBuffer;
|
||||
|
||||
class Bus;
|
||||
class System;
|
||||
|
||||
namespace CPU {
|
||||
class Core;
|
||||
|
||||
namespace Recompiler {
|
||||
class ASMFunctions;
|
||||
}
|
||||
|
||||
union CodeBlockKey
|
||||
{
|
||||
|
@ -58,7 +49,7 @@ struct CodeBlockInstruction
|
|||
|
||||
struct CodeBlock
|
||||
{
|
||||
using HostCodePointer = void (*)(Core*);
|
||||
using HostCodePointer = void (*)();
|
||||
|
||||
CodeBlock(const CodeBlockKey key_) : key(key_) {}
|
||||
|
||||
|
@ -86,67 +77,24 @@ struct CodeBlock
|
|||
}
|
||||
};
|
||||
|
||||
class CodeCache
|
||||
{
|
||||
public:
|
||||
CodeCache();
|
||||
~CodeCache();
|
||||
namespace CodeCache {
|
||||
|
||||
void Initialize(System* system, Core* core, Bus* bus, bool use_recompiler);
|
||||
void Execute();
|
||||
void Initialize(bool use_recompiler);
|
||||
void Shutdown();
|
||||
void Execute();
|
||||
|
||||
/// Flushes the code cache, forcing all blocks to be recompiled.
|
||||
void Flush();
|
||||
/// Flushes the code cache, forcing all blocks to be recompiled.
|
||||
void Flush();
|
||||
|
||||
/// Changes whether the recompiler is enabled.
|
||||
void SetUseRecompiler(bool enable);
|
||||
/// Changes whether the recompiler is enabled.
|
||||
void SetUseRecompiler(bool enable);
|
||||
|
||||
/// Invalidates all blocks which are in the range of the specified code page.
|
||||
void InvalidateBlocksWithPageIndex(u32 page_index);
|
||||
/// Invalidates all blocks which are in the range of the specified code page.
|
||||
void InvalidateBlocksWithPageIndex(u32 page_index);
|
||||
|
||||
private:
|
||||
using BlockMap = std::unordered_map<u32, CodeBlock*>;
|
||||
void InterpretCachedBlock(const CodeBlock& block);
|
||||
void InterpretUncachedBlock();
|
||||
|
||||
void LogCurrentState();
|
||||
|
||||
/// Returns the block key for the current execution state.
|
||||
CodeBlockKey GetNextBlockKey() const;
|
||||
|
||||
/// Looks up the block in the cache if it's already been compiled.
|
||||
CodeBlock* LookupBlock(CodeBlockKey key);
|
||||
|
||||
/// Can the current block execute? This will re-validate the block if necessary.
|
||||
/// The block can also be flushed if recompilation failed, so ignore the pointer if false is returned.
|
||||
bool RevalidateBlock(CodeBlock* block);
|
||||
|
||||
bool CompileBlock(CodeBlock* block);
|
||||
void FlushBlock(CodeBlock* block);
|
||||
void AddBlockToPageMap(CodeBlock* block);
|
||||
void RemoveBlockFromPageMap(CodeBlock* block);
|
||||
|
||||
/// Link block from to to.
|
||||
void LinkBlock(CodeBlock* from, CodeBlock* to);
|
||||
|
||||
/// Unlink all blocks which point to this block, and any that this block links to.
|
||||
void UnlinkBlock(CodeBlock* block);
|
||||
|
||||
void InterpretCachedBlock(const CodeBlock& block);
|
||||
void InterpretUncachedBlock();
|
||||
|
||||
System* m_system = nullptr;
|
||||
Core* m_core = nullptr;
|
||||
Bus* m_bus = nullptr;
|
||||
|
||||
#ifdef WITH_RECOMPILER
|
||||
std::unique_ptr<JitCodeBuffer> m_code_buffer;
|
||||
std::unique_ptr<Recompiler::ASMFunctions> m_asm_functions;
|
||||
#endif
|
||||
|
||||
BlockMap m_blocks;
|
||||
|
||||
bool m_use_recompiler = false;
|
||||
|
||||
std::array<std::vector<CodeBlock*>, CPU_CODE_CACHE_PAGE_COUNT> m_ram_block_map;
|
||||
};
|
||||
}; // namespace CodeCache
|
||||
|
||||
} // namespace CPU
|
File diff suppressed because it is too large
Load Diff
|
@ -1,186 +1,105 @@
|
|||
#pragma once
|
||||
#include "common/bitfield.h"
|
||||
#include "cpu_types.h"
|
||||
#include "gte.h"
|
||||
#include "gte_types.h"
|
||||
#include "types.h"
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
class StateWrapper;
|
||||
|
||||
class Bus;
|
||||
class System;
|
||||
|
||||
namespace CPU {
|
||||
|
||||
class CodeCache;
|
||||
|
||||
namespace Recompiler {
|
||||
class CodeGenerator;
|
||||
class Thunks;
|
||||
} // namespace Recompiler
|
||||
|
||||
class Core
|
||||
enum : VirtualMemoryAddress
|
||||
{
|
||||
public:
|
||||
static constexpr VirtualMemoryAddress RESET_VECTOR = UINT32_C(0xBFC00000);
|
||||
static constexpr PhysicalMemoryAddress DCACHE_LOCATION = UINT32_C(0x1F800000);
|
||||
static constexpr PhysicalMemoryAddress DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00);
|
||||
static constexpr PhysicalMemoryAddress DCACHE_OFFSET_MASK = UINT32_C(0x000003FF);
|
||||
static constexpr PhysicalMemoryAddress DCACHE_SIZE = UINT32_C(0x00000400);
|
||||
|
||||
friend CodeCache;
|
||||
friend Recompiler::CodeGenerator;
|
||||
friend Recompiler::Thunks;
|
||||
|
||||
Core();
|
||||
~Core();
|
||||
|
||||
void Initialize(Bus* bus);
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
void Execute();
|
||||
|
||||
ALWAYS_INLINE Bus* GetBus() const { return m_bus; }
|
||||
|
||||
ALWAYS_INLINE const Registers& GetRegs() const { return m_regs; }
|
||||
ALWAYS_INLINE Registers& GetRegs() { return m_regs; }
|
||||
|
||||
ALWAYS_INLINE TickCount GetPendingTicks() const { return m_pending_ticks; }
|
||||
ALWAYS_INLINE void ResetPendingTicks() { m_pending_ticks = 0; }
|
||||
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { m_pending_ticks += ticks; }
|
||||
|
||||
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
|
||||
ALWAYS_INLINE void SetDowncount(TickCount downcount) { m_downcount = downcount; }
|
||||
|
||||
ALWAYS_INLINE const GTE::Core& GetCop2() const { return m_cop2; }
|
||||
ALWAYS_INLINE GTE::Core& GetCop2() { return m_cop2; }
|
||||
|
||||
// Sets the PC and flushes the pipeline.
|
||||
void SetPC(u32 new_pc);
|
||||
|
||||
// Memory reads variants which do not raise exceptions.
|
||||
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
|
||||
bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
|
||||
bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
|
||||
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
|
||||
bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
|
||||
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
|
||||
|
||||
// External IRQs
|
||||
void SetExternalInterrupt(u8 bit);
|
||||
void ClearExternalInterrupt(u8 bit);
|
||||
|
||||
private:
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount DoMemoryAccess(VirtualMemoryAddress address, u32& value);
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
bool DoAlignmentCheck(VirtualMemoryAddress address);
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
void DoScratchpadAccess(PhysicalMemoryAddress address, u32& value);
|
||||
|
||||
bool ReadMemoryByte(VirtualMemoryAddress addr, u8* value);
|
||||
bool ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
|
||||
bool ReadMemoryWord(VirtualMemoryAddress addr, u32* value);
|
||||
bool WriteMemoryByte(VirtualMemoryAddress addr, u8 value);
|
||||
bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
|
||||
bool WriteMemoryWord(VirtualMemoryAddress addr, u32 value);
|
||||
|
||||
// state helpers
|
||||
ALWAYS_INLINE bool InUserMode() const { return m_cop0_regs.sr.KUc; }
|
||||
ALWAYS_INLINE bool InKernelMode() const { return !m_cop0_regs.sr.KUc; }
|
||||
|
||||
void DisassembleAndPrint(u32 addr);
|
||||
void DisassembleAndLog(u32 addr);
|
||||
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
|
||||
|
||||
// Updates load delays - call after each instruction
|
||||
ALWAYS_INLINE void UpdateLoadDelay()
|
||||
{
|
||||
// the old value is needed in case the delay slot instruction overwrites the same register
|
||||
if (m_load_delay_reg != Reg::count)
|
||||
m_regs.r[static_cast<u8>(m_load_delay_reg)] = m_load_delay_value;
|
||||
|
||||
m_load_delay_reg = m_next_load_delay_reg;
|
||||
m_load_delay_value = m_next_load_delay_value;
|
||||
m_next_load_delay_reg = Reg::count;
|
||||
}
|
||||
|
||||
// Fetches the instruction at m_regs.npc
|
||||
bool FetchInstruction();
|
||||
void ExecuteInstruction();
|
||||
void ExecuteCop0Instruction();
|
||||
void ExecuteCop2Instruction();
|
||||
void Branch(u32 target);
|
||||
|
||||
// exceptions
|
||||
u32 GetExceptionVector(Exception excode) const;
|
||||
void RaiseException(Exception excode);
|
||||
void RaiseException(Exception excode, u32 EPC, bool BD, bool BT, u8 CE);
|
||||
bool HasPendingInterrupt();
|
||||
void DispatchInterrupt();
|
||||
|
||||
// clears pipeline of load/branch delays
|
||||
void FlushPipeline();
|
||||
|
||||
// helper functions for registers which aren't writable
|
||||
u32 ReadReg(Reg rs);
|
||||
void WriteReg(Reg rd, u32 value);
|
||||
|
||||
// helper for generating a load delay write
|
||||
void WriteRegDelayed(Reg rd, u32 value);
|
||||
|
||||
// write to cache control register
|
||||
void WriteCacheControl(u32 value);
|
||||
|
||||
// read/write cop0 regs
|
||||
std::optional<u32> ReadCop0Reg(Cop0Reg reg);
|
||||
void WriteCop0Reg(Cop0Reg reg, u32 value);
|
||||
|
||||
Bus* m_bus = nullptr;
|
||||
|
||||
// ticks the CPU has executed
|
||||
TickCount m_pending_ticks = 0;
|
||||
TickCount m_downcount = MAX_SLICE_SIZE;
|
||||
|
||||
Registers m_regs = {};
|
||||
Cop0Registers m_cop0_regs = {};
|
||||
Instruction m_next_instruction = {};
|
||||
|
||||
// address of the instruction currently being executed
|
||||
Instruction m_current_instruction = {};
|
||||
u32 m_current_instruction_pc = 0;
|
||||
bool m_current_instruction_in_branch_delay_slot = false;
|
||||
bool m_current_instruction_was_branch_taken = false;
|
||||
bool m_next_instruction_is_branch_delay_slot = false;
|
||||
bool m_branch_was_taken = false;
|
||||
bool m_exception_raised = false;
|
||||
bool m_interrupt_delay = false;
|
||||
|
||||
// load delays
|
||||
Reg m_load_delay_reg = Reg::count;
|
||||
u32 m_load_delay_value = 0;
|
||||
Reg m_next_load_delay_reg = Reg::count;
|
||||
u32 m_next_load_delay_value = 0;
|
||||
|
||||
u32 m_cache_control = 0;
|
||||
System* m_system = nullptr;
|
||||
|
||||
// data cache (used as scratchpad)
|
||||
std::array<u8, DCACHE_SIZE> m_dcache = {};
|
||||
|
||||
GTE::Core m_cop2;
|
||||
RESET_VECTOR = UINT32_C(0xBFC00000)
|
||||
};
|
||||
enum : PhysicalMemoryAddress
|
||||
{
|
||||
DCACHE_LOCATION = UINT32_C(0x1F800000),
|
||||
DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00),
|
||||
DCACHE_OFFSET_MASK = UINT32_C(0x000003FF),
|
||||
DCACHE_SIZE = UINT32_C(0x00000400)
|
||||
};
|
||||
|
||||
extern bool TRACE_EXECUTION;
|
||||
extern bool LOG_EXECUTION;
|
||||
struct State
|
||||
{
|
||||
// ticks the CPU has executed
|
||||
TickCount pending_ticks = 0;
|
||||
TickCount downcount = MAX_SLICE_SIZE;
|
||||
|
||||
Registers regs = {};
|
||||
Cop0Registers cop0_regs = {};
|
||||
Instruction next_instruction = {};
|
||||
|
||||
// address of the instruction currently being executed
|
||||
Instruction current_instruction = {};
|
||||
u32 current_instruction_pc = 0;
|
||||
bool current_instruction_in_branch_delay_slot = false;
|
||||
bool current_instruction_was_branch_taken = false;
|
||||
bool next_instruction_is_branch_delay_slot = false;
|
||||
bool branch_was_taken = false;
|
||||
bool exception_raised = false;
|
||||
bool interrupt_delay = false;
|
||||
bool frame_done = false;
|
||||
|
||||
// load delays
|
||||
Reg load_delay_reg = Reg::count;
|
||||
u32 load_delay_value = 0;
|
||||
Reg next_load_delay_reg = Reg::count;
|
||||
u32 next_load_delay_value = 0;
|
||||
|
||||
u32 cache_control = 0;
|
||||
|
||||
// GTE registers are stored here so we can access them on ARM with a single instruction
|
||||
GTE::Regs gte_regs = {};
|
||||
|
||||
// data cache (used as scratchpad)
|
||||
std::array<u8, DCACHE_SIZE> dcache = {};
|
||||
};
|
||||
|
||||
extern State g_state;
|
||||
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
/// Executes interpreter loop.
|
||||
void Execute();
|
||||
|
||||
ALWAYS_INLINE Registers& GetRegs() { return g_state.regs; }
|
||||
|
||||
ALWAYS_INLINE TickCount GetPendingTicks() { return g_state.pending_ticks; }
|
||||
ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; }
|
||||
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { g_state.pending_ticks += ticks; }
|
||||
|
||||
// state helpers
|
||||
ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; }
|
||||
ALWAYS_INLINE bool InKernelMode() { return !g_state.cop0_regs.sr.KUc; }
|
||||
|
||||
// Memory reads variants which do not raise exceptions.
|
||||
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
|
||||
bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
|
||||
bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
|
||||
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
|
||||
bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
|
||||
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
|
||||
|
||||
// External IRQs
|
||||
void SetExternalInterrupt(u8 bit);
|
||||
void ClearExternalInterrupt(u8 bit);
|
||||
bool HasPendingInterrupt();
|
||||
void DispatchInterrupt();
|
||||
|
||||
void DisassembleAndPrint(u32 addr);
|
||||
void DisassembleAndLog(u32 addr);
|
||||
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
|
||||
|
||||
// Write to CPU execution log file.
|
||||
void WriteToExecutionLog(const char* format, ...);
|
||||
|
||||
} // namespace CPU
|
||||
extern bool TRACE_EXECUTION;
|
||||
extern bool LOG_EXECUTION;
|
||||
|
||||
#include "cpu_core.inl"
|
||||
} // namespace CPU
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
#pragma once
|
||||
#include "common/align.h"
|
||||
#include "bus.h"
|
||||
#include "cpu_core.h"
|
||||
|
||||
namespace CPU {
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
TickCount Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
|
||||
{
|
||||
switch (address >> 29)
|
||||
{
|
||||
case 0x00: // KUSEG 0M-512M
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Write)
|
||||
{
|
||||
if (m_cop0_regs.sr.Isc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF);
|
||||
if ((phys_addr & DCACHE_LOCATION_MASK) == DCACHE_LOCATION)
|
||||
{
|
||||
DoScratchpadAccess<type, size>(phys_addr, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_bus->DispatchAccess<type, size>(phys_addr, value);
|
||||
}
|
||||
|
||||
case 0x01: // KUSEG 512M-1024M
|
||||
case 0x02: // KUSEG 1024M-1536M
|
||||
case 0x03: // KUSEG 1536M-2048M
|
||||
{
|
||||
// Above 512mb raises an exception.
|
||||
return -1;
|
||||
}
|
||||
|
||||
case 0x04: // KSEG0 - physical memory cached
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Write)
|
||||
{
|
||||
if (m_cop0_regs.sr.Isc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF);
|
||||
if ((phys_addr & DCACHE_LOCATION_MASK) == DCACHE_LOCATION)
|
||||
{
|
||||
DoScratchpadAccess<type, size>(phys_addr, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_bus->DispatchAccess<type, size>(phys_addr, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x05: // KSEG1 - physical memory uncached
|
||||
{
|
||||
const PhysicalMemoryAddress phys_addr = address & UINT32_C(0x1FFFFFFF);
|
||||
return m_bus->DispatchAccess<type, size>(phys_addr, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x06: // KSEG2
|
||||
case 0x07: // KSEG2
|
||||
{
|
||||
if (address == 0xFFFE0130)
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
value = m_cache_control;
|
||||
else
|
||||
WriteCacheControl(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
UnreachableCode();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
bool CPU::Core::DoAlignmentCheck(VirtualMemoryAddress address)
|
||||
{
|
||||
if constexpr (size == MemoryAccessSize::HalfWord)
|
||||
{
|
||||
if (Common::IsAlignedPow2(address, 2))
|
||||
return true;
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::Word)
|
||||
{
|
||||
if (Common::IsAlignedPow2(address, 4))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_cop0_regs.BadVaddr = address;
|
||||
RaiseException(type == MemoryAccessType::Read ? Exception::AdEL : Exception::AdES);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<MemoryAccessType type, MemoryAccessSize size>
|
||||
void CPU::Core::DoScratchpadAccess(PhysicalMemoryAddress address, u32& value)
|
||||
{
|
||||
const PhysicalMemoryAddress cache_offset = address & DCACHE_OFFSET_MASK;
|
||||
if constexpr (size == MemoryAccessSize::Byte)
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
value = ZeroExtend32(m_dcache[cache_offset]);
|
||||
else
|
||||
m_dcache[cache_offset] = Truncate8(value);
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::HalfWord)
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
{
|
||||
u16 temp;
|
||||
std::memcpy(&temp, &m_dcache[cache_offset], sizeof(temp));
|
||||
value = ZeroExtend32(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 temp = Truncate16(value);
|
||||
std::memcpy(&m_dcache[cache_offset], &temp, sizeof(temp));
|
||||
}
|
||||
}
|
||||
else if constexpr (size == MemoryAccessSize::Word)
|
||||
{
|
||||
if constexpr (type == MemoryAccessType::Read)
|
||||
std::memcpy(&value, &m_dcache[cache_offset], sizeof(value));
|
||||
else
|
||||
std::memcpy(&m_dcache[cache_offset], &value, sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CPU
|
|
@ -1,5 +1,6 @@
|
|||
#include "cpu_disasm.h"
|
||||
#include "cpu_core.h"
|
||||
#include "common/assert.h"
|
||||
#include <array>
|
||||
|
||||
namespace CPU {
|
||||
|
@ -166,7 +167,7 @@ static const std::array<std::pair<CopCommonInstruction, const char*>, 5> s_cop_c
|
|||
|
||||
static const std::array<std::pair<Cop0Instruction, const char*>, 1> s_cop0_table = {{{Cop0Instruction::rfe, "rfe"}}};
|
||||
|
||||
static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core* state, const char* format)
|
||||
static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Registers* regs, const char* format)
|
||||
{
|
||||
dest->Clear();
|
||||
|
||||
|
@ -185,10 +186,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
|
|||
if (std::strncmp(str, "rs", 2) == 0)
|
||||
{
|
||||
dest->AppendString(GetRegName(inst.r.rs));
|
||||
if (state)
|
||||
if (regs)
|
||||
{
|
||||
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rs),
|
||||
state->GetRegs().r[static_cast<u8>(inst.r.rs.GetValue())]);
|
||||
regs->r[static_cast<u8>(inst.r.rs.GetValue())]);
|
||||
}
|
||||
|
||||
str += 2;
|
||||
|
@ -196,10 +197,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
|
|||
else if (std::strncmp(str, "rt", 2) == 0)
|
||||
{
|
||||
dest->AppendString(GetRegName(inst.r.rt));
|
||||
if (state)
|
||||
if (regs)
|
||||
{
|
||||
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rt),
|
||||
state->GetRegs().r[static_cast<u8>(inst.r.rt.GetValue())]);
|
||||
regs->r[static_cast<u8>(inst.r.rt.GetValue())]);
|
||||
}
|
||||
|
||||
str += 2;
|
||||
|
@ -207,10 +208,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
|
|||
else if (std::strncmp(str, "rd", 2) == 0)
|
||||
{
|
||||
dest->AppendString(GetRegName(inst.r.rd));
|
||||
if (state)
|
||||
if (regs)
|
||||
{
|
||||
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rd),
|
||||
state->GetRegs().r[static_cast<u8>(inst.r.rd.GetValue())]);
|
||||
regs->r[static_cast<u8>(inst.r.rd.GetValue())]);
|
||||
}
|
||||
|
||||
str += 2;
|
||||
|
@ -241,10 +242,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
|
|||
{
|
||||
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
|
||||
dest->AppendFormattedString("%d(%s)", offset, GetRegName(inst.i.rs));
|
||||
if (state)
|
||||
if (regs)
|
||||
{
|
||||
comment.AppendFormattedString("%saddr=0x%08X", comment.IsEmpty() ? "" : ", ",
|
||||
state->GetRegs().r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
|
||||
regs->r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
|
||||
}
|
||||
|
||||
str += 8;
|
||||
|
@ -291,14 +292,14 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void FormatCopInstruction(String* dest, u32 pc, Core* state, const Instruction inst,
|
||||
void FormatCopInstruction(String* dest, u32 pc, Registers* regs, const Instruction inst,
|
||||
const std::pair<T, const char*>* table, size_t table_size, T table_key)
|
||||
{
|
||||
for (size_t i = 0; i < table_size; i++)
|
||||
{
|
||||
if (table[i].first == table_key)
|
||||
{
|
||||
FormatInstruction(dest, inst, pc, state, table[i].second);
|
||||
FormatInstruction(dest, inst, pc, regs, table[i].second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -306,13 +307,13 @@ void FormatCopInstruction(String* dest, u32 pc, Core* state, const Instruction i
|
|||
dest->Format("<cop%u 0x%08X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
|
||||
}
|
||||
|
||||
void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
|
||||
void DisassembleInstruction(String* dest, u32 pc, u32 bits, Registers* regs)
|
||||
{
|
||||
const Instruction inst{bits};
|
||||
switch (inst.op)
|
||||
{
|
||||
case InstructionOp::funct:
|
||||
FormatInstruction(dest, inst, pc, state, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
|
||||
FormatInstruction(dest, inst, pc, regs, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
|
||||
return;
|
||||
|
||||
case InstructionOp::cop0:
|
||||
|
@ -322,7 +323,7 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
|
|||
{
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
{
|
||||
FormatCopInstruction(dest, pc, state, inst, s_cop_common_table.data(), s_cop_common_table.size(),
|
||||
FormatCopInstruction(dest, pc, regs, inst, s_cop_common_table.data(), s_cop_common_table.size(),
|
||||
inst.cop.CommonOp());
|
||||
}
|
||||
else
|
||||
|
@ -331,7 +332,7 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
|
|||
{
|
||||
case InstructionOp::cop0:
|
||||
{
|
||||
FormatCopInstruction(dest, pc, state, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
|
||||
FormatCopInstruction(dest, pc, regs, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -355,14 +356,14 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
|
|||
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
|
||||
const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1));
|
||||
if (link)
|
||||
FormatInstruction(dest, inst, pc, state, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
|
||||
FormatInstruction(dest, inst, pc, regs, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
|
||||
else
|
||||
FormatInstruction(dest, inst, pc, state, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
|
||||
FormatInstruction(dest, inst, pc, regs, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
FormatInstruction(dest, inst, pc, state, s_base_table[static_cast<u8>(inst.op.GetValue())]);
|
||||
FormatInstruction(dest, inst, pc, regs, s_base_table[static_cast<u8>(inst.op.GetValue())]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,5 @@
|
|||
#include "cpu_types.h"
|
||||
|
||||
namespace CPU {
|
||||
class Core;
|
||||
|
||||
void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state = nullptr);
|
||||
void DisassembleInstruction(String* dest, u32 pc, u32 bits, Registers* regs = nullptr);
|
||||
} // namespace CPU
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "common/log.h"
|
||||
#include "cpu_core.h"
|
||||
#include "cpu_disasm.h"
|
||||
#include "gte.h"
|
||||
Log_SetChannel(CPU::Recompiler);
|
||||
|
||||
// TODO: Turn load+sext/zext into a single signed/unsigned load
|
||||
|
@ -12,7 +13,7 @@ namespace CPU::Recompiler {
|
|||
|
||||
u32 CodeGenerator::CalculateRegisterOffset(Reg reg)
|
||||
{
|
||||
return u32(offsetof(Core, m_regs.r[0]) + (static_cast<u32>(reg) * sizeof(u32)));
|
||||
return u32(offsetof(State, regs.r[0]) + (static_cast<u32>(reg) * sizeof(u32)));
|
||||
}
|
||||
|
||||
bool CodeGenerator::CompileBlock(const CodeBlock* block, CodeBlock::HostCodePointer* out_host_code,
|
||||
|
@ -802,7 +803,7 @@ void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Excep
|
|||
m_register_cache.FlushAllGuestRegisters(true, true);
|
||||
m_register_cache.FlushLoadDelay(true);
|
||||
|
||||
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), epc, ri_bits);
|
||||
EmitFunctionCall(nullptr, &Thunks::RaiseException, epc, ri_bits);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -814,7 +815,7 @@ void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Excep
|
|||
EmitBranch(GetCurrentFarCodePointer());
|
||||
|
||||
SwitchToFarCode();
|
||||
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), epc, ri_bits);
|
||||
EmitFunctionCall(nullptr, &Thunks::RaiseException, epc, ri_bits);
|
||||
EmitExceptionExit();
|
||||
SwitchToNearCode();
|
||||
|
||||
|
@ -825,7 +826,7 @@ void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Excep
|
|||
|
||||
void CodeGenerator::BlockPrologue()
|
||||
{
|
||||
EmitStoreCPUStructField(offsetof(Core, m_exception_raised), Value::FromConstantU8(0));
|
||||
EmitStoreCPUStructField(offsetof(State, exception_raised), Value::FromConstantU8(0));
|
||||
|
||||
// we don't know the state of the last block, so assume load delays might be in progress
|
||||
// TODO: Pull load delay into register cache
|
||||
|
@ -859,21 +860,21 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou
|
|||
if (m_branch_was_taken_dirty)
|
||||
{
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_8);
|
||||
EmitLoadCPUStructField(temp.host_reg, RegSize_8, offsetof(Core, m_branch_was_taken));
|
||||
EmitStoreCPUStructField(offsetof(Core, m_current_instruction_was_branch_taken), temp);
|
||||
EmitStoreCPUStructField(offsetof(Core, m_branch_was_taken), Value::FromConstantU8(0));
|
||||
EmitLoadCPUStructField(temp.host_reg, RegSize_8, offsetof(State, branch_was_taken));
|
||||
EmitStoreCPUStructField(offsetof(State, current_instruction_was_branch_taken), temp);
|
||||
EmitStoreCPUStructField(offsetof(State, branch_was_taken), Value::FromConstantU8(0));
|
||||
m_current_instruction_was_branch_taken_dirty = true;
|
||||
m_branch_was_taken_dirty = false;
|
||||
}
|
||||
else if (m_current_instruction_was_branch_taken_dirty)
|
||||
{
|
||||
EmitStoreCPUStructField(offsetof(Core, m_current_instruction_was_branch_taken), Value::FromConstantU8(0));
|
||||
EmitStoreCPUStructField(offsetof(State, current_instruction_was_branch_taken), Value::FromConstantU8(0));
|
||||
m_current_instruction_was_branch_taken_dirty = false;
|
||||
}
|
||||
|
||||
if (m_current_instruction_in_branch_delay_slot_dirty && !cbi.is_branch_delay_slot)
|
||||
{
|
||||
EmitStoreCPUStructField(offsetof(Core, m_current_instruction_in_branch_delay_slot), Value::FromConstantU8(0));
|
||||
EmitStoreCPUStructField(offsetof(State, current_instruction_in_branch_delay_slot), Value::FromConstantU8(0));
|
||||
m_current_instruction_in_branch_delay_slot_dirty = false;
|
||||
}
|
||||
|
||||
|
@ -894,7 +895,7 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou
|
|||
if (cbi.is_branch_delay_slot)
|
||||
{
|
||||
// m_current_instruction_in_branch_delay_slot = true
|
||||
EmitStoreCPUStructField(offsetof(Core, m_current_instruction_in_branch_delay_slot), Value::FromConstantU8(1));
|
||||
EmitStoreCPUStructField(offsetof(State, current_instruction_in_branch_delay_slot), Value::FromConstantU8(1));
|
||||
m_current_instruction_in_branch_delay_slot_dirty = true;
|
||||
}
|
||||
|
||||
|
@ -931,7 +932,7 @@ void CodeGenerator::AddPendingCycles(bool commit)
|
|||
if (m_delayed_cycles_add == 0)
|
||||
return;
|
||||
|
||||
EmitAddCPUStructField(offsetof(Core, m_pending_ticks), Value::FromConstantU32(m_delayed_cycles_add));
|
||||
EmitAddCPUStructField(offsetof(State, pending_ticks), Value::FromConstantU32(m_delayed_cycles_add));
|
||||
|
||||
if (commit)
|
||||
m_delayed_cycles_add = 0;
|
||||
|
@ -939,7 +940,7 @@ void CodeGenerator::AddPendingCycles(bool commit)
|
|||
|
||||
void CodeGenerator::SetCurrentInstructionPC(const CodeBlockInstruction& cbi)
|
||||
{
|
||||
EmitStoreCPUStructField(offsetof(Core, m_current_instruction_pc), Value::FromConstantU32(cbi.pc));
|
||||
EmitStoreCPUStructField(offsetof(State, current_instruction_pc), Value::FromConstantU32(cbi.pc));
|
||||
}
|
||||
|
||||
bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
|
||||
|
@ -954,19 +955,19 @@ bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
|
|||
m_register_cache.WriteLoadDelayToCPU(true);
|
||||
}
|
||||
|
||||
EmitStoreCPUStructField(offsetof(Core, m_current_instruction.bits), Value::FromConstantU32(cbi.instruction.bits));
|
||||
EmitStoreCPUStructField(offsetof(State, current_instruction.bits), Value::FromConstantU32(cbi.instruction.bits));
|
||||
|
||||
// emit the function call
|
||||
if (CanInstructionTrap(cbi.instruction, m_block->key.user_mode))
|
||||
{
|
||||
// TODO: Use carry flag or something here too
|
||||
Value return_value = m_register_cache.AllocateScratch(RegSize_8);
|
||||
EmitFunctionCall(&return_value, &Thunks::InterpretInstruction, m_register_cache.GetCPUPtr());
|
||||
EmitFunctionCall(&return_value, &Thunks::InterpretInstruction);
|
||||
EmitExceptionExitOnBool(return_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFunctionCall(nullptr, &Thunks::InterpretInstruction, m_register_cache.GetCPUPtr());
|
||||
EmitFunctionCall(nullptr, &Thunks::InterpretInstruction);
|
||||
}
|
||||
|
||||
m_current_instruction_in_branch_delay_slot_dirty = cbi.is_branch_instruction;
|
||||
|
@ -1389,8 +1390,8 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi)
|
|||
EmitBindLabel(&branch_okay);
|
||||
|
||||
SwitchToFarCode();
|
||||
EmitFunctionCall(nullptr, &Thunks::RaiseAddressException, m_register_cache.GetCPUPtr(), branch_target,
|
||||
Value::FromConstantU8(0), Value::FromConstantU8(1));
|
||||
EmitFunctionCall(nullptr, &Thunks::RaiseAddressException, branch_target, Value::FromConstantU8(0),
|
||||
Value::FromConstantU8(1));
|
||||
EmitExceptionExit();
|
||||
SwitchToNearCode();
|
||||
|
||||
|
@ -1550,53 +1551,53 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
|
|||
switch (reg)
|
||||
{
|
||||
case Cop0Reg::BPC:
|
||||
offset = offsetof(Core, m_cop0_regs.BPC);
|
||||
offset = offsetof(State, cop0_regs.BPC);
|
||||
break;
|
||||
|
||||
case Cop0Reg::BPCM:
|
||||
offset = offsetof(Core, m_cop0_regs.BPCM);
|
||||
offset = offsetof(State, cop0_regs.BPCM);
|
||||
break;
|
||||
|
||||
case Cop0Reg::BDA:
|
||||
offset = offsetof(Core, m_cop0_regs.BDA);
|
||||
offset = offsetof(State, cop0_regs.BDA);
|
||||
break;
|
||||
|
||||
case Cop0Reg::BDAM:
|
||||
offset = offsetof(Core, m_cop0_regs.BDAM);
|
||||
offset = offsetof(State, cop0_regs.BDAM);
|
||||
break;
|
||||
|
||||
case Cop0Reg::DCIC:
|
||||
offset = offsetof(Core, m_cop0_regs.dcic.bits);
|
||||
offset = offsetof(State, cop0_regs.dcic.bits);
|
||||
write_mask = Cop0Registers::DCIC::WRITE_MASK;
|
||||
break;
|
||||
|
||||
case Cop0Reg::JUMPDEST:
|
||||
offset = offsetof(Core, m_cop0_regs.TAR);
|
||||
offset = offsetof(State, cop0_regs.TAR);
|
||||
write_mask = 0;
|
||||
break;
|
||||
|
||||
case Cop0Reg::BadVaddr:
|
||||
offset = offsetof(Core, m_cop0_regs.BadVaddr);
|
||||
offset = offsetof(State, cop0_regs.BadVaddr);
|
||||
write_mask = 0;
|
||||
break;
|
||||
|
||||
case Cop0Reg::SR:
|
||||
offset = offsetof(Core, m_cop0_regs.sr.bits);
|
||||
offset = offsetof(State, cop0_regs.sr.bits);
|
||||
write_mask = Cop0Registers::SR::WRITE_MASK;
|
||||
break;
|
||||
|
||||
case Cop0Reg::CAUSE:
|
||||
offset = offsetof(Core, m_cop0_regs.cause.bits);
|
||||
offset = offsetof(State, cop0_regs.cause.bits);
|
||||
write_mask = Cop0Registers::CAUSE::WRITE_MASK;
|
||||
break;
|
||||
|
||||
case Cop0Reg::EPC:
|
||||
offset = offsetof(Core, m_cop0_regs.EPC);
|
||||
offset = offsetof(State, cop0_regs.EPC);
|
||||
write_mask = 0;
|
||||
break;
|
||||
|
||||
case Cop0Reg::PRID:
|
||||
offset = offsetof(Core, m_cop0_regs.PRID);
|
||||
offset = offsetof(State, cop0_regs.PRID);
|
||||
write_mask = 0;
|
||||
break;
|
||||
|
||||
|
@ -1638,8 +1639,8 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
|
|||
|
||||
// m_cop0_regs.sr.IEc && ((m_cop0_regs.cause.Ip & m_cop0_regs.sr.Im) != 0)
|
||||
LabelType no_interrupt;
|
||||
EmitLoadCPUStructField(sr_value.host_reg, sr_value.size, offsetof(Core, m_cop0_regs.sr.bits));
|
||||
EmitLoadCPUStructField(cause_value.host_reg, cause_value.size, offsetof(Core, m_cop0_regs.cause.bits));
|
||||
EmitLoadCPUStructField(sr_value.host_reg, sr_value.size, offsetof(State, cop0_regs.sr.bits));
|
||||
EmitLoadCPUStructField(cause_value.host_reg, cause_value.size, offsetof(State, cop0_regs.cause.bits));
|
||||
EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt);
|
||||
EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value);
|
||||
EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00));
|
||||
|
@ -1680,7 +1681,7 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
|
|||
// shift mode bits right two, preserving upper bits
|
||||
static constexpr u32 mode_bits_mask = UINT32_C(0b1111);
|
||||
Value sr = m_register_cache.AllocateScratch(RegSize_32);
|
||||
EmitLoadCPUStructField(sr.host_reg, RegSize_32, offsetof(Core, m_cop0_regs.sr.bits));
|
||||
EmitLoadCPUStructField(sr.host_reg, RegSize_32, offsetof(State, cop0_regs.sr.bits));
|
||||
{
|
||||
Value new_mode_bits = m_register_cache.AllocateScratch(RegSize_32);
|
||||
EmitShr(new_mode_bits.host_reg, sr.host_reg, new_mode_bits.size, Value::FromConstantU32(2));
|
||||
|
@ -1689,7 +1690,7 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
|
|||
EmitOr(sr.host_reg, sr.host_reg, new_mode_bits);
|
||||
}
|
||||
|
||||
EmitStoreCPUStructField(offsetof(Core, m_cop0_regs.sr.bits), sr);
|
||||
EmitStoreCPUStructField(offsetof(State, cop0_regs.sr.bits), sr);
|
||||
|
||||
InstructionEpilogue(cbi);
|
||||
return true;
|
||||
|
@ -1717,13 +1718,13 @@ Value CodeGenerator::DoGTERegisterRead(u32 index)
|
|||
case 28: // IRGB
|
||||
case 29: // ORGB
|
||||
{
|
||||
EmitFunctionCall(&value, &Thunks::ReadGTERegister, m_register_cache.GetCPUPtr(), Value::FromConstantU32(index));
|
||||
EmitFunctionCall(&value, >E::ReadRegister, Value::FromConstantU32(index));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
EmitLoadCPUStructField(value.host_reg, RegSize_32, offsetof(Core, m_cop2.m_regs.r32[index]));
|
||||
EmitLoadCPUStructField(value.host_reg, RegSize_32, offsetof(State, gte_regs.r32[index]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1752,7 +1753,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
|
|||
{
|
||||
// sign-extend z component of vector registers
|
||||
Value temp = ConvertValueSize(value.ViewAsSize(RegSize_16), RegSize_32, true);
|
||||
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[index]), temp);
|
||||
EmitStoreCPUStructField(offsetof(State, gte_regs.r32[index]), temp);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -1765,7 +1766,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
|
|||
{
|
||||
// zero-extend unsigned values
|
||||
Value temp = ConvertValueSize(value.ViewAsSize(RegSize_16), RegSize_32, false);
|
||||
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[index]), temp);
|
||||
EmitStoreCPUStructField(offsetof(State, gte_regs.r32[index]), temp);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -1776,15 +1777,15 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
|
|||
Value temp = m_register_cache.AllocateScratch(RegSize_32);
|
||||
|
||||
// SXY0 <- SXY1
|
||||
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(Core, m_cop2.m_regs.r32[13]));
|
||||
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[12]), temp);
|
||||
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(State, gte_regs.r32[13]));
|
||||
EmitStoreCPUStructField(offsetof(State, gte_regs.r32[12]), temp);
|
||||
|
||||
// SXY1 <- SXY2
|
||||
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(Core, m_cop2.m_regs.r32[14]));
|
||||
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[13]), temp);
|
||||
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(State, gte_regs.r32[14]));
|
||||
EmitStoreCPUStructField(offsetof(State, gte_regs.r32[13]), temp);
|
||||
|
||||
// SXY2 <- SXYP
|
||||
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[14]), value);
|
||||
EmitStoreCPUStructField(offsetof(State, gte_regs.r32[14]), value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -1793,8 +1794,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
|
|||
case 30: // LZCS
|
||||
case 63: // FLAG
|
||||
{
|
||||
EmitFunctionCall(nullptr, &Thunks::WriteGTERegister, m_register_cache.GetCPUPtr(), Value::FromConstantU32(index),
|
||||
value);
|
||||
EmitFunctionCall(nullptr, >E::WriteRegister, Value::FromConstantU32(index), value);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1808,7 +1808,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
|
|||
default:
|
||||
{
|
||||
// written as-is, 2x16 or 1x32 bits
|
||||
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[index]), value);
|
||||
EmitStoreCPUStructField(offsetof(State, gte_regs.r32[index]), value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1878,7 +1878,7 @@ bool CodeGenerator::Compile_cop2(const CodeBlockInstruction& cbi)
|
|||
InstructionPrologue(cbi, 1);
|
||||
|
||||
Value instruction_bits = Value::FromConstantU32(cbi.instruction.bits & GTE::Instruction::REQUIRED_BITS_MASK);
|
||||
EmitFunctionCall(nullptr, &Thunks::ExecuteGTEInstruction, m_register_cache.GetCPUPtr(), instruction_bits);
|
||||
EmitFunctionCall(nullptr, GTE::GetInstructionImpl(cbi.instruction.bits), instruction_bits);
|
||||
|
||||
InstructionEpilogue(cbi);
|
||||
return true;
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace CPU::Recompiler {
|
|||
class CodeGenerator
|
||||
{
|
||||
public:
|
||||
CodeGenerator(Core* cpu, JitCodeBuffer* code_buffer, const ASMFunctions& asm_functions);
|
||||
CodeGenerator(JitCodeBuffer* code_buffer);
|
||||
~CodeGenerator();
|
||||
|
||||
static u32 CalculateRegisterOffset(Reg reg);
|
||||
|
@ -62,6 +62,8 @@ public:
|
|||
void EmitLoadCPUStructField(HostReg host_reg, RegSize size, u32 offset);
|
||||
void EmitStoreCPUStructField(u32 offset, const Value& value);
|
||||
void EmitAddCPUStructField(u32 offset, const Value& value);
|
||||
void EmitLoadGlobal(HostReg host_reg, RegSize size, const void* ptr);
|
||||
void EmitStoreGlobal(void* ptr, const Value& value);
|
||||
|
||||
// Automatically generates an exception handler.
|
||||
Value EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const Value& address, RegSize size);
|
||||
|
@ -188,9 +190,7 @@ private:
|
|||
bool Compile_cop0(const CodeBlockInstruction& cbi);
|
||||
bool Compile_cop2(const CodeBlockInstruction& cbi);
|
||||
|
||||
Core* m_cpu;
|
||||
JitCodeBuffer* m_code_buffer;
|
||||
const ASMFunctions& m_asm_functions;
|
||||
const CodeBlock* m_block = nullptr;
|
||||
const CodeBlockInstruction* m_block_start = nullptr;
|
||||
const CodeBlockInstruction* m_block_end = nullptr;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "cpu_core.h"
|
||||
#include "cpu_recompiler_code_generator.h"
|
||||
#include "cpu_recompiler_thunks.h"
|
||||
#include "cpu_core.h"
|
||||
Log_SetChannel(CPU::Recompiler);
|
||||
|
||||
namespace a64 = vixl::aarch64;
|
||||
|
@ -21,10 +23,7 @@ constexpr u64 FUNCTION_CALLER_SAVED_SPACE_RESERVE = 144; // 18 registers -> 224
|
|||
constexpr u64 FUNCTION_STACK_SIZE =
|
||||
FUNCTION_CALLEE_SAVED_SPACE_RESERVE + FUNCTION_CALLER_SAVED_SPACE_RESERVE + FUNCTION_CALL_SHADOW_SPACE;
|
||||
|
||||
static const a64::WRegister GetHostReg8(HostReg reg)
|
||||
{
|
||||
return a64::WRegister(reg);
|
||||
}
|
||||
static const a64::WRegister GetHostReg8(HostReg reg) { return a64::WRegister(reg); }
|
||||
|
||||
static const a64::WRegister GetHostReg8(const Value& value)
|
||||
{
|
||||
|
@ -32,10 +31,7 @@ static const a64::WRegister GetHostReg8(const Value& value)
|
|||
return a64::WRegister(value.host_reg);
|
||||
}
|
||||
|
||||
static const a64::WRegister GetHostReg16(HostReg reg)
|
||||
{
|
||||
return a64::WRegister(reg);
|
||||
}
|
||||
static const a64::WRegister GetHostReg16(HostReg reg) { return a64::WRegister(reg); }
|
||||
|
||||
static const a64::WRegister GetHostReg16(const Value& value)
|
||||
{
|
||||
|
@ -43,10 +39,7 @@ static const a64::WRegister GetHostReg16(const Value& value)
|
|||
return a64::WRegister(value.host_reg);
|
||||
}
|
||||
|
||||
static const a64::WRegister GetHostReg32(HostReg reg)
|
||||
{
|
||||
return a64::WRegister(reg);
|
||||
}
|
||||
static const a64::WRegister GetHostReg32(HostReg reg) { return a64::WRegister(reg); }
|
||||
|
||||
static const a64::WRegister GetHostReg32(const Value& value)
|
||||
{
|
||||
|
@ -54,10 +47,7 @@ static const a64::WRegister GetHostReg32(const Value& value)
|
|||
return a64::WRegister(value.host_reg);
|
||||
}
|
||||
|
||||
static const a64::XRegister GetHostReg64(HostReg reg)
|
||||
{
|
||||
return a64::XRegister(reg);
|
||||
}
|
||||
static const a64::XRegister GetHostReg64(HostReg reg) { return a64::XRegister(reg); }
|
||||
|
||||
static const a64::XRegister GetHostReg64(const Value& value)
|
||||
{
|
||||
|
@ -65,13 +55,10 @@ static const a64::XRegister GetHostReg64(const Value& value)
|
|||
return a64::XRegister(value.host_reg);
|
||||
}
|
||||
|
||||
static const a64::XRegister GetCPUPtrReg()
|
||||
{
|
||||
return GetHostReg64(RCPUPTR);
|
||||
}
|
||||
static const a64::XRegister GetCPUPtrReg() { return GetHostReg64(RCPUPTR); }
|
||||
|
||||
CodeGenerator::CodeGenerator(Core* cpu, JitCodeBuffer* code_buffer, const ASMFunctions& asm_functions)
|
||||
: m_cpu(cpu), m_code_buffer(code_buffer), m_asm_functions(asm_functions), m_register_cache(*this),
|
||||
CodeGenerator::CodeGenerator(JitCodeBuffer* code_buffer)
|
||||
: m_code_buffer(code_buffer), m_register_cache(*this),
|
||||
m_near_emitter(static_cast<vixl::byte*>(code_buffer->GetFreeCodePointer()), code_buffer->GetFreeCodeSpace(),
|
||||
a64::PositionDependentCode),
|
||||
m_far_emitter(static_cast<vixl::byte*>(code_buffer->GetFreeFarCodePointer()), code_buffer->GetFreeFarCodeSpace(),
|
||||
|
@ -111,10 +98,7 @@ const char* CodeGenerator::GetHostRegName(HostReg reg, RegSize size /*= HostPoin
|
|||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::AlignCodeBuffer(JitCodeBuffer* code_buffer)
|
||||
{
|
||||
code_buffer->Align(16, 0x90);
|
||||
}
|
||||
void CodeGenerator::AlignCodeBuffer(JitCodeBuffer* code_buffer) { code_buffer->Align(16, 0x90); }
|
||||
|
||||
void CodeGenerator::InitHostRegs()
|
||||
{
|
||||
|
@ -127,15 +111,9 @@ void CodeGenerator::InitHostRegs()
|
|||
m_register_cache.SetCPUPtrHostReg(RCPUPTR);
|
||||
}
|
||||
|
||||
void CodeGenerator::SwitchToFarCode()
|
||||
{
|
||||
m_emit = &m_far_emitter;
|
||||
}
|
||||
void CodeGenerator::SwitchToFarCode() { m_emit = &m_far_emitter; }
|
||||
|
||||
void CodeGenerator::SwitchToNearCode()
|
||||
{
|
||||
m_emit = &m_near_emitter;
|
||||
}
|
||||
void CodeGenerator::SwitchToNearCode() { m_emit = &m_near_emitter; }
|
||||
|
||||
void* CodeGenerator::GetCurrentNearCodePointer() const
|
||||
{
|
||||
|
@ -168,10 +146,10 @@ void CodeGenerator::EmitBeginBlock()
|
|||
const bool link_reg_allocated = m_register_cache.AllocateHostReg(30);
|
||||
DebugAssert(link_reg_allocated);
|
||||
|
||||
// Store the CPU struct pointer.
|
||||
// Store the CPU struct pointer. TODO: make this better.
|
||||
const bool cpu_reg_allocated = m_register_cache.AllocateHostReg(RCPUPTR);
|
||||
DebugAssert(cpu_reg_allocated);
|
||||
m_emit->Mov(GetCPUPtrReg(), GetHostReg64(RARG1));
|
||||
m_emit->Mov(GetCPUPtrReg(), reinterpret_cast<size_t>(&g_state));
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitEndBlock()
|
||||
|
@ -904,9 +882,13 @@ u32 CodeGenerator::PrepareStackForCall()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void CodeGenerator::RestoreStackAfterCall(u32 adjust_size)
|
||||
void CodeGenerator::RestoreStackAfterCall(u32 adjust_size) { m_register_cache.PopCallerSavedRegisters(); }
|
||||
|
||||
static s64 GetBranchDisplacement(const void* current, const void* target)
|
||||
{
|
||||
m_register_cache.PopCallerSavedRegisters();
|
||||
Assert(Common::IsAlignedPow2(reinterpret_cast<size_t>(current), 4));
|
||||
Assert(Common::IsAlignedPow2(reinterpret_cast<size_t>(target), 4));
|
||||
return static_cast<s64>((reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(current)) >> 2);
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr)
|
||||
|
@ -914,22 +896,25 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr)
|
|||
if (return_value)
|
||||
return_value->Discard();
|
||||
|
||||
// must be allocated before the stack push
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
|
||||
// shadow space allocate
|
||||
const u32 adjust_size = PrepareStackForCall();
|
||||
|
||||
// actually call the function
|
||||
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(temp));
|
||||
const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
|
||||
const bool use_blr = !vixl::IsInt26(displacement);
|
||||
if (use_blr)
|
||||
{
|
||||
m_emit->Mov(GetHostReg64(RRETURN), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(RRETURN));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->bl(displacement);
|
||||
}
|
||||
|
||||
// shadow space release
|
||||
RestoreStackAfterCall(adjust_size);
|
||||
|
||||
// must happen after the stack push
|
||||
temp.ReleaseAndClear();
|
||||
|
||||
// copy out return value if requested
|
||||
if (return_value)
|
||||
{
|
||||
|
@ -943,9 +928,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
if (return_value)
|
||||
return_value->Discard();
|
||||
|
||||
// must be allocated before the stack push
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
|
||||
// shadow space allocate
|
||||
const u32 adjust_size = PrepareStackForCall();
|
||||
|
||||
|
@ -953,15 +935,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
EmitCopyValue(RARG1, arg1);
|
||||
|
||||
// actually call the function
|
||||
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(temp));
|
||||
const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
|
||||
const bool use_blr = !vixl::IsInt26(displacement);
|
||||
if (use_blr)
|
||||
{
|
||||
m_emit->Mov(GetHostReg64(RRETURN), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(RRETURN));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->bl(displacement);
|
||||
}
|
||||
|
||||
// shadow space release
|
||||
RestoreStackAfterCall(adjust_size);
|
||||
|
||||
// must happen after the stack push
|
||||
temp.ReleaseAndClear();
|
||||
|
||||
// copy out return value if requested
|
||||
if (return_value)
|
||||
{
|
||||
|
@ -975,9 +963,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
if (return_value)
|
||||
return_value->Discard();
|
||||
|
||||
// must be allocated before the stack push
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
|
||||
// shadow space allocate
|
||||
const u32 adjust_size = PrepareStackForCall();
|
||||
|
||||
|
@ -986,15 +971,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
EmitCopyValue(RARG2, arg2);
|
||||
|
||||
// actually call the function
|
||||
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(temp));
|
||||
const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
|
||||
const bool use_blr = !vixl::IsInt26(displacement);
|
||||
if (use_blr)
|
||||
{
|
||||
m_emit->Mov(GetHostReg64(RRETURN), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(RRETURN));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->bl(displacement);
|
||||
}
|
||||
|
||||
// shadow space release
|
||||
RestoreStackAfterCall(adjust_size);
|
||||
|
||||
// must happen after the stack push
|
||||
temp.ReleaseAndClear();
|
||||
|
||||
// copy out return value if requested
|
||||
if (return_value)
|
||||
{
|
||||
|
@ -1009,9 +1000,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
if (return_value)
|
||||
m_register_cache.DiscardHostReg(return_value->GetHostRegister());
|
||||
|
||||
// must be allocated before the stack push
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
|
||||
// shadow space allocate
|
||||
const u32 adjust_size = PrepareStackForCall();
|
||||
|
||||
|
@ -1021,15 +1009,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
EmitCopyValue(RARG3, arg3);
|
||||
|
||||
// actually call the function
|
||||
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(temp));
|
||||
const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
|
||||
const bool use_blr = !vixl::IsInt26(displacement);
|
||||
if (use_blr)
|
||||
{
|
||||
m_emit->Mov(GetHostReg64(RRETURN), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(RRETURN));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->bl(displacement);
|
||||
}
|
||||
|
||||
// shadow space release
|
||||
RestoreStackAfterCall(adjust_size);
|
||||
|
||||
// must happen after the stack push
|
||||
temp.ReleaseAndClear();
|
||||
|
||||
// copy out return value if requested
|
||||
if (return_value)
|
||||
{
|
||||
|
@ -1044,8 +1038,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
if (return_value)
|
||||
return_value->Discard();
|
||||
|
||||
// must be allocated before the stack push
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
|
||||
// shadow space allocate
|
||||
const u32 adjust_size = PrepareStackForCall();
|
||||
|
@ -1057,15 +1049,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
|
|||
EmitCopyValue(RARG4, arg4);
|
||||
|
||||
// actually call the function
|
||||
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(temp));
|
||||
const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
|
||||
const bool use_blr = !vixl::IsInt26(displacement);
|
||||
if (use_blr)
|
||||
{
|
||||
m_emit->Mov(GetHostReg64(RRETURN), reinterpret_cast<uintptr_t>(ptr));
|
||||
m_emit->Blr(GetHostReg64(RRETURN));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->bl(displacement);
|
||||
}
|
||||
|
||||
// shadow space release
|
||||
RestoreStackAfterCall(adjust_size);
|
||||
|
||||
// must happen after the stack push
|
||||
temp.ReleaseAndClear();
|
||||
|
||||
// copy out return value if requested
|
||||
if (return_value)
|
||||
{
|
||||
|
@ -1222,15 +1220,15 @@ Value CodeGenerator::EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const
|
|||
switch (size)
|
||||
{
|
||||
case RegSize_8:
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), pc, address);
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, pc, address);
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address);
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, pc, address);
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), pc, address);
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, pc, address);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1285,15 +1283,15 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
|
|||
switch (value.size)
|
||||
{
|
||||
case RegSize_8:
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), pc, address, value);
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, pc, address, value);
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address, value);
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, pc, address, value);
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), pc, address, value);
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, pc, address, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1316,14 +1314,18 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
|
|||
m_register_cache.PopState();
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitLoadGlobal(HostReg host_reg, RegSize size, const void* ptr) { Panic("Not implemented"); }
|
||||
|
||||
void CodeGenerator::EmitStoreGlobal(void* ptr, const Value& value) { Panic("Not implemented"); }
|
||||
|
||||
void CodeGenerator::EmitFlushInterpreterLoadDelay()
|
||||
{
|
||||
Value reg = m_register_cache.AllocateScratch(RegSize_32);
|
||||
Value value = m_register_cache.AllocateScratch(RegSize_32);
|
||||
|
||||
const a64::MemOperand load_delay_reg(GetCPUPtrReg(), offsetof(Core, m_load_delay_reg));
|
||||
const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(Core, m_load_delay_value));
|
||||
const a64::MemOperand regs_base(GetCPUPtrReg(), offsetof(Core, m_regs.r[0]));
|
||||
const a64::MemOperand load_delay_reg(GetCPUPtrReg(), offsetof(State, load_delay_reg));
|
||||
const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(State, load_delay_value));
|
||||
const a64::MemOperand regs_base(GetCPUPtrReg(), offsetof(State, regs.r[0]));
|
||||
|
||||
a64::Label skip_flush;
|
||||
|
||||
|
@ -1339,7 +1341,7 @@ void CodeGenerator::EmitFlushInterpreterLoadDelay()
|
|||
|
||||
// reg = offset(r[0] + reg << 2)
|
||||
m_emit->Lsl(GetHostReg32(reg), GetHostReg32(reg), 2);
|
||||
m_emit->Add(GetHostReg32(reg), GetHostReg32(reg), offsetof(Core, m_regs.r[0]));
|
||||
m_emit->Add(GetHostReg32(reg), GetHostReg32(reg), offsetof(State, regs.r[0]));
|
||||
|
||||
// r[reg] = value
|
||||
m_emit->Str(GetHostReg32(value), a64::MemOperand(GetCPUPtrReg(), GetHostReg32(reg)));
|
||||
|
@ -1356,10 +1358,10 @@ void CodeGenerator::EmitMoveNextInterpreterLoadDelay()
|
|||
Value reg = m_register_cache.AllocateScratch(RegSize_32);
|
||||
Value value = m_register_cache.AllocateScratch(RegSize_32);
|
||||
|
||||
const a64::MemOperand load_delay_reg(GetCPUPtrReg(), offsetof(Core, m_load_delay_reg));
|
||||
const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(Core, m_load_delay_value));
|
||||
const a64::MemOperand next_load_delay_reg(GetCPUPtrReg(), offsetof(Core, m_next_load_delay_reg));
|
||||
const a64::MemOperand next_load_delay_value(GetCPUPtrReg(), offsetof(Core, m_next_load_delay_value));
|
||||
const a64::MemOperand load_delay_reg(GetCPUPtrReg(), offsetof(State, load_delay_reg));
|
||||
const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(State, load_delay_value));
|
||||
const a64::MemOperand next_load_delay_reg(GetCPUPtrReg(), offsetof(State, next_load_delay_reg));
|
||||
const a64::MemOperand next_load_delay_value(GetCPUPtrReg(), offsetof(State, next_load_delay_value));
|
||||
|
||||
m_emit->Ldrb(GetHostReg32(reg), next_load_delay_reg);
|
||||
m_emit->Ldr(GetHostReg32(value), next_load_delay_value);
|
||||
|
@ -1374,7 +1376,7 @@ void CodeGenerator::EmitCancelInterpreterLoadDelayForReg(Reg reg)
|
|||
if (!m_load_delay_dirty)
|
||||
return;
|
||||
|
||||
const a64::MemOperand load_delay_reg(GetCPUPtrReg(), offsetof(Core, m_load_delay_reg));
|
||||
const a64::MemOperand load_delay_reg(GetCPUPtrReg(), offsetof(State, load_delay_reg));
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_8);
|
||||
|
||||
a64::Label skip_cancel;
|
||||
|
@ -1632,11 +1634,6 @@ void CodeGenerator::EmitBranchIfBitClear(HostReg reg, RegSize size, u8 bit, Labe
|
|||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitBindLabel(LabelType* label)
|
||||
{
|
||||
m_emit->Bind(label);
|
||||
}
|
||||
|
||||
void ASMFunctions::Generate(JitCodeBuffer* code_buffer) {}
|
||||
void CodeGenerator::EmitBindLabel(LabelType* label) { m_emit->Bind(label); }
|
||||
|
||||
} // namespace CPU::Recompiler
|
||||
|
|
|
@ -17,8 +17,8 @@ void CodeGenerator::EmitStoreGuestRegister(Reg guest_reg, const Value& value)
|
|||
void CodeGenerator::EmitStoreInterpreterLoadDelay(Reg reg, const Value& value)
|
||||
{
|
||||
DebugAssert(value.size == RegSize_32 && value.IsInHostRegister());
|
||||
EmitStoreCPUStructField(offsetof(Core, m_load_delay_reg), Value::FromConstantU8(static_cast<u8>(reg)));
|
||||
EmitStoreCPUStructField(offsetof(Core, m_load_delay_value), value);
|
||||
EmitStoreCPUStructField(offsetof(State, load_delay_reg), Value::FromConstantU8(static_cast<u8>(reg)));
|
||||
EmitStoreCPUStructField(offsetof(State, load_delay_value), value);
|
||||
m_load_delay_dirty = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "cpu_core.h"
|
||||
#include "cpu_recompiler_code_generator.h"
|
||||
#include "cpu_recompiler_thunks.h"
|
||||
#include "common/align.h"
|
||||
|
||||
namespace CPU::Recompiler {
|
||||
|
||||
|
@ -73,8 +74,8 @@ static const Xbyak::Reg64 GetCPUPtrReg()
|
|||
return GetHostReg64(RCPUPTR);
|
||||
}
|
||||
|
||||
CodeGenerator::CodeGenerator(Core* cpu, JitCodeBuffer* code_buffer, const ASMFunctions& asm_functions)
|
||||
: m_cpu(cpu), m_code_buffer(code_buffer), m_asm_functions(asm_functions), m_register_cache(*this),
|
||||
CodeGenerator::CodeGenerator(JitCodeBuffer* code_buffer)
|
||||
: m_code_buffer(code_buffer), m_register_cache(*this),
|
||||
m_near_emitter(code_buffer->GetFreeCodeSpace(), code_buffer->GetFreeCodePointer()),
|
||||
m_far_emitter(code_buffer->GetFreeFarCodeSpace(), code_buffer->GetFreeFarCodePointer()), m_emit(&m_near_emitter)
|
||||
{
|
||||
|
@ -187,7 +188,7 @@ void CodeGenerator::EmitBeginBlock()
|
|||
// Store the CPU struct pointer.
|
||||
const bool cpu_reg_allocated = m_register_cache.AllocateHostReg(RCPUPTR);
|
||||
DebugAssert(cpu_reg_allocated);
|
||||
m_emit->mov(GetCPUPtrReg(), GetHostReg64(RARG1));
|
||||
m_emit->mov(GetCPUPtrReg(), reinterpret_cast<size_t>(&g_state));
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitEndBlock()
|
||||
|
@ -1305,8 +1306,15 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr)
|
|||
const u32 adjust_size = PrepareStackForCall();
|
||||
|
||||
// actually call the function
|
||||
m_emit->mov(GetHostReg64(RRETURN), reinterpret_cast<size_t>(ptr));
|
||||
m_emit->call(GetHostReg64(RRETURN));
|
||||
if (Xbyak::inner::IsInInt32(reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(m_emit->getCurr())))
|
||||
{
|
||||
m_emit->call(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->mov(GetHostReg64(RRETURN), reinterpret_cast<size_t>(ptr));
|
||||
m_emit->call(GetHostReg64(RRETURN));
|
||||
}
|
||||
|
||||
// shadow space release
|
||||
RestoreStackAfterCall(adjust_size);
|
||||
|
@ -1645,15 +1653,15 @@ Value CodeGenerator::EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const
|
|||
switch (size)
|
||||
{
|
||||
case RegSize_8:
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), pc, address);
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, pc, address);
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address);
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, pc, address);
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), pc, address);
|
||||
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, pc, address);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1706,15 +1714,15 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
|
|||
switch (value.size)
|
||||
{
|
||||
case RegSize_8:
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), pc, address, value);
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, pc, address, value);
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address, value);
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, pc, address, value);
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), pc, address, value);
|
||||
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, pc, address, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1735,14 +1743,208 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
|
|||
m_register_cache.PopState();
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitLoadGlobal(HostReg host_reg, RegSize size, const void* ptr)
|
||||
{
|
||||
const s64 displacement =
|
||||
static_cast<s64>(reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(m_emit->getCurr())) + 2;
|
||||
if (Xbyak::inner::IsInInt32(static_cast<u64>(displacement)))
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case RegSize_8:
|
||||
m_emit->mov(GetHostReg8(host_reg), m_emit->byte[m_emit->rip + ptr]);
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
m_emit->mov(GetHostReg16(host_reg), m_emit->word[m_emit->rip + ptr]);
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
m_emit->mov(GetHostReg32(host_reg), m_emit->dword[m_emit->rip + ptr]);
|
||||
break;
|
||||
|
||||
case RegSize_64:
|
||||
m_emit->mov(GetHostReg64(host_reg), m_emit->qword[m_emit->rip + ptr]);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
UnreachableCode();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
m_emit->mov(GetHostReg64(temp), reinterpret_cast<size_t>(ptr));
|
||||
switch (size)
|
||||
{
|
||||
case RegSize_8:
|
||||
m_emit->mov(GetHostReg8(host_reg), m_emit->byte[GetHostReg64(temp)]);
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
m_emit->mov(GetHostReg16(host_reg), m_emit->word[GetHostReg64(temp)]);
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
m_emit->mov(GetHostReg32(host_reg), m_emit->dword[GetHostReg64(temp)]);
|
||||
break;
|
||||
|
||||
case RegSize_64:
|
||||
m_emit->mov(GetHostReg64(host_reg), m_emit->qword[GetHostReg64(temp)]);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
UnreachableCode();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitStoreGlobal(void* ptr, const Value& value)
|
||||
{
|
||||
DebugAssert(value.IsInHostRegister() || value.IsConstant());
|
||||
|
||||
const s64 displacement =
|
||||
static_cast<s64>(reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(m_emit->getCurr()));
|
||||
if (Xbyak::inner::IsInInt32(static_cast<u64>(displacement)))
|
||||
{
|
||||
switch (value.size)
|
||||
{
|
||||
case RegSize_8:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
m_emit->mov(m_emit->byte[m_emit->rip + ptr], value.constant_value);
|
||||
else
|
||||
m_emit->mov(m_emit->byte[m_emit->rip + ptr], GetHostReg8(value.host_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
m_emit->mov(m_emit->word[m_emit->rip + ptr], value.constant_value);
|
||||
else
|
||||
m_emit->mov(m_emit->word[m_emit->rip + ptr], GetHostReg16(value.host_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
m_emit->mov(m_emit->dword[m_emit->rip + ptr], value.constant_value);
|
||||
else
|
||||
m_emit->mov(m_emit->dword[m_emit->rip + ptr], GetHostReg32(value.host_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
case RegSize_64:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
{
|
||||
// we need a temporary to load the value if it doesn't fit in 32-bits
|
||||
if (!Xbyak::inner::IsInInt32(value.constant_value))
|
||||
{
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
EmitCopyValue(temp.host_reg, value);
|
||||
m_emit->mov(m_emit->qword[m_emit->rip + ptr], GetHostReg64(temp.host_reg));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->mov(m_emit->qword[m_emit->rip + ptr], value.constant_value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->mov(m_emit->qword[m_emit->rip + ptr], GetHostReg64(value.host_reg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
UnreachableCode();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Value address_temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
m_emit->mov(GetHostReg64(address_temp), reinterpret_cast<size_t>(ptr));
|
||||
switch (value.size)
|
||||
{
|
||||
case RegSize_8:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
m_emit->mov(m_emit->byte[GetHostReg64(address_temp)], value.constant_value);
|
||||
else
|
||||
m_emit->mov(m_emit->byte[GetHostReg64(address_temp)], GetHostReg8(value.host_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
case RegSize_16:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
m_emit->mov(m_emit->word[GetHostReg64(address_temp)], value.constant_value);
|
||||
else
|
||||
m_emit->mov(m_emit->word[GetHostReg64(address_temp)], GetHostReg16(value.host_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
case RegSize_32:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
m_emit->mov(m_emit->dword[GetHostReg64(address_temp)], value.constant_value);
|
||||
else
|
||||
m_emit->mov(m_emit->dword[GetHostReg64(address_temp)], GetHostReg32(value.host_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
case RegSize_64:
|
||||
{
|
||||
if (value.IsConstant())
|
||||
{
|
||||
// we need a temporary to load the value if it doesn't fit in 32-bits
|
||||
if (!Xbyak::inner::IsInInt32(value.constant_value))
|
||||
{
|
||||
Value temp = m_register_cache.AllocateScratch(RegSize_64);
|
||||
EmitCopyValue(temp.host_reg, value);
|
||||
m_emit->mov(m_emit->qword[GetHostReg64(address_temp)], GetHostReg64(temp.host_reg));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->mov(m_emit->qword[GetHostReg64(address_temp)], value.constant_value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_emit->mov(m_emit->qword[GetHostReg64(address_temp)], GetHostReg64(value.host_reg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
UnreachableCode();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::EmitFlushInterpreterLoadDelay()
|
||||
{
|
||||
Value reg = m_register_cache.AllocateScratch(RegSize_8);
|
||||
Value value = m_register_cache.AllocateScratch(RegSize_32);
|
||||
|
||||
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_load_delay_reg)];
|
||||
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_value)];
|
||||
auto reg_ptr = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_regs.r[0]) + GetHostReg64(reg.host_reg) * 4];
|
||||
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
|
||||
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, load_delay_value)];
|
||||
auto reg_ptr = m_emit->dword[GetCPUPtrReg() + offsetof(State, regs.r[0]) + GetHostReg64(reg.host_reg) * 4];
|
||||
|
||||
Xbyak::Label skip_flush;
|
||||
|
||||
|
@ -1768,10 +1970,10 @@ void CodeGenerator::EmitMoveNextInterpreterLoadDelay()
|
|||
Value reg = m_register_cache.AllocateScratch(RegSize_8);
|
||||
Value value = m_register_cache.AllocateScratch(RegSize_32);
|
||||
|
||||
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_load_delay_reg)];
|
||||
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_value)];
|
||||
auto next_load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_reg)];
|
||||
auto next_load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_value)];
|
||||
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
|
||||
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, load_delay_value)];
|
||||
auto next_load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, next_load_delay_reg)];
|
||||
auto next_load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, next_load_delay_value)];
|
||||
|
||||
m_emit->mov(GetHostReg32(value), next_load_delay_value);
|
||||
m_emit->mov(GetHostReg8(reg), next_load_delay_reg);
|
||||
|
@ -1785,7 +1987,7 @@ void CodeGenerator::EmitCancelInterpreterLoadDelayForReg(Reg reg)
|
|||
if (!m_load_delay_dirty)
|
||||
return;
|
||||
|
||||
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_load_delay_reg)];
|
||||
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
|
||||
|
||||
Xbyak::Label skip_cancel;
|
||||
|
||||
|
@ -2047,6 +2249,4 @@ void CodeGenerator::EmitBindLabel(LabelType* label)
|
|||
m_emit->L(*label);
|
||||
}
|
||||
|
||||
void ASMFunctions::Generate(JitCodeBuffer* code_buffer) {}
|
||||
|
||||
} // namespace CPU::Recompiler
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
#include "cpu_recompiler_thunks.h"
|
||||
#include "cpu_code_cache.h"
|
||||
#include "cpu_core.h"
|
||||
|
||||
namespace CPU::Recompiler {
|
||||
|
||||
u32 Thunks::MakeRaiseExceptionInfo(Exception excode, const CodeBlockInstruction& cbi)
|
||||
{
|
||||
RaiseExceptionInfo ri = {};
|
||||
ri.excode = static_cast<u8>(excode);
|
||||
ri.BD = cbi.is_branch_delay_slot;
|
||||
ri.CE = cbi.instruction.cop.cop_n;
|
||||
return ri.bits;
|
||||
}
|
||||
|
||||
// TODO: Port thunks to "ASM routines", i.e. code in the jit buffer.
|
||||
|
||||
u64 Thunks::ReadMemoryByte(Core* cpu, u32 pc, u32 address)
|
||||
{
|
||||
cpu->m_current_instruction_pc = pc;
|
||||
|
||||
u32 temp = 0;
|
||||
const TickCount cycles = cpu->DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(address, temp);
|
||||
if (cycles < 0)
|
||||
{
|
||||
cpu->RaiseException(Exception::DBE);
|
||||
return UINT64_C(0xFFFFFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
cpu->m_pending_ticks += cycles;
|
||||
return ZeroExtend64(temp);
|
||||
}
|
||||
|
||||
u64 Thunks::ReadMemoryHalfWord(Core* cpu, u32 pc, u32 address)
|
||||
{
|
||||
cpu->m_current_instruction_pc = pc;
|
||||
|
||||
if (!cpu->DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(address))
|
||||
return UINT64_C(0xFFFFFFFFFFFFFFFF);
|
||||
|
||||
u32 temp = 0;
|
||||
const TickCount cycles = cpu->DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(address, temp);
|
||||
if (cycles < 0)
|
||||
{
|
||||
cpu->RaiseException(Exception::DBE);
|
||||
return UINT64_C(0xFFFFFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
cpu->m_pending_ticks += cycles;
|
||||
return ZeroExtend64(temp);
|
||||
}
|
||||
|
||||
u64 Thunks::ReadMemoryWord(Core* cpu, u32 pc, u32 address)
|
||||
{
|
||||
cpu->m_current_instruction_pc = pc;
|
||||
|
||||
if (!cpu->DoAlignmentCheck<MemoryAccessType::Read, MemoryAccessSize::Word>(address))
|
||||
return UINT64_C(0xFFFFFFFFFFFFFFFF);
|
||||
|
||||
u32 temp = 0;
|
||||
const TickCount cycles = cpu->DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(address, temp);
|
||||
if (cycles < 0)
|
||||
{
|
||||
cpu->RaiseException(Exception::DBE);
|
||||
return UINT64_C(0xFFFFFFFFFFFFFFFF);
|
||||
}
|
||||
|
||||
cpu->m_pending_ticks += cycles;
|
||||
return ZeroExtend64(temp);
|
||||
}
|
||||
|
||||
bool Thunks::WriteMemoryByte(Core* cpu, u32 pc, u32 address, u8 value)
|
||||
{
|
||||
cpu->m_current_instruction_pc = pc;
|
||||
|
||||
u32 temp = ZeroExtend32(value);
|
||||
const TickCount cycles = cpu->DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte>(address, temp);
|
||||
if (cycles < 0)
|
||||
{
|
||||
cpu->RaiseException(Exception::DBE);
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugAssert(cycles == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Thunks::WriteMemoryHalfWord(Core* cpu, u32 pc, u32 address, u16 value)
|
||||
{
|
||||
cpu->m_current_instruction_pc = pc;
|
||||
|
||||
if (!cpu->DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(address))
|
||||
return false;
|
||||
|
||||
u32 temp = ZeroExtend32(value);
|
||||
const TickCount cycles = cpu->DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord>(address, temp);
|
||||
if (cycles < 0)
|
||||
{
|
||||
cpu->RaiseException(Exception::DBE);
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugAssert(cycles == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Thunks::WriteMemoryWord(Core* cpu, u32 pc, u32 address, u32 value)
|
||||
{
|
||||
cpu->m_current_instruction_pc = pc;
|
||||
|
||||
if (!cpu->DoAlignmentCheck<MemoryAccessType::Write, MemoryAccessSize::Word>(address))
|
||||
return false;
|
||||
|
||||
const TickCount cycles = cpu->DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(address, value);
|
||||
if (cycles < 0)
|
||||
{
|
||||
cpu->RaiseException(Exception::DBE);
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugAssert(cycles == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Thunks::InterpretInstruction(Core* cpu)
|
||||
{
|
||||
cpu->ExecuteInstruction();
|
||||
return cpu->m_exception_raised;
|
||||
}
|
||||
|
||||
void Thunks::UpdateLoadDelay(Core* cpu)
|
||||
{
|
||||
cpu->UpdateLoadDelay();
|
||||
}
|
||||
|
||||
void Thunks::RaiseException(Core* cpu, u32 epc, u32 ri_bits)
|
||||
{
|
||||
const RaiseExceptionInfo ri{ri_bits};
|
||||
cpu->RaiseException(static_cast<Exception>(ri.excode), epc, ri.BD, cpu->m_current_instruction_was_branch_taken,
|
||||
ri.CE);
|
||||
}
|
||||
|
||||
void Thunks::RaiseAddressException(Core* cpu, u32 address, bool store, bool branch)
|
||||
{
|
||||
cpu->m_cop0_regs.BadVaddr = address;
|
||||
if (branch)
|
||||
cpu->RaiseException(Exception::AdEL, address, false, false, 0);
|
||||
else
|
||||
cpu->RaiseException(store ? Exception::AdES : Exception::AdEL);
|
||||
}
|
||||
|
||||
void Thunks::ExecuteGTEInstruction(Core* cpu, u32 instruction_bits)
|
||||
{
|
||||
cpu->m_cop2.ExecuteInstruction(GTE::Instruction{instruction_bits});
|
||||
}
|
||||
|
||||
u32 Thunks::ReadGTERegister(Core* cpu, u32 reg)
|
||||
{
|
||||
return cpu->m_cop2.ReadRegister(reg);
|
||||
}
|
||||
|
||||
void Thunks::WriteGTERegister(Core* cpu, u32 reg, u32 value)
|
||||
{
|
||||
cpu->m_cop2.WriteRegister(reg, value);
|
||||
}
|
||||
|
||||
} // namespace CPU::Recompiler
|
|
@ -1,67 +1,51 @@
|
|||
#pragma once
|
||||
#include "cpu_code_cache.h"
|
||||
#include "cpu_types.h"
|
||||
|
||||
class JitCodeBuffer;
|
||||
|
||||
namespace CPU {
|
||||
|
||||
struct CodeBlockInstruction;
|
||||
|
||||
class Core;
|
||||
namespace Recompiler::Thunks {
|
||||
|
||||
namespace Recompiler {
|
||||
|
||||
class Thunks
|
||||
union RaiseExceptionInfo
|
||||
{
|
||||
public:
|
||||
union RaiseExceptionInfo
|
||||
u32 bits;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 bits;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 excode;
|
||||
bool BD;
|
||||
u8 CE;
|
||||
u8 unused;
|
||||
};
|
||||
u8 excode;
|
||||
bool BD;
|
||||
u8 CE;
|
||||
u8 unused;
|
||||
};
|
||||
|
||||
static u32 MakeRaiseExceptionInfo(Exception excode, const CodeBlockInstruction& cbi);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Trampolines for calling back from the JIT
|
||||
// Needed because we can't cast member functions to void*...
|
||||
// TODO: Abuse carry flag or something else for exception
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static u64 ReadMemoryByte(Core* cpu, u32 pc, u32 address);
|
||||
static u64 ReadMemoryHalfWord(Core* cpu, u32 pc, u32 address);
|
||||
static u64 ReadMemoryWord(Core* cpu, u32 pc, u32 address);
|
||||
static bool WriteMemoryByte(Core* cpu, u32 pc, u32 address, u8 value);
|
||||
static bool WriteMemoryHalfWord(Core* cpu, u32 pc, u32 address, u16 value);
|
||||
static bool WriteMemoryWord(Core* cpu, u32 pc, u32 address, u32 value);
|
||||
static bool InterpretInstruction(Core* cpu);
|
||||
static void UpdateLoadDelay(Core* cpu);
|
||||
static void RaiseException(Core* cpu, u32 epc, u32 ri_bits);
|
||||
static void RaiseAddressException(Core* cpu, u32 address, bool store, bool branch);
|
||||
static void ExecuteGTEInstruction(Core* cpu, u32 instruction_bits);
|
||||
static u32 ReadGTERegister(Core* cpu, u32 reg);
|
||||
static void WriteGTERegister(Core* cpu, u32 reg, u32 value);
|
||||
};
|
||||
|
||||
class ASMFunctions
|
||||
ALWAYS_INLINE u32 MakeRaiseExceptionInfo(Exception excode, const CodeBlockInstruction& cbi)
|
||||
{
|
||||
public:
|
||||
bool (*read_memory_byte)(u32 address, u8* value);
|
||||
bool (*read_memory_word)(u32 address, u16* value);
|
||||
bool (*read_memory_dword)(u32 address, u32* value);
|
||||
void (*write_memory_byte)(u32 address, u8 value);
|
||||
void (*write_memory_word)(u32 address, u16 value);
|
||||
void (*write_memory_dword)(u32 address, u32 value);
|
||||
RaiseExceptionInfo ri = {};
|
||||
ri.excode = static_cast<u8>(excode);
|
||||
ri.BD = cbi.is_branch_delay_slot;
|
||||
ri.CE = cbi.instruction.cop.cop_n;
|
||||
return ri.bits;
|
||||
}
|
||||
|
||||
void Generate(JitCodeBuffer* code_buffer);
|
||||
};
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Trampolines for calling back from the JIT
|
||||
// Needed because we can't cast member functions to void*...
|
||||
// TODO: Abuse carry flag or something else for exception
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool InterpretInstruction();
|
||||
void RaiseException(u32 epc, u32 ri_bits);
|
||||
void RaiseAddressException(u32 address, bool store, bool branch);
|
||||
|
||||
} // namespace Recompiler
|
||||
// Memory access functions for the JIT - MSB is set on exception.
|
||||
u64 ReadMemoryByte(u32 pc, u32 address);
|
||||
u64 ReadMemoryHalfWord(u32 pc, u32 address);
|
||||
u64 ReadMemoryWord(u32 pc, u32 address);
|
||||
bool WriteMemoryByte(u32 pc, u32 address, u8 value);
|
||||
bool WriteMemoryHalfWord(u32 pc, u32 address, u16 value);
|
||||
bool WriteMemoryWord(u32 pc, u32 address, u32 value);
|
||||
|
||||
} // namespace Recompiler::Thunks
|
||||
|
||||
} // namespace CPU
|
|
@ -21,9 +21,6 @@
|
|||
|
||||
namespace CPU {
|
||||
|
||||
class Core;
|
||||
class CodeCache;
|
||||
|
||||
namespace Recompiler {
|
||||
|
||||
class CodeGenerator;
|
||||
|
@ -77,6 +74,9 @@ constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
|
|||
// Are shifts implicitly masked to 0..31?
|
||||
constexpr bool SHIFTS_ARE_IMPLICITLY_MASKED = true;
|
||||
|
||||
// Alignment of code stoarge.
|
||||
constexpr u32 CODE_STORAGE_ALIGNMENT = 4096;
|
||||
|
||||
// ABI selection
|
||||
#if defined(WIN32)
|
||||
#define ABI_WIN64 1
|
||||
|
@ -105,6 +105,9 @@ constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
|
|||
// Are shifts implicitly masked to 0..31?
|
||||
constexpr bool SHIFTS_ARE_IMPLICITLY_MASKED = true;
|
||||
|
||||
// Alignment of code stoarge.
|
||||
constexpr u32 CODE_STORAGE_ALIGNMENT = 4096;
|
||||
|
||||
#else
|
||||
|
||||
using HostReg = int;
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
namespace CPU {
|
||||
|
||||
class Core;
|
||||
|
||||
// Memory address mask used for fetching as well as loadstores (removes cached/uncached/user/kernel bits).
|
||||
enum : PhysicalMemoryAddress
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/string_util.h"
|
||||
#include "cpu_core.h"
|
||||
#include "gpu.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "mdec.h"
|
||||
|
@ -11,27 +12,27 @@
|
|||
#include "system.h"
|
||||
Log_SetChannel(DMA);
|
||||
|
||||
DMA g_dma;
|
||||
|
||||
DMA::DMA() = default;
|
||||
|
||||
DMA::~DMA() = default;
|
||||
|
||||
void DMA::Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom,
|
||||
SPU* spu, MDEC* mdec)
|
||||
void DMA::Initialize()
|
||||
{
|
||||
m_system = system;
|
||||
m_bus = bus;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_gpu = gpu;
|
||||
m_cdrom = cdrom;
|
||||
m_spu = spu;
|
||||
m_mdec = mdec;
|
||||
|
||||
m_max_slice_ticks = system->GetSettings().dma_max_slice_ticks;
|
||||
m_halt_ticks = system->GetSettings().dma_halt_ticks;
|
||||
m_max_slice_ticks = g_settings.dma_max_slice_ticks;
|
||||
m_halt_ticks = g_settings.dma_halt_ticks;
|
||||
|
||||
m_transfer_buffer.resize(32);
|
||||
m_unhalt_event = system->CreateTimingEvent("DMA Transfer Unhalt", 1, m_max_slice_ticks,
|
||||
std::bind(&DMA::UnhaltTransfer, this, std::placeholders::_1), false);
|
||||
m_unhalt_event = TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, m_max_slice_ticks,
|
||||
std::bind(&DMA::UnhaltTransfer, this, std::placeholders::_1), false);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void DMA::Shutdown()
|
||||
{
|
||||
m_unhalt_event.reset();
|
||||
}
|
||||
|
||||
void DMA::Reset()
|
||||
|
@ -238,7 +239,7 @@ void DMA::UpdateIRQ()
|
|||
if (m_DICR.master_flag)
|
||||
{
|
||||
Log_TracePrintf("Firing DMA master interrupt");
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::DMA);
|
||||
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::DMA);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +268,7 @@ bool DMA::TransferChannel(Channel channel)
|
|||
else
|
||||
used_ticks = TransferDeviceToMemory(channel, current_address & ADDRESS_MASK, increment, word_count);
|
||||
|
||||
m_system->StallCPU(used_ticks);
|
||||
CPU::AddPendingTicks(used_ticks);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -283,7 +284,7 @@ bool DMA::TransferChannel(Channel channel)
|
|||
Log_DebugPrintf("DMA%u: Copying linked list starting at 0x%08X to device", static_cast<u32>(channel),
|
||||
current_address & ADDRESS_MASK);
|
||||
|
||||
u8* ram_pointer = m_bus->GetRAM();
|
||||
u8* ram_pointer = Bus::g_ram;
|
||||
bool halt_transfer = false;
|
||||
while (cs.request)
|
||||
{
|
||||
|
@ -319,7 +320,7 @@ bool DMA::TransferChannel(Channel channel)
|
|||
}
|
||||
|
||||
cs.base_address = current_address;
|
||||
m_system->StallCPU(used_ticks);
|
||||
CPU::AddPendingTicks(used_ticks);
|
||||
|
||||
if (current_address & UINT32_C(0x800000))
|
||||
break;
|
||||
|
@ -370,7 +371,7 @@ bool DMA::TransferChannel(Channel channel)
|
|||
|
||||
cs.base_address = current_address & BASE_ADDRESS_MASK;
|
||||
cs.block_control.request.block_count = blocks_remaining;
|
||||
m_system->StallCPU(used_ticks);
|
||||
CPU::AddPendingTicks(used_ticks);
|
||||
|
||||
// finish transfer later if the request was cleared
|
||||
if (blocks_remaining > 0)
|
||||
|
@ -427,28 +428,34 @@ void DMA::UnhaltTransfer(TickCount ticks)
|
|||
|
||||
TickCount DMA::TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count)
|
||||
{
|
||||
const u32* src_pointer = reinterpret_cast<u32*>(m_bus->GetRAM() + address);
|
||||
const u32* src_pointer = reinterpret_cast<u32*>(Bus::g_ram + address);
|
||||
if (static_cast<s32>(increment) < 0 || ((address + (increment * word_count)) & ADDRESS_MASK) <= address)
|
||||
{
|
||||
// Use temp buffer if it's wrapping around
|
||||
if (m_transfer_buffer.size() < word_count)
|
||||
m_transfer_buffer.resize(word_count);
|
||||
src_pointer = m_transfer_buffer.data();
|
||||
m_bus->ReadWords(address, m_transfer_buffer.data(), word_count);
|
||||
|
||||
u8* ram_pointer = Bus::g_ram;
|
||||
for (u32 i = 0; i < word_count; i++)
|
||||
{
|
||||
std::memcpy(&m_transfer_buffer[i], &ram_pointer[address], sizeof(u32));
|
||||
address = (address + increment) & ADDRESS_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
switch (channel)
|
||||
{
|
||||
case Channel::GPU:
|
||||
m_gpu->DMAWrite(src_pointer, word_count);
|
||||
g_gpu->DMAWrite(src_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::SPU:
|
||||
m_spu->DMAWrite(src_pointer, word_count);
|
||||
g_spu.DMAWrite(src_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::MDECin:
|
||||
m_mdec->DMAWrite(src_pointer, word_count);
|
||||
g_mdec.DMAWrite(src_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::CDROM:
|
||||
|
@ -459,7 +466,7 @@ TickCount DMA::TransferMemoryToDevice(Channel channel, u32 address, u32 incremen
|
|||
break;
|
||||
}
|
||||
|
||||
return m_bus->GetDMARAMTickCount(word_count);
|
||||
return Bus::GetDMARAMTickCount(word_count);
|
||||
}
|
||||
|
||||
TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 increment, u32 word_count)
|
||||
|
@ -467,7 +474,7 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
|||
if (channel == Channel::OTC)
|
||||
{
|
||||
// clear ordering table
|
||||
u8* ram_pointer = m_bus->GetRAM();
|
||||
u8* ram_pointer = Bus::g_ram;
|
||||
const u32 word_count_less_1 = word_count - 1;
|
||||
for (u32 i = 0; i < word_count_less_1; i++)
|
||||
{
|
||||
|
@ -478,11 +485,11 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
|||
|
||||
const u32 terminator = UINT32_C(0xFFFFFF);
|
||||
std::memcpy(&ram_pointer[address], &terminator, sizeof(terminator));
|
||||
m_bus->InvalidateCodePages(address, word_count);
|
||||
return m_bus->GetDMARAMTickCount(word_count);
|
||||
Bus::InvalidateCodePages(address, word_count);
|
||||
return Bus::GetDMARAMTickCount(word_count);
|
||||
}
|
||||
|
||||
u32* dest_pointer = reinterpret_cast<u32*>(&m_bus->m_ram[address]);
|
||||
u32* dest_pointer = reinterpret_cast<u32*>(&Bus::g_ram[address]);
|
||||
if (static_cast<s32>(increment) < 0 || ((address + (increment * word_count)) & ADDRESS_MASK) <= address)
|
||||
{
|
||||
// Use temp buffer if it's wrapping around
|
||||
|
@ -495,19 +502,19 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
|||
switch (channel)
|
||||
{
|
||||
case Channel::GPU:
|
||||
m_gpu->DMARead(dest_pointer, word_count);
|
||||
g_gpu->DMARead(dest_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::CDROM:
|
||||
m_cdrom->DMARead(dest_pointer, word_count);
|
||||
g_cdrom.DMARead(dest_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::SPU:
|
||||
m_spu->DMARead(dest_pointer, word_count);
|
||||
g_spu.DMARead(dest_pointer, word_count);
|
||||
break;
|
||||
|
||||
case Channel::MDECout:
|
||||
m_mdec->DMARead(dest_pointer, word_count);
|
||||
g_mdec.DMARead(dest_pointer, word_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -518,7 +525,7 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
|||
|
||||
if (dest_pointer == m_transfer_buffer.data())
|
||||
{
|
||||
u8* ram_pointer = m_bus->m_ram;
|
||||
u8* ram_pointer = Bus::g_ram;
|
||||
for (u32 i = 0; i < word_count; i++)
|
||||
{
|
||||
std::memcpy(&ram_pointer[address], &m_transfer_buffer[i], sizeof(u32));
|
||||
|
@ -526,6 +533,6 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
|
|||
}
|
||||
}
|
||||
|
||||
m_bus->InvalidateCodePages(address, word_count);
|
||||
return m_bus->GetDMARAMTickCount(word_count);
|
||||
Bus::InvalidateCodePages(address, word_count);
|
||||
return Bus::GetDMARAMTickCount(word_count);
|
||||
}
|
||||
|
|
|
@ -7,14 +7,7 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class Bus;
|
||||
class InterruptController;
|
||||
class GPU;
|
||||
class CDROM;
|
||||
class SPU;
|
||||
class MDEC;
|
||||
|
||||
class DMA
|
||||
{
|
||||
|
@ -38,8 +31,8 @@ public:
|
|||
DMA();
|
||||
~DMA();
|
||||
|
||||
void Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, SPU* spu,
|
||||
MDEC* mdec);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -49,7 +42,6 @@ public:
|
|||
void SetRequest(Channel channel, bool request);
|
||||
|
||||
// changing interfaces
|
||||
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
||||
void SetMaxSliceTicks(TickCount ticks) { m_max_slice_ticks = ticks; }
|
||||
void SetHaltTicks(TickCount ticks) { m_halt_ticks = ticks; }
|
||||
|
||||
|
@ -82,14 +74,6 @@ private:
|
|||
// from memory -> device
|
||||
TickCount TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count);
|
||||
|
||||
System* m_system = nullptr;
|
||||
Bus* m_bus = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
GPU* m_gpu = nullptr;
|
||||
CDROM* m_cdrom = nullptr;
|
||||
SPU* m_spu = nullptr;
|
||||
MDEC* m_mdec = nullptr;
|
||||
|
||||
// configuration
|
||||
TickCount m_max_slice_ticks = 1000;
|
||||
TickCount m_halt_ticks = 100;
|
||||
|
@ -212,3 +196,5 @@ private:
|
|||
}
|
||||
} m_DICR = {};
|
||||
};
|
||||
|
||||
extern DMA g_dma;
|
|
@ -13,51 +13,45 @@
|
|||
#include <imgui.h>
|
||||
Log_SetChannel(GPU);
|
||||
|
||||
std::unique_ptr<GPU> g_gpu;
|
||||
|
||||
const GPU::GP0CommandHandlerTable GPU::s_GP0_command_handler_table = GPU::GenerateGP0CommandHandlerTable();
|
||||
|
||||
GPU::GPU() = default;
|
||||
|
||||
GPU::~GPU() = default;
|
||||
|
||||
bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers)
|
||||
bool GPU::Initialize(HostDisplay* host_display)
|
||||
{
|
||||
m_host_display = host_display;
|
||||
m_system = system;
|
||||
m_dma = dma;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_timers = timers;
|
||||
m_force_progressive_scan = m_system->GetSettings().gpu_disable_interlacing;
|
||||
m_force_ntsc_timings = m_system->GetSettings().gpu_force_ntsc_timings;
|
||||
m_crtc_state.display_aspect_ratio =
|
||||
Settings::GetDisplayAspectRatioValue(m_system->GetSettings().display_aspect_ratio);
|
||||
m_crtc_tick_event = m_system->CreateTimingEvent("GPU CRTC Tick", 1, 1,
|
||||
std::bind(&GPU::CRTCTickEvent, this, std::placeholders::_1), true);
|
||||
m_command_tick_event = m_system->CreateTimingEvent(
|
||||
m_force_progressive_scan = g_settings.gpu_disable_interlacing;
|
||||
m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings;
|
||||
m_crtc_state.display_aspect_ratio = Settings::GetDisplayAspectRatioValue(g_settings.display_aspect_ratio);
|
||||
m_crtc_tick_event = TimingEvents::CreateTimingEvent(
|
||||
"GPU CRTC Tick", 1, 1, std::bind(&GPU::CRTCTickEvent, this, std::placeholders::_1), true);
|
||||
m_command_tick_event = TimingEvents::CreateTimingEvent(
|
||||
"GPU Command Tick", 1, 1, std::bind(&GPU::CommandTickEvent, this, std::placeholders::_1), true);
|
||||
m_fifo_size = system->GetSettings().gpu_fifo_size;
|
||||
m_max_run_ahead = system->GetSettings().gpu_max_run_ahead;
|
||||
m_console_is_pal = system->IsPALRegion();
|
||||
m_fifo_size = g_settings.gpu_fifo_size;
|
||||
m_max_run_ahead = g_settings.gpu_max_run_ahead;
|
||||
m_console_is_pal = System::IsPALRegion();
|
||||
UpdateCRTCConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU::UpdateSettings()
|
||||
{
|
||||
const Settings& settings = m_system->GetSettings();
|
||||
m_force_progressive_scan = g_settings.gpu_disable_interlacing;
|
||||
m_fifo_size = g_settings.gpu_fifo_size;
|
||||
m_max_run_ahead = g_settings.gpu_max_run_ahead;
|
||||
|
||||
m_force_progressive_scan = settings.gpu_disable_interlacing;
|
||||
m_fifo_size = settings.gpu_fifo_size;
|
||||
m_max_run_ahead = settings.gpu_max_run_ahead;
|
||||
|
||||
if (m_force_ntsc_timings != settings.gpu_force_ntsc_timings || m_console_is_pal != m_system->IsPALRegion())
|
||||
if (m_force_ntsc_timings != g_settings.gpu_force_ntsc_timings || m_console_is_pal != System::IsPALRegion())
|
||||
{
|
||||
m_force_ntsc_timings = settings.gpu_force_ntsc_timings;
|
||||
m_console_is_pal = m_system->IsPALRegion();
|
||||
m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings;
|
||||
m_console_is_pal = System::IsPALRegion();
|
||||
UpdateCRTCConfig();
|
||||
}
|
||||
|
||||
m_crtc_state.display_aspect_ratio = Settings::GetDisplayAspectRatioValue(settings.display_aspect_ratio);
|
||||
m_crtc_state.display_aspect_ratio = Settings::GetDisplayAspectRatioValue(g_settings.display_aspect_ratio);
|
||||
|
||||
// Crop mode calls this, so recalculate the display area
|
||||
UpdateCRTCDisplayParameters();
|
||||
|
@ -75,7 +69,7 @@ void GPU::SoftReset()
|
|||
FlushRender();
|
||||
|
||||
m_GPUSTAT.bits = 0x14802000;
|
||||
m_GPUSTAT.pal_mode = m_system->IsPALRegion();
|
||||
m_GPUSTAT.pal_mode = System::IsPALRegion();
|
||||
m_drawing_area.Set(0, 0, 0, 0);
|
||||
m_drawing_area_changed = true;
|
||||
m_drawing_offset = {};
|
||||
|
@ -276,7 +270,7 @@ void GPU::UpdateDMARequest()
|
|||
break;
|
||||
}
|
||||
m_GPUSTAT.dma_data_request = dma_request;
|
||||
m_dma->SetRequest(DMA::Channel::GPU, dma_request);
|
||||
g_dma.SetRequest(DMA::Channel::GPU, dma_request);
|
||||
}
|
||||
|
||||
void GPU::UpdateGPUIdle()
|
||||
|
@ -498,7 +492,7 @@ void GPU::UpdateCRTCConfig()
|
|||
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
|
||||
}
|
||||
|
||||
m_system->SetThrottleFrequency(ComputeVerticalFrequency());
|
||||
System::SetThrottleFrequency(ComputeVerticalFrequency());
|
||||
|
||||
UpdateCRTCDisplayParameters();
|
||||
UpdateCRTCTickEvent();
|
||||
|
@ -507,7 +501,7 @@ void GPU::UpdateCRTCConfig()
|
|||
void GPU::UpdateCRTCDisplayParameters()
|
||||
{
|
||||
CRTCState& cs = m_crtc_state;
|
||||
const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode;
|
||||
const DisplayCropMode crop_mode = g_settings.display_crop_mode;
|
||||
|
||||
const u16 horizontal_total = m_GPUSTAT.pal_mode ? PAL_TICKS_PER_LINE : NTSC_TICKS_PER_LINE;
|
||||
const u16 vertical_total = m_GPUSTAT.pal_mode ? PAL_TOTAL_LINES : NTSC_TOTAL_LINES;
|
||||
|
@ -657,8 +651,8 @@ void GPU::UpdateCRTCTickEvent()
|
|||
(m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end ?
|
||||
(m_crtc_state.vertical_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) :
|
||||
(m_crtc_state.vertical_display_end - m_crtc_state.current_scanline));
|
||||
const TickCount lines_until_event = m_timers->IsExternalIRQEnabled(HBLANK_TIMER_INDEX) ?
|
||||
std::min(m_timers->GetTicksUntilIRQ(HBLANK_TIMER_INDEX), lines_until_vblank) :
|
||||
const TickCount lines_until_event = g_timers.IsExternalIRQEnabled(HBLANK_TIMER_INDEX) ?
|
||||
std::min(g_timers.GetTicksUntilIRQ(HBLANK_TIMER_INDEX), lines_until_vblank) :
|
||||
lines_until_vblank;
|
||||
const TickCount ticks_until_event =
|
||||
lines_until_event * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline;
|
||||
|
@ -697,8 +691,8 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||
const bool old_hblank = m_crtc_state.in_hblank;
|
||||
const bool new_hblank = m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_start ||
|
||||
m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end;
|
||||
if (!old_hblank && new_hblank && m_timers->IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||
m_timers->AddTicks(HBLANK_TIMER_INDEX, 1);
|
||||
if (!old_hblank && new_hblank && g_timers.IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||
g_timers.AddTicks(HBLANK_TIMER_INDEX, 1);
|
||||
|
||||
UpdateCRTCTickEvent();
|
||||
return;
|
||||
|
@ -715,10 +709,10 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||
const bool new_hblank = m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_start ||
|
||||
m_crtc_state.current_tick_in_scanline >= m_crtc_state.horizontal_display_end;
|
||||
m_crtc_state.in_hblank = new_hblank;
|
||||
if (m_timers->IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||
if (g_timers.IsUsingExternalClock(HBLANK_TIMER_INDEX))
|
||||
{
|
||||
const u32 hblank_timer_ticks = BoolToUInt32(!old_hblank) + BoolToUInt32(new_hblank) + (lines_to_draw - 1);
|
||||
m_timers->AddTicks(HBLANK_TIMER_INDEX, static_cast<TickCount>(hblank_timer_ticks));
|
||||
g_timers.AddTicks(HBLANK_TIMER_INDEX, static_cast<TickCount>(hblank_timer_ticks));
|
||||
}
|
||||
|
||||
while (lines_to_draw > 0)
|
||||
|
@ -734,7 +728,7 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||
if (prev_scanline < m_crtc_state.vertical_display_start &&
|
||||
m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end)
|
||||
{
|
||||
m_timers->SetGate(HBLANK_TIMER_INDEX, false);
|
||||
g_timers.SetGate(HBLANK_TIMER_INDEX, false);
|
||||
m_crtc_state.in_vblank = false;
|
||||
}
|
||||
|
||||
|
@ -745,12 +739,12 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||
if (new_vblank)
|
||||
{
|
||||
Log_DebugPrintf("Now in v-blank");
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::VBLANK);
|
||||
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::VBLANK);
|
||||
|
||||
// flush any pending draws and "scan out" the image
|
||||
FlushRender();
|
||||
UpdateDisplay();
|
||||
m_system->IncrementFrameNumber();
|
||||
System::FrameDone();
|
||||
|
||||
// switch fields early. this is needed so we draw to the correct one.
|
||||
if (m_GPUSTAT.vertical_interlace)
|
||||
|
@ -759,7 +753,7 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||
m_crtc_state.interlaced_field = 0;
|
||||
}
|
||||
|
||||
m_timers->SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
||||
g_timers.SetGate(HBLANK_TIMER_INDEX, new_vblank);
|
||||
m_crtc_state.in_vblank = new_vblank;
|
||||
}
|
||||
|
||||
|
@ -938,7 +932,7 @@ void GPU::WriteGP1(u32 value)
|
|||
{
|
||||
m_crtc_state.regs.display_address_start = param & CRTCState::Regs::DISPLAY_ADDRESS_START_MASK;
|
||||
Log_DebugPrintf("Display address start <- 0x%08X", m_crtc_state.regs.display_address_start);
|
||||
m_system->IncrementInternalFrameNumber();
|
||||
System::IncrementInternalFrameNumber();
|
||||
UpdateCRTCDisplayParameters();
|
||||
}
|
||||
break;
|
||||
|
@ -1339,7 +1333,7 @@ void GPU::DrawDebugStateWindow()
|
|||
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(450.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("GPU", &m_system->GetSettings().debugging.show_gpu_state))
|
||||
if (!ImGui::Begin("GPU", &g_settings.debugging.show_gpu_state))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
|
|
|
@ -15,10 +15,7 @@ class StateWrapper;
|
|||
|
||||
class HostDisplay;
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class DMA;
|
||||
class InterruptController;
|
||||
class Timers;
|
||||
|
||||
class GPU
|
||||
|
@ -122,8 +119,7 @@ public:
|
|||
|
||||
virtual bool IsHardwareRenderer() const = 0;
|
||||
|
||||
virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers);
|
||||
virtual bool Initialize(HostDisplay* host_display);
|
||||
virtual void Reset();
|
||||
virtual bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -452,10 +448,6 @@ protected:
|
|||
}
|
||||
|
||||
HostDisplay* m_host_display = nullptr;
|
||||
System* m_system = nullptr;
|
||||
DMA* m_dma = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
Timers* m_timers = nullptr;
|
||||
|
||||
std::unique_ptr<TimingEvent> m_crtc_tick_event;
|
||||
std::unique_ptr<TimingEvent> m_command_tick_event;
|
||||
|
@ -757,3 +749,5 @@ private:
|
|||
};
|
||||
|
||||
IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(GPU::TextureMode);
|
||||
|
||||
extern std::unique_ptr<GPU> g_gpu;
|
||||
|
|
|
@ -205,7 +205,7 @@ bool GPU::HandleInterruptRequestCommand()
|
|||
if (!m_GPUSTAT.interrupt_request)
|
||||
{
|
||||
m_GPUSTAT.interrupt_request = true;
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::GPU);
|
||||
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::GPU);
|
||||
}
|
||||
|
||||
m_fifo.RemoveOne();
|
||||
|
@ -496,7 +496,7 @@ bool GPU::HandleCopyRectangleCPUToVRAMCommand()
|
|||
|
||||
void GPU::FinishVRAMWrite()
|
||||
{
|
||||
if (m_system->GetSettings().debugging.dump_cpu_to_vram_copies)
|
||||
if (g_settings.debugging.dump_cpu_to_vram_copies)
|
||||
{
|
||||
DumpVRAMToFile(StringUtil::StdStringFromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++).c_str(),
|
||||
m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * m_vram_transfer.width,
|
||||
|
@ -535,7 +535,7 @@ bool GPU::HandleCopyRectangleVRAMToCPUCommand()
|
|||
// ensure VRAM shadow is up to date
|
||||
ReadVRAM(m_vram_transfer.x, m_vram_transfer.y, m_vram_transfer.width, m_vram_transfer.height);
|
||||
|
||||
if (m_system->GetSettings().debugging.dump_vram_to_cpu_copies)
|
||||
if (g_settings.debugging.dump_vram_to_cpu_copies)
|
||||
{
|
||||
DumpVRAMToFile(StringUtil::StdStringFromFormat("vram_to_cpu_copy_%u.png", s_vram_to_cpu_dump_id++).c_str(),
|
||||
m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * VRAM_WIDTH,
|
||||
|
|
|
@ -8,34 +8,26 @@
|
|||
#include <sstream>
|
||||
Log_SetChannel(GPU_HW);
|
||||
|
||||
GPU_HW::GPU_HW() : GPU()
|
||||
{
|
||||
m_vram_ptr = m_vram_shadow.data();
|
||||
}
|
||||
GPU_HW::GPU_HW() : GPU() { m_vram_ptr = m_vram_shadow.data(); }
|
||||
|
||||
GPU_HW::~GPU_HW() = default;
|
||||
|
||||
bool GPU_HW::IsHardwareRenderer() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool GPU_HW::IsHardwareRenderer() const { return true; }
|
||||
|
||||
bool GPU_HW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers)
|
||||
bool GPU_HW::Initialize(HostDisplay* host_display)
|
||||
{
|
||||
if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
if (!GPU::Initialize(host_display))
|
||||
return false;
|
||||
|
||||
const Settings& settings = m_system->GetSettings();
|
||||
m_resolution_scale = settings.gpu_resolution_scale;
|
||||
m_resolution_scale = g_settings.gpu_resolution_scale;
|
||||
m_render_api = host_display->GetRenderAPI();
|
||||
m_true_color = settings.gpu_true_color;
|
||||
m_scaled_dithering = settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = settings.gpu_texture_filtering;
|
||||
m_true_color = g_settings.gpu_true_color;
|
||||
m_scaled_dithering = g_settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filtering;
|
||||
if (m_resolution_scale < 1 || m_resolution_scale > m_max_resolution_scale)
|
||||
{
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(5.0f, "Invalid resolution scale %ux specified. Maximum is %u.",
|
||||
m_resolution_scale, m_max_resolution_scale);
|
||||
g_host_interface->AddFormattedOSDMessage(5.0f, "Invalid resolution scale %ux specified. Maximum is %u.",
|
||||
m_resolution_scale, m_max_resolution_scale);
|
||||
m_resolution_scale = std::clamp<u32>(m_resolution_scale, 1u, m_max_resolution_scale);
|
||||
}
|
||||
|
||||
|
@ -79,11 +71,10 @@ void GPU_HW::UpdateSettings()
|
|||
{
|
||||
GPU::UpdateSettings();
|
||||
|
||||
const Settings& settings = m_system->GetSettings();
|
||||
m_resolution_scale = std::clamp<u32>(settings.gpu_resolution_scale, 1, m_max_resolution_scale);
|
||||
m_true_color = settings.gpu_true_color;
|
||||
m_scaled_dithering = settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = settings.gpu_texture_filtering;
|
||||
m_resolution_scale = std::clamp<u32>(g_settings.gpu_resolution_scale, 1, m_max_resolution_scale);
|
||||
m_true_color = g_settings.gpu_true_color;
|
||||
m_scaled_dithering = g_settings.gpu_scaled_dithering;
|
||||
m_texture_filtering = g_settings.gpu_texture_filtering;
|
||||
PrintSettingsToLog();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,7 @@ public:
|
|||
|
||||
virtual bool IsHardwareRenderer() const override;
|
||||
|
||||
virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers) override;
|
||||
virtual bool Initialize(HostDisplay* host_display) override;
|
||||
virtual void Reset() override;
|
||||
virtual bool DoState(StateWrapper& sw) override;
|
||||
virtual void UpdateSettings() override;
|
||||
|
|
|
@ -19,8 +19,7 @@ GPU_HW_D3D11::~GPU_HW_D3D11()
|
|||
}
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers)
|
||||
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display)
|
||||
{
|
||||
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
|
||||
{
|
||||
|
@ -30,7 +29,7 @@ bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dm
|
|||
|
||||
SetCapabilities();
|
||||
|
||||
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
if (!GPU_HW::Initialize(host_display))
|
||||
return false;
|
||||
|
||||
m_device = static_cast<ID3D11Device*>(host_display->GetRenderDevice());
|
||||
|
@ -38,8 +37,8 @@ bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dm
|
|||
if (!m_device || !m_context)
|
||||
return false;
|
||||
|
||||
m_shader_cache.Open(system->GetHostInterface()->GetShaderCacheBasePath(), m_device->GetFeatureLevel(),
|
||||
system->GetSettings().gpu_use_debug_device);
|
||||
m_shader_cache.Open(g_host_interface->GetShaderCacheBasePath(), m_device->GetFeatureLevel(),
|
||||
g_settings.gpu_use_debug_device);
|
||||
|
||||
if (!CreateFramebuffer())
|
||||
{
|
||||
|
@ -370,7 +369,7 @@ bool GPU_HW_D3D11::CompileShaders()
|
|||
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_true_color, m_scaled_dithering,
|
||||
m_texture_filtering, m_supports_dual_source_blend);
|
||||
|
||||
m_system->GetHostInterface()->DisplayLoadingScreen("Compiling shaders...");
|
||||
g_host_interface->DisplayLoadingScreen("Compiling shaders...");
|
||||
|
||||
m_screen_quad_vertex_shader =
|
||||
m_shader_cache.GetVertexShader(m_device.Get(), shadergen.GenerateScreenQuadVertexShader());
|
||||
|
@ -572,7 +571,7 @@ void GPU_HW_D3D11::UpdateDisplay()
|
|||
{
|
||||
GPU_HW::UpdateDisplay();
|
||||
|
||||
if (m_system->GetSettings().debugging.show_vram)
|
||||
if (g_settings.debugging.show_vram)
|
||||
{
|
||||
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
|
||||
0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
|
@ -789,7 +788,4 @@ void GPU_HW_D3D11::UpdateDepthBufferFromMaskBit()
|
|||
RestoreGraphicsAPIState();
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer()
|
||||
{
|
||||
return std::make_unique<GPU_HW_D3D11>();
|
||||
}
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer() { return std::make_unique<GPU_HW_D3D11>(); }
|
||||
|
|
|
@ -19,8 +19,7 @@ public:
|
|||
GPU_HW_D3D11();
|
||||
~GPU_HW_D3D11() override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers) override;
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
|
|
|
@ -27,8 +27,7 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
|
|||
}
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers)
|
||||
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display)
|
||||
{
|
||||
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL &&
|
||||
host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGLES)
|
||||
|
@ -39,9 +38,9 @@ bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* d
|
|||
|
||||
SetCapabilities(host_display);
|
||||
|
||||
m_shader_cache.Open(IsGLES(), system->GetHostInterface()->GetShaderCacheBasePath());
|
||||
m_shader_cache.Open(IsGLES(), g_host_interface->GetShaderCacheBasePath());
|
||||
|
||||
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
if (!GPU_HW::Initialize(host_display))
|
||||
return false;
|
||||
|
||||
if (!CreateFramebuffer())
|
||||
|
@ -337,7 +336,7 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
|||
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_true_color, m_scaled_dithering,
|
||||
m_texture_filtering, m_supports_dual_source_blend);
|
||||
|
||||
m_system->GetHostInterface()->DisplayLoadingScreen("Compiling Shaders...");
|
||||
g_host_interface->DisplayLoadingScreen("Compiling Shaders...");
|
||||
|
||||
for (u32 render_mode = 0; render_mode < 4; render_mode++)
|
||||
{
|
||||
|
@ -580,7 +579,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
|
|||
{
|
||||
GPU_HW::UpdateDisplay();
|
||||
|
||||
if (m_system->GetSettings().debugging.show_vram)
|
||||
if (g_settings.debugging.show_vram)
|
||||
{
|
||||
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
|
||||
m_vram_texture.GetWidth(), static_cast<s32>(m_vram_texture.GetHeight()), 0,
|
||||
|
|
|
@ -15,8 +15,7 @@ public:
|
|||
GPU_HW_OpenGL();
|
||||
~GPU_HW_OpenGL() override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers) override;
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
|
|
|
@ -25,8 +25,7 @@ GPU_HW_Vulkan::~GPU_HW_Vulkan()
|
|||
DestroyResources();
|
||||
}
|
||||
|
||||
bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display, System* system, DMA* dma,
|
||||
InterruptController* interrupt_controller, Timers* timers)
|
||||
bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
|
||||
{
|
||||
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::Vulkan)
|
||||
{
|
||||
|
@ -37,7 +36,7 @@ bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display, System* system, DMA* d
|
|||
Assert(g_vulkan_shader_cache);
|
||||
SetCapabilities();
|
||||
|
||||
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
if (!GPU_HW::Initialize(host_display))
|
||||
return false;
|
||||
|
||||
if (!CreatePipelineLayouts())
|
||||
|
@ -570,7 +569,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
|
|||
{VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST}};
|
||||
static constexpr std::array<VkPolygonMode, 2> polygon_mode_mapping = {{VK_POLYGON_MODE_LINE, VK_POLYGON_MODE_FILL}};
|
||||
|
||||
m_system->GetHostInterface()->DisplayLoadingScreen("Compiling Shaders...");
|
||||
g_host_interface->DisplayLoadingScreen("Compiling Shaders...");
|
||||
|
||||
VkDevice device = g_vulkan_context->GetDevice();
|
||||
VkPipelineCache pipeline_cache = g_vulkan_shader_cache->GetPipelineCache();
|
||||
|
@ -941,7 +940,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
|
|||
{
|
||||
GPU_HW::UpdateDisplay();
|
||||
|
||||
if (m_system->GetSettings().debugging.show_vram)
|
||||
if (g_settings.debugging.show_vram)
|
||||
{
|
||||
m_host_display->SetDisplayTexture(&m_vram_texture, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 0, 0,
|
||||
m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
|
|
|
@ -14,8 +14,7 @@ public:
|
|||
GPU_HW_Vulkan();
|
||||
~GPU_HW_Vulkan() override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers) override;
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
|
||||
void ResetGraphicsAPIState() override;
|
||||
|
|
|
@ -23,10 +23,9 @@ bool GPU_SW::IsHardwareRenderer() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GPU_SW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers)
|
||||
bool GPU_SW::Initialize(HostDisplay* host_display)
|
||||
{
|
||||
if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers))
|
||||
if (!GPU::Initialize(host_display))
|
||||
return false;
|
||||
|
||||
m_display_texture = host_display->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, nullptr, 0, true);
|
||||
|
@ -148,7 +147,7 @@ void GPU_SW::UpdateDisplay()
|
|||
// fill display texture
|
||||
m_display_texture_buffer.resize(VRAM_WIDTH * VRAM_HEIGHT);
|
||||
|
||||
if (!m_system->GetSettings().debugging.show_vram)
|
||||
if (!g_settings.debugging.show_vram)
|
||||
{
|
||||
if (IsDisplayDisabled())
|
||||
{
|
||||
|
|
|
@ -14,8 +14,7 @@ public:
|
|||
|
||||
bool IsHardwareRenderer() const override;
|
||||
|
||||
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller,
|
||||
Timers* timers) override;
|
||||
bool Initialize(HostDisplay* host_display) override;
|
||||
void Reset() override;
|
||||
|
||||
u16 GetPixel(u32 x, u32 y) const { return m_vram[VRAM_WIDTH * y + x]; }
|
||||
|
|
1622
src/core/gte.cpp
1622
src/core/gte.cpp
File diff suppressed because it is too large
Load Diff
119
src/core/gte.h
119
src/core/gte.h
|
@ -1,119 +1,24 @@
|
|||
#pragma once
|
||||
#include "common/state_wrapper.h"
|
||||
#include "gte_types.h"
|
||||
|
||||
namespace CPU {
|
||||
class Core;
|
||||
|
||||
namespace Recompiler {
|
||||
class CodeGenerator;
|
||||
}
|
||||
} // namespace CPU
|
||||
class StateWrapper;
|
||||
|
||||
namespace GTE {
|
||||
|
||||
class Core
|
||||
{
|
||||
public:
|
||||
friend CPU::Core;
|
||||
friend CPU::Recompiler::CodeGenerator;
|
||||
void Initialize();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
Core();
|
||||
~Core();
|
||||
// control registers are offset by +32
|
||||
u32 ReadRegister(u32 index);
|
||||
void WriteRegister(u32 index, u32 value);
|
||||
|
||||
ALWAYS_INLINE void SetWidescreenHack(bool enabled) { m_widescreen_hack = enabled; }
|
||||
// use with care, direct register access
|
||||
u32* GetRegisterPtr(u32 index);
|
||||
|
||||
void Initialize();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
void ExecuteInstruction(u32 inst_bits);
|
||||
|
||||
// control registers are offset by +32
|
||||
u32 ReadRegister(u32 index) const;
|
||||
void WriteRegister(u32 index, u32 value);
|
||||
|
||||
void ExecuteInstruction(Instruction inst);
|
||||
|
||||
private:
|
||||
static constexpr s64 MAC0_MIN_VALUE = -(INT64_C(1) << 31);
|
||||
static constexpr s64 MAC0_MAX_VALUE = (INT64_C(1) << 31) - 1;
|
||||
static constexpr s64 MAC123_MIN_VALUE = -(INT64_C(1) << 43);
|
||||
static constexpr s64 MAC123_MAX_VALUE = (INT64_C(1) << 43) - 1;
|
||||
static constexpr s32 IR0_MIN_VALUE = 0x0000;
|
||||
static constexpr s32 IR0_MAX_VALUE = 0x1000;
|
||||
static constexpr s32 IR123_MIN_VALUE = -(INT64_C(1) << 15);
|
||||
static constexpr s32 IR123_MAX_VALUE = (INT64_C(1) << 15) - 1;
|
||||
|
||||
// Checks for underflow/overflow.
|
||||
template<u32 index>
|
||||
void CheckMACOverflow(s64 value);
|
||||
|
||||
// Checks for underflow/overflow, sign-extending to 31/43 bits.
|
||||
template<u32 index>
|
||||
s64 SignExtendMACResult(s64 value);
|
||||
|
||||
template<u32 index>
|
||||
void TruncateAndSetMAC(s64 value, u8 shift);
|
||||
|
||||
template<u32 index>
|
||||
void TruncateAndSetMACAndIR(s64 value, u8 shift, bool lm);
|
||||
|
||||
template<u32 index>
|
||||
void TruncateAndSetIR(s32 value, bool lm);
|
||||
|
||||
template<u32 index>
|
||||
u32 TruncateRGB(s32 value);
|
||||
|
||||
void SetOTZ(s32 value);
|
||||
void PushSXY(s32 x, s32 y);
|
||||
void PushSZ(s32 value);
|
||||
void PushRGBFromMAC();
|
||||
|
||||
// Divide using Unsigned Newton-Raphson algorithm.
|
||||
u32 UNRDivide(u32 lhs, u32 rhs);
|
||||
|
||||
// 3x3 matrix * 3x1 vector, updates MAC[1-3] and IR[1-3]
|
||||
void MulMatVec(const s16 M[3][3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
|
||||
|
||||
// 3x3 matrix * 3x1 vector with translation, updates MAC[1-3] and IR[1-3]
|
||||
void MulMatVec(const s16 M[3][3], const s32 T[3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
|
||||
void MulMatVecBuggy(const s16 M[3][3], const s32 T[3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
|
||||
|
||||
// Interpolate colour, or as in nocash "MAC+(FC-MAC)*IR0".
|
||||
void InterpolateColor(s64 in_MAC1, s64 in_MAC2, s64 in_MAC3, u8 shift, bool lm);
|
||||
|
||||
void RTPS(const s16 V[3], u8 shift, bool lm, bool last);
|
||||
void NCS(const s16 V[3], u8 shift, bool lm);
|
||||
void NCCS(const s16 V[3], u8 shift, bool lm);
|
||||
void NCDS(const s16 V[3], u8 shift, bool lm);
|
||||
void DPCS(const u8 color[3], u8 shift, bool lm);
|
||||
|
||||
void Execute_MVMVA(Instruction inst);
|
||||
void Execute_SQR(Instruction inst);
|
||||
void Execute_OP(Instruction inst);
|
||||
void Execute_RTPS(Instruction inst);
|
||||
void Execute_RTPT(Instruction inst);
|
||||
void Execute_NCLIP(Instruction inst);
|
||||
void Execute_AVSZ3(Instruction inst);
|
||||
void Execute_AVSZ4(Instruction inst);
|
||||
void Execute_NCS(Instruction inst);
|
||||
void Execute_NCT(Instruction inst);
|
||||
void Execute_NCCS(Instruction inst);
|
||||
void Execute_NCCT(Instruction inst);
|
||||
void Execute_NCDS(Instruction inst);
|
||||
void Execute_NCDT(Instruction inst);
|
||||
void Execute_CC(Instruction inst);
|
||||
void Execute_CDP(Instruction inst);
|
||||
void Execute_DPCS(Instruction inst);
|
||||
void Execute_DPCT(Instruction inst);
|
||||
void Execute_DCPL(Instruction inst);
|
||||
void Execute_INTPL(Instruction inst);
|
||||
void Execute_GPL(Instruction inst);
|
||||
void Execute_GPF(Instruction inst);
|
||||
|
||||
Regs m_regs = {};
|
||||
bool m_widescreen_hack = false;
|
||||
};
|
||||
|
||||
#include "gte.inl"
|
||||
using InstructionImpl = void (*)(Instruction);
|
||||
InstructionImpl GetInstructionImpl(u32 inst_bits);
|
||||
|
||||
} // namespace GTE
|
117
src/core/gte.inl
117
src/core/gte.inl
|
@ -1,117 +0,0 @@
|
|||
#include "gte.h"
|
||||
|
||||
template<u32 index>
|
||||
void GTE::Core::CheckMACOverflow(s64 value)
|
||||
{
|
||||
constexpr s64 MIN_VALUE = (index == 0) ? MAC0_MIN_VALUE : MAC123_MIN_VALUE;
|
||||
constexpr s64 MAX_VALUE = (index == 0) ? MAC0_MAX_VALUE : MAC123_MAX_VALUE;
|
||||
if (value < MIN_VALUE)
|
||||
{
|
||||
if constexpr (index == 0)
|
||||
m_regs.FLAG.mac0_underflow = true;
|
||||
else if constexpr (index == 1)
|
||||
m_regs.FLAG.mac1_underflow = true;
|
||||
else if constexpr (index == 2)
|
||||
m_regs.FLAG.mac2_underflow = true;
|
||||
else if constexpr (index == 3)
|
||||
m_regs.FLAG.mac3_underflow = true;
|
||||
}
|
||||
else if (value > MAX_VALUE)
|
||||
{
|
||||
if constexpr (index == 0)
|
||||
m_regs.FLAG.mac0_overflow = true;
|
||||
else if constexpr (index == 1)
|
||||
m_regs.FLAG.mac1_overflow = true;
|
||||
else if constexpr (index == 2)
|
||||
m_regs.FLAG.mac2_overflow = true;
|
||||
else if constexpr (index == 3)
|
||||
m_regs.FLAG.mac3_overflow = true;
|
||||
}
|
||||
}
|
||||
|
||||
template<u32 index>
|
||||
s64 GTE::Core::SignExtendMACResult(s64 value)
|
||||
{
|
||||
CheckMACOverflow<index>(value);
|
||||
return SignExtendN < index == 0 ? 31 : 44 > (value);
|
||||
}
|
||||
|
||||
template<u32 index>
|
||||
void GTE::Core::TruncateAndSetMAC(s64 value, u8 shift)
|
||||
{
|
||||
CheckMACOverflow<index>(value);
|
||||
|
||||
// shift should be done before storing to avoid losing precision
|
||||
value >>= shift;
|
||||
|
||||
m_regs.dr32[24 + index] = Truncate32(static_cast<u64>(value));
|
||||
}
|
||||
|
||||
template<u32 index>
|
||||
void GTE::Core::TruncateAndSetIR(s32 value, bool lm)
|
||||
{
|
||||
constexpr s32 MIN_VALUE = (index == 0) ? IR0_MIN_VALUE : IR123_MIN_VALUE;
|
||||
constexpr s32 MAX_VALUE = (index == 0) ? IR0_MAX_VALUE : IR123_MAX_VALUE;
|
||||
const s32 actual_min_value = lm ? 0 : MIN_VALUE;
|
||||
if (value < actual_min_value)
|
||||
{
|
||||
value = actual_min_value;
|
||||
if constexpr (index == 0)
|
||||
m_regs.FLAG.ir0_saturated = true;
|
||||
else if constexpr (index == 1)
|
||||
m_regs.FLAG.ir1_saturated = true;
|
||||
else if constexpr (index == 2)
|
||||
m_regs.FLAG.ir2_saturated = true;
|
||||
else if constexpr (index == 3)
|
||||
m_regs.FLAG.ir3_saturated = true;
|
||||
}
|
||||
else if (value > MAX_VALUE)
|
||||
{
|
||||
value = MAX_VALUE;
|
||||
if constexpr (index == 0)
|
||||
m_regs.FLAG.ir0_saturated = true;
|
||||
else if constexpr (index == 1)
|
||||
m_regs.FLAG.ir1_saturated = true;
|
||||
else if constexpr (index == 2)
|
||||
m_regs.FLAG.ir2_saturated = true;
|
||||
else if constexpr (index == 3)
|
||||
m_regs.FLAG.ir3_saturated = true;
|
||||
}
|
||||
|
||||
// store sign-extended 16-bit value as 32-bit
|
||||
m_regs.dr32[8 + index] = value;
|
||||
}
|
||||
|
||||
template<u32 index>
|
||||
void GTE::Core::TruncateAndSetMACAndIR(s64 value, u8 shift, bool lm)
|
||||
{
|
||||
CheckMACOverflow<index>(value);
|
||||
|
||||
// shift should be done before storing to avoid losing precision
|
||||
value >>= shift;
|
||||
|
||||
// set MAC
|
||||
const s32 value32 = static_cast<s32>(value);
|
||||
m_regs.dr32[24 + index] = value32;
|
||||
|
||||
// set IR
|
||||
TruncateAndSetIR<index>(value32, lm);
|
||||
}
|
||||
|
||||
template<u32 index>
|
||||
u32 GTE::Core::TruncateRGB(s32 value)
|
||||
{
|
||||
if (value < 0 || value > 0xFF)
|
||||
{
|
||||
if constexpr (index == 0)
|
||||
m_regs.FLAG.color_r_saturated = true;
|
||||
else if constexpr (index == 1)
|
||||
m_regs.FLAG.color_g_saturated = true;
|
||||
else
|
||||
m_regs.FLAG.color_b_saturated = true;
|
||||
|
||||
return (value < 0) ? 0 : 0xFF;
|
||||
}
|
||||
|
||||
return static_cast<u32>(value);
|
||||
}
|
|
@ -3,9 +3,13 @@
|
|||
#include "types.h"
|
||||
|
||||
namespace GTE {
|
||||
static constexpr u32 NUM_DATA_REGS = 32;
|
||||
static constexpr u32 NUM_CONTROL_REGS = 32;
|
||||
static constexpr u32 NUM_REGS = NUM_DATA_REGS + NUM_CONTROL_REGS;
|
||||
|
||||
enum : u32
|
||||
{
|
||||
NUM_DATA_REGS = 32,
|
||||
NUM_CONTROL_REGS = 32,
|
||||
NUM_REGS = NUM_DATA_REGS + NUM_CONTROL_REGS
|
||||
};
|
||||
|
||||
union FLAGS
|
||||
{
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
#include "common/string_util.h"
|
||||
#include "controller.h"
|
||||
#include "cpu_core.h"
|
||||
#include "cpu_code_cache.h"
|
||||
#include "dma.h"
|
||||
#include "gpu.h"
|
||||
#include "gte.h"
|
||||
#include "host_display.h"
|
||||
#include "save_state_version.h"
|
||||
#include "system.h"
|
||||
|
@ -21,8 +23,13 @@
|
|||
#include <stdlib.h>
|
||||
Log_SetChannel(HostInterface);
|
||||
|
||||
HostInterface* g_host_interface;
|
||||
|
||||
HostInterface::HostInterface()
|
||||
{
|
||||
Assert(!g_host_interface);
|
||||
g_host_interface = this;
|
||||
|
||||
// we can get the program directory at construction time
|
||||
const std::string program_path = FileSystem::GetProgramPath();
|
||||
m_program_directory = FileSystem::GetPathDirectory(program_path.c_str());
|
||||
|
@ -31,7 +38,9 @@ HostInterface::HostInterface()
|
|||
HostInterface::~HostInterface()
|
||||
{
|
||||
// system should be shut down prior to the destructor
|
||||
Assert(!m_system && !m_audio_stream && !m_display);
|
||||
Assert(System::IsShutdown() && !m_audio_stream && !m_display);
|
||||
Assert(g_host_interface == this);
|
||||
g_host_interface = nullptr;
|
||||
}
|
||||
|
||||
bool HostInterface::Initialize()
|
||||
|
@ -44,20 +53,20 @@ void HostInterface::Shutdown() {}
|
|||
void HostInterface::CreateAudioStream()
|
||||
{
|
||||
Log_InfoPrintf("Creating '%s' audio stream, sample rate = %u, channels = %u, buffer size = %u",
|
||||
Settings::GetAudioBackendName(m_settings.audio_backend), AUDIO_SAMPLE_RATE, AUDIO_CHANNELS,
|
||||
m_settings.audio_buffer_size);
|
||||
Settings::GetAudioBackendName(g_settings.audio_backend), AUDIO_SAMPLE_RATE, AUDIO_CHANNELS,
|
||||
g_settings.audio_buffer_size);
|
||||
|
||||
m_audio_stream = CreateAudioStream(m_settings.audio_backend);
|
||||
m_audio_stream = CreateAudioStream(g_settings.audio_backend);
|
||||
|
||||
if (!m_audio_stream || !m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, m_settings.audio_buffer_size))
|
||||
if (!m_audio_stream || !m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, g_settings.audio_buffer_size))
|
||||
{
|
||||
ReportFormattedError("Failed to create or configure audio stream, falling back to null output.");
|
||||
m_audio_stream.reset();
|
||||
m_audio_stream = AudioStream::CreateNullAudioStream();
|
||||
m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, m_settings.audio_buffer_size);
|
||||
m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, g_settings.audio_buffer_size);
|
||||
}
|
||||
|
||||
m_audio_stream->SetOutputVolume(m_settings.audio_output_muted ? 0 : m_settings.audio_output_volume);
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_muted ? 0 : g_settings.audio_output_volume);
|
||||
}
|
||||
|
||||
bool HostInterface::BootSystem(const SystemBootParameters& parameters)
|
||||
|
@ -78,14 +87,13 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters)
|
|||
}
|
||||
|
||||
// set host display settings
|
||||
m_display->SetDisplayLinearFiltering(m_settings.display_linear_filtering);
|
||||
m_display->SetDisplayIntegerScaling(m_settings.display_integer_scaling);
|
||||
m_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering);
|
||||
m_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
|
||||
|
||||
// create the audio stream. this will never fail, since we'll just fall back to null
|
||||
CreateAudioStream();
|
||||
|
||||
m_system = System::Create(this);
|
||||
if (!m_system->Boot(parameters))
|
||||
if (!System::Boot(parameters))
|
||||
{
|
||||
ReportFormattedError("System failed to boot. The log may contain more information.");
|
||||
DestroySystem();
|
||||
|
@ -101,25 +109,22 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters)
|
|||
|
||||
void HostInterface::ResetSystem()
|
||||
{
|
||||
m_system->Reset();
|
||||
m_system->ResetPerformanceCounters();
|
||||
System::Reset();
|
||||
System::ResetPerformanceCounters();
|
||||
AddOSDMessage("System reset.");
|
||||
}
|
||||
|
||||
void HostInterface::PowerOffSystem()
|
||||
{
|
||||
if (!m_system)
|
||||
return;
|
||||
|
||||
DestroySystem();
|
||||
}
|
||||
|
||||
void HostInterface::DestroySystem()
|
||||
{
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return;
|
||||
|
||||
m_system.reset();
|
||||
System::Shutdown();
|
||||
m_audio_stream.reset();
|
||||
UpdateSoftwareCursor();
|
||||
ReleaseHostDisplay();
|
||||
|
@ -210,50 +215,50 @@ std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
|
|||
} while (0)
|
||||
|
||||
// Try the configured image.
|
||||
TRY_FILENAME(m_settings.bios_path.c_str());
|
||||
TRY_FILENAME(g_settings.bios_path.c_str());
|
||||
|
||||
// Try searching in the same folder for other region's images.
|
||||
switch (region)
|
||||
{
|
||||
case ConsoleRegion::NTSC_J:
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph3000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-11j.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph1000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-10j.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph5500.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-30j.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7500.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph9000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-40j.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph3000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-11j.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph1000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-10j.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5500.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-30j.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7500.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9000.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-40j.bin", false, false));
|
||||
break;
|
||||
|
||||
case ConsoleRegion::NTSC_U:
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph1001.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-22a.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph5501.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph5503.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7003.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-30a.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7001.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7501.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7503.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph9001.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph9003.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph9903.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-41a.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph1001.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-22a.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5501.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5503.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7003.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-30a.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7001.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7501.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7503.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9001.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9003.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9903.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-41a.bin", false, false));
|
||||
break;
|
||||
|
||||
case ConsoleRegion::PAL:
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph1002.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-21e.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph5502.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph5552.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-30e.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7002.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph7502.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph9002.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "ps-41e.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph1002.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-21e.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5502.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph5552.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-30e.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7002.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph7502.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "scph9002.bin", false, false));
|
||||
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_settings.bios_path.c_str(), "ps-41e.bin", false, false));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -266,8 +271,8 @@ std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
|
|||
// Fall back to the default image.
|
||||
Log_WarningPrintf("No suitable BIOS image for region %s could be located, using configured image '%s'. This may "
|
||||
"result in instability.",
|
||||
Settings::GetConsoleRegionName(region), m_settings.bios_path.c_str());
|
||||
return BIOS::LoadImageFromFile(m_settings.bios_path);
|
||||
Settings::GetConsoleRegionName(region), g_settings.bios_path.c_str());
|
||||
return BIOS::LoadImageFromFile(g_settings.bios_path);
|
||||
}
|
||||
|
||||
bool HostInterface::LoadState(const char* filename)
|
||||
|
@ -278,12 +283,12 @@ bool HostInterface::LoadState(const char* filename)
|
|||
|
||||
AddFormattedOSDMessage(2.0f, "Loading state from '%s'...", filename);
|
||||
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
if (!m_system->LoadState(stream.get()))
|
||||
if (!System::LoadState(stream.get()))
|
||||
{
|
||||
ReportFormattedError("Loading state from '%s' failed. Resetting.", filename);
|
||||
m_system->Reset();
|
||||
ResetSystem();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -295,7 +300,7 @@ bool HostInterface::LoadState(const char* filename)
|
|||
return false;
|
||||
}
|
||||
|
||||
m_system->ResetPerformanceCounters();
|
||||
System::ResetPerformanceCounters();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -307,7 +312,7 @@ bool HostInterface::SaveState(const char* filename)
|
|||
if (!stream)
|
||||
return false;
|
||||
|
||||
const bool result = m_system->SaveState(stream.get());
|
||||
const bool result = System::SaveState(stream.get());
|
||||
if (!result)
|
||||
{
|
||||
ReportFormattedError("Saving state to '%s' failed.", filename);
|
||||
|
@ -422,89 +427,86 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
|
||||
void HostInterface::LoadSettings(SettingsInterface& si)
|
||||
{
|
||||
m_settings.Load(si);
|
||||
g_settings.Load(si);
|
||||
}
|
||||
|
||||
void HostInterface::SaveSettings(SettingsInterface& si)
|
||||
{
|
||||
m_settings.Save(si);
|
||||
g_settings.Save(si);
|
||||
}
|
||||
|
||||
void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
||||
{
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
if (m_settings.gpu_renderer != old_settings.gpu_renderer ||
|
||||
m_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device)
|
||||
if (g_settings.gpu_renderer != old_settings.gpu_renderer ||
|
||||
g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device)
|
||||
{
|
||||
ReportFormattedMessage("Switching to %s%s GPU renderer.", Settings::GetRendererName(m_settings.gpu_renderer),
|
||||
m_settings.gpu_use_debug_device ? " (debug)" : "");
|
||||
ReportFormattedMessage("Switching to %s%s GPU renderer.", Settings::GetRendererName(g_settings.gpu_renderer),
|
||||
g_settings.gpu_use_debug_device ? " (debug)" : "");
|
||||
RecreateSystem();
|
||||
}
|
||||
|
||||
if (m_settings.audio_backend != old_settings.audio_backend ||
|
||||
m_settings.audio_buffer_size != old_settings.audio_buffer_size)
|
||||
if (g_settings.audio_backend != old_settings.audio_backend ||
|
||||
g_settings.audio_buffer_size != old_settings.audio_buffer_size)
|
||||
{
|
||||
if (m_settings.audio_backend != old_settings.audio_backend)
|
||||
if (g_settings.audio_backend != old_settings.audio_backend)
|
||||
ReportFormattedMessage("Switching to %s audio backend.",
|
||||
Settings::GetAudioBackendName(m_settings.audio_backend));
|
||||
Settings::GetAudioBackendName(g_settings.audio_backend));
|
||||
DebugAssert(m_audio_stream);
|
||||
m_audio_stream.reset();
|
||||
CreateAudioStream();
|
||||
m_audio_stream->PauseOutput(false);
|
||||
m_audio_stream->PauseOutput(System::IsPaused());
|
||||
}
|
||||
|
||||
if (m_settings.emulation_speed != old_settings.emulation_speed)
|
||||
m_system->UpdateThrottlePeriod();
|
||||
if (g_settings.emulation_speed != old_settings.emulation_speed)
|
||||
System::UpdateThrottlePeriod();
|
||||
|
||||
if (m_settings.cpu_execution_mode != old_settings.cpu_execution_mode)
|
||||
if (g_settings.cpu_execution_mode != old_settings.cpu_execution_mode)
|
||||
{
|
||||
ReportFormattedMessage("Switching to %s CPU execution mode.",
|
||||
Settings::GetCPUExecutionModeName(m_settings.cpu_execution_mode));
|
||||
m_system->SetCPUExecutionMode(m_settings.cpu_execution_mode);
|
||||
Settings::GetCPUExecutionModeName(g_settings.cpu_execution_mode));
|
||||
CPU::CodeCache::SetUseRecompiler(g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler);
|
||||
}
|
||||
|
||||
m_audio_stream->SetOutputVolume(m_settings.audio_output_muted ? 0 : m_settings.audio_output_volume);
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_muted ? 0 : g_settings.audio_output_volume);
|
||||
|
||||
if (m_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale ||
|
||||
m_settings.gpu_fifo_size != old_settings.gpu_fifo_size ||
|
||||
m_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead ||
|
||||
m_settings.gpu_true_color != old_settings.gpu_true_color ||
|
||||
m_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
|
||||
m_settings.gpu_texture_filtering != old_settings.gpu_texture_filtering ||
|
||||
m_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
|
||||
m_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
|
||||
m_settings.display_crop_mode != old_settings.display_crop_mode ||
|
||||
m_settings.display_aspect_ratio != old_settings.display_aspect_ratio)
|
||||
if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale ||
|
||||
g_settings.gpu_fifo_size != old_settings.gpu_fifo_size ||
|
||||
g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead ||
|
||||
g_settings.gpu_true_color != old_settings.gpu_true_color ||
|
||||
g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
|
||||
g_settings.gpu_texture_filtering != old_settings.gpu_texture_filtering ||
|
||||
g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
|
||||
g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
|
||||
g_settings.display_crop_mode != old_settings.display_crop_mode ||
|
||||
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio)
|
||||
{
|
||||
m_system->UpdateGPUSettings();
|
||||
g_gpu->UpdateSettings();
|
||||
}
|
||||
|
||||
if (m_settings.cdrom_read_thread != old_settings.cdrom_read_thread)
|
||||
m_system->GetCDROM()->SetUseReadThread(m_settings.cdrom_read_thread);
|
||||
if (g_settings.cdrom_read_thread != old_settings.cdrom_read_thread)
|
||||
g_cdrom.SetUseReadThread(g_settings.cdrom_read_thread);
|
||||
|
||||
if (m_settings.memory_card_types != old_settings.memory_card_types ||
|
||||
m_settings.memory_card_paths != old_settings.memory_card_paths)
|
||||
if (g_settings.memory_card_types != old_settings.memory_card_types ||
|
||||
g_settings.memory_card_paths != old_settings.memory_card_paths)
|
||||
{
|
||||
m_system->UpdateMemoryCards();
|
||||
System::UpdateMemoryCards();
|
||||
}
|
||||
|
||||
m_system->GetDMA()->SetMaxSliceTicks(m_settings.dma_max_slice_ticks);
|
||||
m_system->GetDMA()->SetHaltTicks(m_settings.dma_halt_ticks);
|
||||
|
||||
if (m_settings.gpu_widescreen_hack != old_settings.gpu_widescreen_hack)
|
||||
m_system->GetCPU()->GetCop2().SetWidescreenHack(m_settings.gpu_widescreen_hack);
|
||||
g_dma.SetMaxSliceTicks(g_settings.dma_max_slice_ticks);
|
||||
g_dma.SetHaltTicks(g_settings.dma_halt_ticks);
|
||||
}
|
||||
|
||||
bool controllers_updated = false;
|
||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||
{
|
||||
if (m_settings.controller_types[i] != old_settings.controller_types[i])
|
||||
if (g_settings.controller_types[i] != old_settings.controller_types[i])
|
||||
{
|
||||
if (m_system && !controllers_updated)
|
||||
if (!System::IsShutdown() && !controllers_updated)
|
||||
{
|
||||
m_system->UpdateControllers();
|
||||
m_system->ResetControllers();
|
||||
System::UpdateControllers();
|
||||
System::ResetControllers();
|
||||
UpdateSoftwareCursor();
|
||||
controllers_updated = true;
|
||||
}
|
||||
|
@ -512,18 +514,18 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
OnControllerTypeChanged(i);
|
||||
}
|
||||
|
||||
if (m_system && !controllers_updated)
|
||||
if (!System::IsShutdown() && !controllers_updated)
|
||||
{
|
||||
m_system->UpdateControllerSettings();
|
||||
System::UpdateControllerSettings();
|
||||
UpdateSoftwareCursor();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_display && m_settings.display_linear_filtering != old_settings.display_linear_filtering)
|
||||
m_display->SetDisplayLinearFiltering(m_settings.display_linear_filtering);
|
||||
if (m_display && g_settings.display_linear_filtering != old_settings.display_linear_filtering)
|
||||
m_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering);
|
||||
|
||||
if (m_display && m_settings.display_integer_scaling != old_settings.display_integer_scaling)
|
||||
m_display->SetDisplayIntegerScaling(m_settings.display_integer_scaling);
|
||||
if (m_display && g_settings.display_integer_scaling != old_settings.display_integer_scaling)
|
||||
m_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
|
||||
}
|
||||
|
||||
void HostInterface::SetUserDirectoryToProgramDirectory()
|
||||
|
@ -620,35 +622,35 @@ float HostInterface::GetFloatSettingValue(const char* section, const char* key,
|
|||
|
||||
void HostInterface::ToggleSoftwareRendering()
|
||||
{
|
||||
if (!m_system || m_settings.gpu_renderer == GPURenderer::Software)
|
||||
if (System::IsShutdown() || g_settings.gpu_renderer == GPURenderer::Software)
|
||||
return;
|
||||
|
||||
const GPURenderer new_renderer =
|
||||
m_system->GetGPU()->IsHardwareRenderer() ? GPURenderer::Software : m_settings.gpu_renderer;
|
||||
g_gpu->IsHardwareRenderer() ? GPURenderer::Software : g_settings.gpu_renderer;
|
||||
|
||||
AddFormattedOSDMessage(2.0f, "Switching to %s renderer...", Settings::GetRendererDisplayName(new_renderer));
|
||||
m_system->RecreateGPU(new_renderer);
|
||||
System::RecreateGPU(new_renderer);
|
||||
}
|
||||
|
||||
void HostInterface::ModifyResolutionScale(s32 increment)
|
||||
{
|
||||
const u32 new_resolution_scale = std::clamp<u32>(
|
||||
static_cast<u32>(static_cast<s32>(m_settings.gpu_resolution_scale) + increment), 1, GPU::MAX_RESOLUTION_SCALE);
|
||||
if (new_resolution_scale == m_settings.gpu_resolution_scale)
|
||||
static_cast<u32>(static_cast<s32>(g_settings.gpu_resolution_scale) + increment), 1, GPU::MAX_RESOLUTION_SCALE);
|
||||
if (new_resolution_scale == g_settings.gpu_resolution_scale)
|
||||
return;
|
||||
|
||||
m_settings.gpu_resolution_scale = new_resolution_scale;
|
||||
AddFormattedOSDMessage(2.0f, "Resolution scale set to %ux (%ux%u)", m_settings.gpu_resolution_scale,
|
||||
GPU::VRAM_WIDTH * m_settings.gpu_resolution_scale,
|
||||
GPU::VRAM_HEIGHT * m_settings.gpu_resolution_scale);
|
||||
g_settings.gpu_resolution_scale = new_resolution_scale;
|
||||
AddFormattedOSDMessage(2.0f, "Resolution scale set to %ux (%ux%u)", g_settings.gpu_resolution_scale,
|
||||
GPU::VRAM_WIDTH * g_settings.gpu_resolution_scale,
|
||||
GPU::VRAM_HEIGHT * g_settings.gpu_resolution_scale);
|
||||
|
||||
if (m_system)
|
||||
m_system->GetGPU()->UpdateSettings();
|
||||
if (!System::IsShutdown())
|
||||
g_gpu->UpdateSettings();
|
||||
}
|
||||
|
||||
void HostInterface::UpdateSoftwareCursor()
|
||||
{
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
m_display->ClearSoftwareCursor();
|
||||
return;
|
||||
|
@ -659,7 +661,7 @@ void HostInterface::UpdateSoftwareCursor()
|
|||
|
||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||
{
|
||||
Controller* controller = m_system->GetController(i);
|
||||
Controller* controller = System::GetController(i);
|
||||
if (controller && controller->GetSoftwareCursor(&image, &image_scale))
|
||||
break;
|
||||
}
|
||||
|
@ -677,8 +679,10 @@ void HostInterface::UpdateSoftwareCursor()
|
|||
|
||||
void HostInterface::RecreateSystem()
|
||||
{
|
||||
Assert(!System::IsShutdown());
|
||||
|
||||
std::unique_ptr<ByteStream> stream = ByteStream_CreateGrowableMemoryStream(nullptr, 8 * 1024);
|
||||
if (!m_system->SaveState(stream.get()) || !stream->SeekAbsolute(0))
|
||||
if (!System::SaveState(stream.get()) || !stream->SeekAbsolute(0))
|
||||
{
|
||||
ReportError("Failed to save state before system recreation. Shutting down.");
|
||||
DestroySystem();
|
||||
|
@ -695,7 +699,7 @@ void HostInterface::RecreateSystem()
|
|||
return;
|
||||
}
|
||||
|
||||
m_system->ResetPerformanceCounters();
|
||||
System::ResetPerformanceCounters();
|
||||
}
|
||||
|
||||
void HostInterface::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/,
|
||||
|
|
|
@ -20,13 +20,10 @@ class CDImage;
|
|||
class HostDisplay;
|
||||
class GameList;
|
||||
|
||||
class System;
|
||||
struct SystemBootParameters;
|
||||
|
||||
class HostInterface
|
||||
{
|
||||
friend System;
|
||||
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
|
@ -44,12 +41,6 @@ public:
|
|||
/// Access to host audio stream.
|
||||
ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); }
|
||||
|
||||
/// Returns a settings object which can be modified.
|
||||
ALWAYS_INLINE Settings& GetSettings() { return m_settings; }
|
||||
|
||||
/// Access to emulated system.
|
||||
ALWAYS_INLINE System* GetSystem() const { return m_system.get(); }
|
||||
|
||||
/// Initializes the emulator frontend.
|
||||
virtual bool Initialize();
|
||||
|
||||
|
@ -118,6 +109,12 @@ public:
|
|||
/// Returns a float setting from the configuration.
|
||||
virtual float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
|
||||
|
||||
/// Loads the BIOS image for the specified region.
|
||||
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
|
||||
|
||||
virtual void OnRunningGameChanged();
|
||||
virtual void OnSystemPerformanceCountersUpdated();
|
||||
|
||||
protected:
|
||||
virtual bool AcquireHostDisplay() = 0;
|
||||
virtual void ReleaseHostDisplay() = 0;
|
||||
|
@ -125,9 +122,7 @@ protected:
|
|||
|
||||
virtual void OnSystemCreated();
|
||||
virtual void OnSystemDestroyed();
|
||||
virtual void OnSystemPerformanceCountersUpdated();
|
||||
virtual void OnSystemStateSaved(bool global, s32 slot);
|
||||
virtual void OnRunningGameChanged();
|
||||
virtual void OnControllerTypeChanged(u32 slot);
|
||||
|
||||
/// Restores all settings to defaults.
|
||||
|
@ -148,9 +143,6 @@ protected:
|
|||
/// Sets the user directory to the program directory, i.e. "portable mode".
|
||||
void SetUserDirectoryToProgramDirectory();
|
||||
|
||||
/// Loads the BIOS image for the specified region.
|
||||
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
|
||||
|
||||
/// Quick switch between software and hardware rendering.
|
||||
void ToggleSoftwareRendering();
|
||||
|
||||
|
@ -165,8 +157,8 @@ protected:
|
|||
|
||||
std::unique_ptr<HostDisplay> m_display;
|
||||
std::unique_ptr<AudioStream> m_audio_stream;
|
||||
std::unique_ptr<System> m_system;
|
||||
Settings m_settings;
|
||||
std::string m_program_directory;
|
||||
std::string m_user_directory;
|
||||
};
|
||||
|
||||
extern HostInterface* g_host_interface;
|
||||
|
|
|
@ -2,15 +2,9 @@
|
|||
#include "common/log.h"
|
||||
Log_SetChannel(HostInterfaceProgressCallback);
|
||||
|
||||
HostInterfaceProgressCallback::HostInterfaceProgressCallback(HostInterface* intf)
|
||||
: BaseProgressCallback(), m_host_interface(intf)
|
||||
{
|
||||
}
|
||||
HostInterfaceProgressCallback::HostInterfaceProgressCallback() : BaseProgressCallback() {}
|
||||
|
||||
void HostInterfaceProgressCallback::PushState()
|
||||
{
|
||||
BaseProgressCallback::PushState();
|
||||
}
|
||||
void HostInterfaceProgressCallback::PushState() { BaseProgressCallback::PushState(); }
|
||||
|
||||
void HostInterfaceProgressCallback::PopState()
|
||||
{
|
||||
|
@ -58,38 +52,23 @@ void HostInterfaceProgressCallback::Redraw(bool force)
|
|||
return;
|
||||
|
||||
m_last_progress_percent = percent;
|
||||
m_host_interface->DisplayLoadingScreen(m_status_text, 0, static_cast<int>(m_progress_range),
|
||||
g_host_interface->DisplayLoadingScreen(m_status_text, 0, static_cast<int>(m_progress_range),
|
||||
static_cast<int>(m_progress_value));
|
||||
}
|
||||
|
||||
void HostInterfaceProgressCallback::DisplayError(const char* message)
|
||||
{
|
||||
Log_ErrorPrint(message);
|
||||
}
|
||||
void HostInterfaceProgressCallback::DisplayError(const char* message) { Log_ErrorPrint(message); }
|
||||
|
||||
void HostInterfaceProgressCallback::DisplayWarning(const char* message)
|
||||
{
|
||||
Log_WarningPrint(message);
|
||||
}
|
||||
void HostInterfaceProgressCallback::DisplayWarning(const char* message) { Log_WarningPrint(message); }
|
||||
|
||||
void HostInterfaceProgressCallback::DisplayInformation(const char* message)
|
||||
{
|
||||
Log_InfoPrint(message);
|
||||
}
|
||||
void HostInterfaceProgressCallback::DisplayInformation(const char* message) { Log_InfoPrint(message); }
|
||||
|
||||
void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message)
|
||||
{
|
||||
Log_DevPrint(message);
|
||||
}
|
||||
void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message) { Log_DevPrint(message); }
|
||||
|
||||
void HostInterfaceProgressCallback::ModalError(const char* message)
|
||||
{
|
||||
m_host_interface->ReportError(message);
|
||||
}
|
||||
void HostInterfaceProgressCallback::ModalError(const char* message) { g_host_interface->ReportError(message); }
|
||||
|
||||
bool HostInterfaceProgressCallback::ModalConfirmation(const char* message)
|
||||
{
|
||||
return m_host_interface->ConfirmMessage(message);
|
||||
return g_host_interface->ConfirmMessage(message);
|
||||
}
|
||||
|
||||
u32 HostInterfaceProgressCallback::ModalPrompt(const char* message, u32 num_options, ...)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
class HostInterfaceProgressCallback : public BaseProgressCallback
|
||||
{
|
||||
public:
|
||||
HostInterfaceProgressCallback(HostInterface* intf);
|
||||
HostInterfaceProgressCallback();
|
||||
|
||||
void PushState() override;
|
||||
void PopState() override;
|
||||
|
@ -27,6 +27,5 @@ public:
|
|||
private:
|
||||
void Redraw(bool force);
|
||||
|
||||
HostInterface* m_host_interface;
|
||||
int m_last_progress_percent = -1;
|
||||
};
|
||||
|
|
|
@ -4,15 +4,19 @@
|
|||
#include "cpu_core.h"
|
||||
Log_SetChannel(InterruptController);
|
||||
|
||||
InterruptController g_interrupt_controller;
|
||||
|
||||
InterruptController::InterruptController() = default;
|
||||
|
||||
InterruptController::~InterruptController() = default;
|
||||
|
||||
void InterruptController::Initialize(CPU::Core* cpu)
|
||||
void InterruptController::Initialize()
|
||||
{
|
||||
m_cpu = cpu;
|
||||
Reset();
|
||||
}
|
||||
|
||||
void InterruptController::Shutdown() {}
|
||||
|
||||
void InterruptController::Reset()
|
||||
{
|
||||
m_interrupt_status_register = 0;
|
||||
|
@ -82,7 +86,7 @@ void InterruptController::UpdateCPUInterruptRequest()
|
|||
{
|
||||
// external interrupts set bit 10 only?
|
||||
if ((m_interrupt_status_register & m_interrupt_mask_register) != 0)
|
||||
m_cpu->SetExternalInterrupt(2);
|
||||
CPU::SetExternalInterrupt(2);
|
||||
else
|
||||
m_cpu->ClearExternalInterrupt(2);
|
||||
CPU::ClearExternalInterrupt(2);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,7 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
namespace CPU
|
||||
{
|
||||
class Core;
|
||||
}
|
||||
|
||||
class InterruptController
|
||||
class InterruptController final
|
||||
{
|
||||
public:
|
||||
static constexpr u32 NUM_IRQS = 11;
|
||||
|
@ -32,12 +27,13 @@ public:
|
|||
InterruptController();
|
||||
~InterruptController();
|
||||
|
||||
void Initialize(CPU::Core* cpu);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
// Should mirror CPU state.
|
||||
bool GetIRQLineState() const { return (m_interrupt_status_register != 0); }
|
||||
ALWAYS_INLINE bool GetIRQLineState() const { return (m_interrupt_status_register != 0); }
|
||||
|
||||
// Interupts are edge-triggered, so if it is masked when TriggerInterrupt() is called, it will be lost.
|
||||
void InterruptRequest(IRQ irq);
|
||||
|
@ -52,9 +48,8 @@ private:
|
|||
|
||||
void UpdateCPUInterruptRequest();
|
||||
|
||||
CPU::Core* m_cpu;
|
||||
|
||||
u32 m_interrupt_status_register = 0;
|
||||
u32 m_interrupt_mask_register = DEFAULT_INTERRUPT_MASK;
|
||||
};
|
||||
|
||||
extern InterruptController g_interrupt_controller;
|
|
@ -1,26 +1,35 @@
|
|||
#include "mdec.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "cpu_core.h"
|
||||
#include "dma.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "system.h"
|
||||
#include <imgui.h>
|
||||
Log_SetChannel(MDEC);
|
||||
|
||||
MDEC g_mdec;
|
||||
|
||||
MDEC::MDEC() = default;
|
||||
|
||||
MDEC::~MDEC() = default;
|
||||
|
||||
void MDEC::Initialize(System* system, DMA* dma)
|
||||
void MDEC::Initialize()
|
||||
{
|
||||
m_system = system;
|
||||
m_dma = dma;
|
||||
m_block_copy_out_event = system->CreateTimingEvent("MDEC Block Copy Out", TICKS_PER_BLOCK, TICKS_PER_BLOCK,
|
||||
std::bind(&MDEC::CopyOutBlock, this), false);
|
||||
m_block_copy_out_event = TimingEvents::CreateTimingEvent("MDEC Block Copy Out", TICKS_PER_BLOCK, TICKS_PER_BLOCK,
|
||||
std::bind(&MDEC::CopyOutBlock, this), false);
|
||||
m_total_blocks_decoded = 0;
|
||||
Reset();
|
||||
}
|
||||
|
||||
void MDEC::Shutdown()
|
||||
{
|
||||
m_block_copy_out_event.reset();
|
||||
}
|
||||
|
||||
void MDEC::Reset()
|
||||
{
|
||||
m_block_copy_out_event->Deactivate();
|
||||
SoftReset();
|
||||
}
|
||||
|
||||
|
@ -176,12 +185,12 @@ void MDEC::UpdateStatus()
|
|||
// we always want data in if it's enabled
|
||||
const bool data_in_request = m_enable_dma_in && m_data_in_fifo.GetSpace() >= (32 * 2);
|
||||
m_status.data_in_request = data_in_request;
|
||||
m_dma->SetRequest(DMA::Channel::MDECin, data_in_request);
|
||||
g_dma.SetRequest(DMA::Channel::MDECin, data_in_request);
|
||||
|
||||
// we only want to send data out if we have some in the fifo
|
||||
const bool data_out_request = m_enable_dma_out && !m_data_out_fifo.IsEmpty();
|
||||
m_status.data_out_request = data_out_request;
|
||||
m_dma->SetRequest(DMA::Channel::MDECout, data_out_request);
|
||||
g_dma.SetRequest(DMA::Channel::MDECout, data_out_request);
|
||||
}
|
||||
|
||||
u32 MDEC::ReadDataRegister()
|
||||
|
@ -192,7 +201,7 @@ u32 MDEC::ReadDataRegister()
|
|||
if (HasPendingBlockCopyOut())
|
||||
{
|
||||
Log_DevPrint("MDEC data out FIFO empty on read - stalling CPU");
|
||||
m_system->StallCPU(m_block_copy_out_event->GetTicksUntilNextExecution());
|
||||
CPU::AddPendingTicks(m_block_copy_out_event->GetTicksUntilNextExecution());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -695,7 +704,7 @@ void MDEC::DrawDebugStateWindow()
|
|||
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(300.0f * framebuffer_scale, 350.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("MDEC State", &m_system->GetSettings().debugging.show_mdec_state))
|
||||
if (!ImGui::Begin("MDEC State", &g_settings.debugging.show_mdec_state))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class DMA;
|
||||
|
||||
class MDEC
|
||||
{
|
||||
|
@ -17,7 +15,8 @@ public:
|
|||
MDEC();
|
||||
~MDEC();
|
||||
|
||||
void Initialize(System* system, DMA* dma);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -122,9 +121,6 @@ private:
|
|||
const std::array<s16, 64>& Yblk);
|
||||
void y_to_mono(const std::array<s16, 64>& Yblk);
|
||||
|
||||
System* m_system = nullptr;
|
||||
DMA* m_dma = nullptr;
|
||||
|
||||
StatusRegister m_status = {};
|
||||
bool m_enable_dma_in = false;
|
||||
bool m_enable_dma_out = false;
|
||||
|
@ -151,3 +147,5 @@ private:
|
|||
|
||||
u32 m_total_blocks_decoded = 0;
|
||||
};
|
||||
|
||||
extern MDEC g_mdec;
|
|
@ -1,21 +1,21 @@
|
|||
#include "memory_card.h"
|
||||
#include "common/byte_stream.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/string_util.h"
|
||||
#include "host_interface.h"
|
||||
#include "system.h"
|
||||
#include <cstdio>
|
||||
Log_SetChannel(MemoryCard);
|
||||
|
||||
MemoryCard::MemoryCard(System* system) : m_system(system)
|
||||
MemoryCard::MemoryCard()
|
||||
{
|
||||
m_FLAG.no_write_yet = true;
|
||||
|
||||
m_save_event =
|
||||
system->CreateTimingEvent("Memory Card Host Flush", SAVE_DELAY_IN_SYSCLK_TICKS, SAVE_DELAY_IN_SYSCLK_TICKS,
|
||||
std::bind(&MemoryCard::SaveIfChanged, this, true), false);
|
||||
TimingEvents::CreateTimingEvent("Memory Card Host Flush", SAVE_DELAY_IN_SYSCLK_TICKS, SAVE_DELAY_IN_SYSCLK_TICKS,
|
||||
std::bind(&MemoryCard::SaveIfChanged, this, true), false);
|
||||
}
|
||||
|
||||
MemoryCard::~MemoryCard()
|
||||
|
@ -237,16 +237,16 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
|
|||
return ack;
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryCard> MemoryCard::Create(System* system)
|
||||
std::unique_ptr<MemoryCard> MemoryCard::Create()
|
||||
{
|
||||
std::unique_ptr<MemoryCard> mc = std::make_unique<MemoryCard>(system);
|
||||
std::unique_ptr<MemoryCard> mc = std::make_unique<MemoryCard>();
|
||||
mc->Format();
|
||||
return mc;
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryCard> MemoryCard::Open(System* system, std::string_view filename)
|
||||
std::unique_ptr<MemoryCard> MemoryCard::Open(std::string_view filename)
|
||||
{
|
||||
std::unique_ptr<MemoryCard> mc = std::make_unique<MemoryCard>(system);
|
||||
std::unique_ptr<MemoryCard> mc = std::make_unique<MemoryCard>();
|
||||
mc->m_filename = filename;
|
||||
if (!mc->LoadFromFile())
|
||||
{
|
||||
|
@ -255,7 +255,7 @@ std::unique_ptr<MemoryCard> MemoryCard::Open(System* system, std::string_view fi
|
|||
message.AppendString(filename.data(), static_cast<u32>(filename.length()));
|
||||
message.AppendString("' could not be read, formatting.");
|
||||
Log_ErrorPrint(message);
|
||||
system->GetHostInterface()->AddOSDMessage(message.GetCharArray(), 5.0f);
|
||||
g_host_interface->AddOSDMessage(message.GetCharArray(), 5.0f);
|
||||
mc->Format();
|
||||
}
|
||||
|
||||
|
@ -385,8 +385,7 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
|
|||
Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str());
|
||||
if (display_osd_message)
|
||||
{
|
||||
m_system->GetHostInterface()->AddOSDMessage(
|
||||
StringUtil::StdStringFromFormat("Saved memory card to '%s'", m_filename.c_str()));
|
||||
g_host_interface->AddOSDMessage(StringUtil::StdStringFromFormat("Saved memory card to '%s'", m_filename.c_str()));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
|
||||
class MemoryCard final
|
||||
|
@ -19,11 +18,11 @@ public:
|
|||
NUM_SECTORS = DATA_SIZE / SECTOR_SIZE
|
||||
};
|
||||
|
||||
MemoryCard(System* system);
|
||||
MemoryCard();
|
||||
~MemoryCard();
|
||||
|
||||
static std::unique_ptr<MemoryCard> Create(System* system);
|
||||
static std::unique_ptr<MemoryCard> Open(System* system, std::string_view filename);
|
||||
static std::unique_ptr<MemoryCard> Create();
|
||||
static std::unique_ptr<MemoryCard> Open(std::string_view filename);
|
||||
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
@ -85,7 +84,6 @@ private:
|
|||
bool SaveIfChanged(bool display_osd_message);
|
||||
void QueueFileSave();
|
||||
|
||||
System* m_system;
|
||||
std::unique_ptr<TimingEvent> m_save_event;
|
||||
|
||||
State m_state = State::Idle;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <array>
|
||||
Log_SetChannel(NamcoGunCon);
|
||||
|
||||
NamcoGunCon::NamcoGunCon(System* system) : m_system(system) {}
|
||||
NamcoGunCon::NamcoGunCon() = default;
|
||||
|
||||
NamcoGunCon::~NamcoGunCon() = default;
|
||||
|
||||
|
@ -153,14 +153,13 @@ bool NamcoGunCon::Transfer(const u8 data_in, u8* data_out)
|
|||
void NamcoGunCon::UpdatePosition()
|
||||
{
|
||||
// get screen coordinates
|
||||
const HostDisplay* display = m_system->GetHostInterface()->GetDisplay();
|
||||
const HostDisplay* display = g_host_interface->GetDisplay();
|
||||
const s32 mouse_x = display->GetMousePositionX();
|
||||
const s32 mouse_y = display->GetMousePositionY();
|
||||
|
||||
// are we within the active display area?
|
||||
u32 tick, line;
|
||||
if (mouse_x < 0 || mouse_y < 0 ||
|
||||
!m_system->GetGPU()->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, &tick, &line))
|
||||
if (mouse_x < 0 || mouse_y < 0 || !g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, &tick, &line))
|
||||
{
|
||||
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
|
||||
m_position_x = 0x01;
|
||||
|
@ -169,16 +168,16 @@ void NamcoGunCon::UpdatePosition()
|
|||
}
|
||||
|
||||
// 8MHz units for X = 44100*768*11/7 = 53222400 / 8000000 = 6.6528
|
||||
const double divider = static_cast<double>(m_system->GetGPU()->GetCRTCFrequency()) / 8000000.0;
|
||||
const double divider = static_cast<double>(g_gpu->GetCRTCFrequency()) / 8000000.0;
|
||||
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
|
||||
m_position_y = static_cast<u16>(line);
|
||||
Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
|
||||
m_position_x);
|
||||
}
|
||||
|
||||
std::unique_ptr<NamcoGunCon> NamcoGunCon::Create(System* system)
|
||||
std::unique_ptr<NamcoGunCon> NamcoGunCon::Create()
|
||||
{
|
||||
return std::make_unique<NamcoGunCon>(system);
|
||||
return std::make_unique<NamcoGunCon>();
|
||||
}
|
||||
|
||||
std::optional<s32> NamcoGunCon::StaticGetAxisCodeByName(std::string_view button_name)
|
||||
|
@ -234,11 +233,11 @@ Controller::SettingList NamcoGunCon::StaticGetSettings()
|
|||
return SettingList(settings.begin(), settings.end());
|
||||
}
|
||||
|
||||
void NamcoGunCon::LoadSettings(HostInterface* host_interface, const char* section)
|
||||
void NamcoGunCon::LoadSettings(const char* section)
|
||||
{
|
||||
Controller::LoadSettings(host_interface, section);
|
||||
Controller::LoadSettings(section);
|
||||
|
||||
std::string path = host_interface->GetStringSettingValue(section, "CrosshairImagePath");
|
||||
std::string path = g_host_interface->GetStringSettingValue(section, "CrosshairImagePath");
|
||||
if (path != m_crosshair_image_path)
|
||||
{
|
||||
m_crosshair_image_path = std::move(path);
|
||||
|
@ -255,7 +254,7 @@ void NamcoGunCon::LoadSettings(HostInterface* host_interface, const char* sectio
|
|||
Resources::CROSSHAIR_IMAGE_DATA.data());
|
||||
}
|
||||
|
||||
m_crosshair_image_scale = host_interface->GetFloatSettingValue(section, "CrosshairScale", 1.0f);
|
||||
m_crosshair_image_scale = g_host_interface->GetFloatSettingValue(section, "CrosshairScale", 1.0f);
|
||||
}
|
||||
|
||||
bool NamcoGunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale)
|
||||
|
|
|
@ -15,10 +15,10 @@ public:
|
|||
Count
|
||||
};
|
||||
|
||||
NamcoGunCon(System* system);
|
||||
NamcoGunCon();
|
||||
~NamcoGunCon() override;
|
||||
|
||||
static std::unique_ptr<NamcoGunCon> Create(System* system);
|
||||
static std::unique_ptr<NamcoGunCon> Create();
|
||||
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
|
||||
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
|
||||
static AxisList StaticGetAxisNames();
|
||||
|
@ -32,7 +32,7 @@ public:
|
|||
|
||||
void Reset() override;
|
||||
bool DoState(StateWrapper& sw) override;
|
||||
void LoadSettings(HostInterface* host_interface, const char* section) override;
|
||||
void LoadSettings(const char* section) override;
|
||||
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale) override;
|
||||
|
||||
void SetAxisState(s32 axis_code, float value) override;
|
||||
|
@ -58,7 +58,6 @@ private:
|
|||
YMSB
|
||||
};
|
||||
|
||||
System* m_system;
|
||||
Common::RGBA8Image m_crosshair_image;
|
||||
std::string m_crosshair_image_path;
|
||||
float m_crosshair_image_scale = 1.0f;
|
||||
|
|
|
@ -8,16 +8,28 @@
|
|||
#include "system.h"
|
||||
Log_SetChannel(Pad);
|
||||
|
||||
Pad g_pad;
|
||||
|
||||
Pad::Pad() = default;
|
||||
|
||||
Pad::~Pad() = default;
|
||||
|
||||
void Pad::Initialize(System* system, InterruptController* interrupt_controller)
|
||||
void Pad::Initialize()
|
||||
{
|
||||
m_system = system;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_transfer_event = system->CreateTimingEvent("Pad Serial Transfer", 1, 1,
|
||||
std::bind(&Pad::TransferEvent, this, std::placeholders::_2), false);
|
||||
m_transfer_event = TimingEvents::CreateTimingEvent(
|
||||
"Pad Serial Transfer", 1, 1, std::bind(&Pad::TransferEvent, this, std::placeholders::_2), false);
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Pad::Shutdown()
|
||||
{
|
||||
m_transfer_event.reset();
|
||||
|
||||
for (u32 i = 0; i < NUM_SLOTS; i++)
|
||||
{
|
||||
m_controllers[i].reset();
|
||||
m_memory_cards[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Pad::Reset()
|
||||
|
@ -44,27 +56,26 @@ bool Pad::DoState(StateWrapper& sw)
|
|||
|
||||
if (controller_type != state_controller_type)
|
||||
{
|
||||
if (m_system->GetSettings().load_devices_from_save_states)
|
||||
if (g_settings.load_devices_from_save_states)
|
||||
{
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
2.0f, "Save state contains controller type %s in port %u, but %s is used. Switching.",
|
||||
Settings::GetControllerTypeName(state_controller_type), i + 1u,
|
||||
Settings::GetControllerTypeName(controller_type));
|
||||
|
||||
m_controllers[i].reset();
|
||||
if (state_controller_type != ControllerType::None)
|
||||
m_controllers[i] = Controller::Create(m_system, state_controller_type, i);
|
||||
m_controllers[i] = Controller::Create(state_controller_type, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(2.0f, "Ignoring mismatched controller type %s in port %u.",
|
||||
Settings::GetControllerTypeName(state_controller_type),
|
||||
i + 1u);
|
||||
g_host_interface->AddFormattedOSDMessage(2.0f, "Ignoring mismatched controller type %s in port %u.",
|
||||
Settings::GetControllerTypeName(state_controller_type), i + 1u);
|
||||
|
||||
// we still need to read the save state controller state
|
||||
if (state_controller_type != ControllerType::None)
|
||||
{
|
||||
std::unique_ptr<Controller> dummy_controller = Controller::Create(m_system, state_controller_type, i);
|
||||
std::unique_ptr<Controller> dummy_controller = Controller::Create(state_controller_type, i);
|
||||
if (dummy_controller)
|
||||
{
|
||||
if (!sw.DoMarker("Controller") || !dummy_controller->DoState(sw))
|
||||
|
@ -85,11 +96,11 @@ bool Pad::DoState(StateWrapper& sw)
|
|||
bool card_present = static_cast<bool>(m_memory_cards[i]);
|
||||
sw.Do(&card_present);
|
||||
|
||||
if (sw.IsReading() && card_present && !m_system->GetSettings().load_devices_from_save_states)
|
||||
if (sw.IsReading() && card_present && !g_settings.load_devices_from_save_states)
|
||||
{
|
||||
Log_WarningPrintf("Skipping loading memory card %u from save state.", i + 1u);
|
||||
|
||||
MemoryCard dummy_card(m_system);
|
||||
MemoryCard dummy_card;
|
||||
if (!sw.DoMarker("MemoryCard") || !dummy_card.DoState(sw))
|
||||
return false;
|
||||
|
||||
|
@ -102,13 +113,13 @@ bool Pad::DoState(StateWrapper& sw)
|
|||
|
||||
if (card_present && !m_memory_cards[i])
|
||||
{
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
2.0f, "Memory card %u present in save state but not in system. Creating temporary card.", i + 1u);
|
||||
m_memory_cards[i] = MemoryCard::Create(m_system);
|
||||
m_memory_cards[i] = MemoryCard::Create();
|
||||
}
|
||||
else if (!card_present && m_memory_cards[i])
|
||||
{
|
||||
m_system->GetHostInterface()->AddFormattedOSDMessage(
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
2.0f, "Memory card %u present in system but not in save state. Removing card.", i + 1u);
|
||||
m_memory_cards[i].reset();
|
||||
}
|
||||
|
@ -411,7 +422,7 @@ void Pad::DoACK()
|
|||
{
|
||||
Log_DebugPrintf("Triggering ACK interrupt");
|
||||
m_JOY_STAT.INTR = true;
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::IRQ7);
|
||||
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::IRQ7);
|
||||
}
|
||||
|
||||
EndTransfer();
|
||||
|
|
|
@ -7,19 +7,18 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class InterruptController;
|
||||
class Controller;
|
||||
class MemoryCard;
|
||||
|
||||
class Pad
|
||||
class Pad final
|
||||
{
|
||||
public:
|
||||
Pad();
|
||||
~Pad();
|
||||
|
||||
void Initialize(System* system, InterruptController* interrupt_controller);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -108,9 +107,6 @@ private:
|
|||
void EndTransfer();
|
||||
void ResetDeviceTransferState();
|
||||
|
||||
System* m_system = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
|
||||
std::array<std::unique_ptr<Controller>, NUM_SLOTS> m_controllers;
|
||||
std::array<std::unique_ptr<MemoryCard>, NUM_SLOTS> m_memory_cards;
|
||||
|
||||
|
@ -129,3 +125,5 @@ private:
|
|||
bool m_receive_buffer_full = false;
|
||||
bool m_transmit_buffer_full = false;
|
||||
};
|
||||
|
||||
extern Pad g_pad;
|
|
@ -9,10 +9,10 @@
|
|||
#include <array>
|
||||
Log_SetChannel(PlayStationMouse);
|
||||
|
||||
PlayStationMouse::PlayStationMouse(System* system) : m_system(system)
|
||||
PlayStationMouse::PlayStationMouse()
|
||||
{
|
||||
m_last_host_position_x = system->GetHostInterface()->GetDisplay()->GetMousePositionX();
|
||||
m_last_host_position_y = system->GetHostInterface()->GetDisplay()->GetMousePositionY();
|
||||
m_last_host_position_x = g_host_interface->GetDisplay()->GetMousePositionX();
|
||||
m_last_host_position_y = g_host_interface->GetDisplay()->GetMousePositionY();
|
||||
}
|
||||
|
||||
PlayStationMouse::~PlayStationMouse() = default;
|
||||
|
@ -142,7 +142,7 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out)
|
|||
void PlayStationMouse::UpdatePosition()
|
||||
{
|
||||
// get screen coordinates
|
||||
const HostDisplay* display = m_system->GetHostInterface()->GetDisplay();
|
||||
const HostDisplay* display = g_host_interface->GetDisplay();
|
||||
const s32 mouse_x = display->GetMousePositionX();
|
||||
const s32 mouse_y = display->GetMousePositionY();
|
||||
const s32 delta_x = mouse_x - m_last_host_position_x;
|
||||
|
@ -157,9 +157,9 @@ void PlayStationMouse::UpdatePosition()
|
|||
m_delta_y = static_cast<s8>(std::clamp<s32>(delta_y, std::numeric_limits<s8>::min(), std::numeric_limits<s8>::max()));
|
||||
}
|
||||
|
||||
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create(System* system)
|
||||
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create()
|
||||
{
|
||||
return std::make_unique<PlayStationMouse>(system);
|
||||
return std::make_unique<PlayStationMouse>();
|
||||
}
|
||||
|
||||
std::optional<s32> PlayStationMouse::StaticGetAxisCodeByName(std::string_view button_name)
|
||||
|
|
|
@ -14,10 +14,10 @@ public:
|
|||
Count
|
||||
};
|
||||
|
||||
PlayStationMouse(System* system);
|
||||
PlayStationMouse();
|
||||
~PlayStationMouse() override;
|
||||
|
||||
static std::unique_ptr<PlayStationMouse> Create(System* system);
|
||||
static std::unique_ptr<PlayStationMouse> Create();
|
||||
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
|
||||
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
|
||||
static AxisList StaticGetAxisNames();
|
||||
|
@ -52,8 +52,6 @@ private:
|
|||
DeltaY
|
||||
};
|
||||
|
||||
System* m_system;
|
||||
|
||||
s32 m_last_host_position_x = 0;
|
||||
s32 m_last_host_position_y = 0;
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
Settings g_settings;
|
||||
|
||||
const char* SettingInfo::StringDefaultValue() const
|
||||
{
|
||||
return default_value ? default_value : "";
|
||||
|
|
|
@ -146,6 +146,9 @@ struct Settings
|
|||
bool log_to_window = false;
|
||||
bool log_to_file = false;
|
||||
|
||||
ALWAYS_INLINE bool IsUsingRecompiler() const { return (cpu_execution_mode == CPUExecutionMode::Recompiler); }
|
||||
ALWAYS_INLINE bool IsUsingSoftwareRenderer() const { return (gpu_renderer == GPURenderer::Software); }
|
||||
|
||||
bool HasAnyPerGameMemoryCards() const;
|
||||
|
||||
enum : u32
|
||||
|
@ -216,3 +219,5 @@ struct Settings
|
|||
static constexpr MemoryCardType DEFAULT_MEMORY_CARD_2_TYPE = MemoryCardType::None;
|
||||
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
|
||||
};
|
||||
|
||||
extern Settings g_settings;
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
#include "sio.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "controller.h"
|
||||
#include "host_interface.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "memory_card.h"
|
||||
#include "controller.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(SIO);
|
||||
|
||||
SIO g_sio;
|
||||
|
||||
SIO::SIO() = default;
|
||||
|
||||
SIO::~SIO() = default;
|
||||
|
||||
void SIO::Initialize(System* system, InterruptController* interrupt_controller)
|
||||
void SIO::Initialize()
|
||||
{
|
||||
m_system = system;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
Reset();
|
||||
}
|
||||
|
||||
void SIO::Shutdown() {}
|
||||
|
||||
void SIO::Reset()
|
||||
{
|
||||
SoftReset();
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
class System;
|
||||
class InterruptController;
|
||||
class Controller;
|
||||
class MemoryCard;
|
||||
|
||||
|
@ -18,7 +16,8 @@ public:
|
|||
SIO();
|
||||
~SIO();
|
||||
|
||||
void Initialize(System* system, InterruptController* interrupt_controller);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -73,11 +72,10 @@ private:
|
|||
|
||||
void SoftReset();
|
||||
|
||||
System* m_system = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
|
||||
SIO_CTRL m_SIO_CTRL = {};
|
||||
SIO_STAT m_SIO_STAT = {};
|
||||
SIO_MODE m_SIO_MODE = {};
|
||||
u16 m_SIO_BAUD = 0;
|
||||
};
|
||||
|
||||
extern SIO g_sio;
|
|
@ -11,21 +11,28 @@
|
|||
#include <imgui.h>
|
||||
Log_SetChannel(SPU);
|
||||
|
||||
SPU g_spu;
|
||||
|
||||
SPU::SPU() = default;
|
||||
|
||||
SPU::~SPU() = default;
|
||||
|
||||
void SPU::Initialize(System* system, DMA* dma, CDROM* cdrom, InterruptController* interrupt_controller)
|
||||
void SPU::Initialize()
|
||||
{
|
||||
m_system = system;
|
||||
m_dma = dma;
|
||||
m_cdrom = cdrom;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_tick_event = m_system->CreateTimingEvent("SPU Sample", SYSCLK_TICKS_PER_SPU_TICK, SYSCLK_TICKS_PER_SPU_TICK,
|
||||
std::bind(&SPU::Execute, this, std::placeholders::_1), false);
|
||||
m_tick_event = TimingEvents::CreateTimingEvent("SPU Sample", SYSCLK_TICKS_PER_SPU_TICK, SYSCLK_TICKS_PER_SPU_TICK,
|
||||
std::bind(&SPU::Execute, this, std::placeholders::_1), false);
|
||||
m_transfer_event =
|
||||
m_system->CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
|
||||
std::bind(&SPU::ExecuteTransfer, this, std::placeholders::_1), false);
|
||||
TimingEvents::CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
|
||||
std::bind(&SPU::ExecuteTransfer, this, std::placeholders::_1), false);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void SPU::Shutdown()
|
||||
{
|
||||
m_tick_event.reset();
|
||||
m_transfer_event.reset();
|
||||
m_dump_writer.reset();
|
||||
}
|
||||
|
||||
void SPU::Reset()
|
||||
|
@ -81,6 +88,7 @@ void SPU::Reset()
|
|||
}
|
||||
|
||||
m_transfer_fifo.Clear();
|
||||
m_transfer_event->Deactivate();
|
||||
m_ram.fill(0);
|
||||
UpdateEventInterval();
|
||||
}
|
||||
|
@ -147,7 +155,7 @@ bool SPU::DoState(StateWrapper& sw)
|
|||
|
||||
if (sw.IsReading())
|
||||
{
|
||||
m_system->GetHostInterface()->GetAudioStream()->EmptyBuffers();
|
||||
g_host_interface->GetAudioStream()->EmptyBuffers();
|
||||
UpdateEventInterval();
|
||||
UpdateTransferEvent();
|
||||
}
|
||||
|
@ -649,7 +657,7 @@ void SPU::CheckRAMIRQ(u32 address)
|
|||
{
|
||||
Log_DebugPrintf("SPU IRQ at address 0x%08X", address);
|
||||
m_SPUSTAT.irq9_flag = true;
|
||||
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::SPU);
|
||||
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::SPU);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -675,7 +683,7 @@ void SPU::Execute(TickCount ticks)
|
|||
|
||||
while (remaining_frames > 0)
|
||||
{
|
||||
AudioStream* const output_stream = m_system->GetHostInterface()->GetAudioStream();
|
||||
AudioStream* const output_stream = g_host_interface->GetAudioStream();
|
||||
s16* output_frame_start;
|
||||
u32 output_frame_space = remaining_frames;
|
||||
output_stream->BeginWrite(&output_frame_start, &output_frame_space);
|
||||
|
@ -730,7 +738,7 @@ void SPU::Execute(TickCount ticks)
|
|||
UpdateNoise();
|
||||
|
||||
// Mix in CD audio.
|
||||
const auto [cd_audio_left, cd_audio_right] = m_cdrom->GetAudioFrame();
|
||||
const auto [cd_audio_left, cd_audio_right] = g_cdrom.GetAudioFrame();
|
||||
if (m_SPUCNT.cd_audio_enable)
|
||||
{
|
||||
const s32 cd_audio_volume_left = ApplyVolume(s32(cd_audio_left), m_cd_audio_volume_left);
|
||||
|
@ -782,7 +790,7 @@ void SPU::UpdateEventInterval()
|
|||
// Don't generate more than the audio buffer since in a single slice, otherwise we'll both overflow the buffers when
|
||||
// we do write it, and the audio thread will underflow since it won't have enough data it the game isn't messing with
|
||||
// the SPU state.
|
||||
const u32 max_slice_frames = m_system->GetHostInterface()->GetAudioStream()->GetBufferSize();
|
||||
const u32 max_slice_frames = g_host_interface->GetAudioStream()->GetBufferSize();
|
||||
|
||||
// TODO: Make this predict how long until the interrupt will be hit instead...
|
||||
const u32 interval = (m_SPUCNT.enable && m_SPUCNT.irq9_enable) ? 1 : max_slice_frames;
|
||||
|
@ -930,7 +938,7 @@ void SPU::UpdateDMARequest()
|
|||
}
|
||||
|
||||
// This might call us back directly.
|
||||
m_dma->SetRequest(DMA::Channel::SPU, m_SPUSTAT.dma_request);
|
||||
g_dma.SetRequest(DMA::Channel::SPU, m_SPUSTAT.dma_request);
|
||||
}
|
||||
|
||||
void SPU::DMARead(u32* words, u32 word_count)
|
||||
|
@ -1744,7 +1752,7 @@ void SPU::DrawDebugStateWindow()
|
|||
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 800.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("SPU State", &m_system->GetSettings().debugging.show_spu_state))
|
||||
if (!ImGui::Begin("SPU State", &g_settings.debugging.show_spu_state))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
|
|
|
@ -11,11 +11,7 @@ namespace Common {
|
|||
class WAVWriter;
|
||||
}
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class DMA;
|
||||
class CDROM;
|
||||
class InterruptController;
|
||||
|
||||
class SPU
|
||||
{
|
||||
|
@ -23,7 +19,8 @@ public:
|
|||
SPU();
|
||||
~SPU();
|
||||
|
||||
void Initialize(System* system, DMA* dma, CDROM* cdrom, InterruptController* interrupt_controller);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -369,10 +366,6 @@ private:
|
|||
void UpdateTransferEvent();
|
||||
void UpdateDMARequest();
|
||||
|
||||
System* m_system = nullptr;
|
||||
DMA* m_dma = nullptr;
|
||||
CDROM* m_cdrom = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
std::unique_ptr<TimingEvent> m_tick_event;
|
||||
std::unique_ptr<TimingEvent> m_transfer_event;
|
||||
std::unique_ptr<Common::WAVWriter> m_dump_writer;
|
||||
|
@ -422,3 +415,5 @@ private:
|
|||
|
||||
std::array<u8, RAM_SIZE> m_ram{};
|
||||
};
|
||||
|
||||
extern SPU g_spu;
|
File diff suppressed because it is too large
Load Diff
|
@ -11,22 +11,7 @@ class ByteStream;
|
|||
class CDImage;
|
||||
class StateWrapper;
|
||||
|
||||
namespace CPU {
|
||||
class Core;
|
||||
class CodeCache;
|
||||
} // namespace CPU
|
||||
|
||||
class Bus;
|
||||
class DMA;
|
||||
class InterruptController;
|
||||
class GPU;
|
||||
class CDROM;
|
||||
class Pad;
|
||||
class Controller;
|
||||
class Timers;
|
||||
class SPU;
|
||||
class MDEC;
|
||||
class SIO;
|
||||
|
||||
struct SystemBootParameters
|
||||
{
|
||||
|
@ -44,227 +29,105 @@ struct SystemBootParameters
|
|||
bool force_software_renderer = false;
|
||||
};
|
||||
|
||||
class System
|
||||
namespace System {
|
||||
|
||||
enum : u32
|
||||
{
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
// 5 megabytes is sufficient for now, at the moment they're around 4.2MB.
|
||||
MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024
|
||||
};
|
||||
|
||||
friend TimingEvent;
|
||||
|
||||
~System();
|
||||
|
||||
/// Returns the preferred console type for a disc.
|
||||
static ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
|
||||
|
||||
/// Creates a new System.
|
||||
static std::unique_ptr<System> Create(HostInterface* host_interface);
|
||||
|
||||
// Accessing components.
|
||||
HostInterface* GetHostInterface() const { return m_host_interface; }
|
||||
CPU::Core* GetCPU() const { return m_cpu.get(); }
|
||||
Bus* GetBus() const { return m_bus.get(); }
|
||||
DMA* GetDMA() const { return m_dma.get(); }
|
||||
InterruptController* GetInterruptController() const { return m_interrupt_controller.get(); }
|
||||
GPU* GetGPU() const { return m_gpu.get(); }
|
||||
CDROM* GetCDROM() const { return m_cdrom.get(); }
|
||||
Pad* GetPad() const { return m_pad.get(); }
|
||||
Timers* GetTimers() const { return m_timers.get(); }
|
||||
SPU* GetSPU() const { return m_spu.get(); }
|
||||
MDEC* GetMDEC() const { return m_mdec.get(); }
|
||||
|
||||
ConsoleRegion GetRegion() const { return m_region; }
|
||||
bool IsPALRegion() const { return m_region == ConsoleRegion::PAL; }
|
||||
u32 GetFrameNumber() const { return m_frame_number; }
|
||||
u32 GetInternalFrameNumber() const { return m_internal_frame_number; }
|
||||
u32 GetGlobalTickCounter() const { return m_global_tick_counter; }
|
||||
void IncrementFrameNumber()
|
||||
{
|
||||
m_frame_number++;
|
||||
m_frame_done = true;
|
||||
}
|
||||
void IncrementInternalFrameNumber() { m_internal_frame_number++; }
|
||||
|
||||
const Settings& GetSettings() { return m_host_interface->GetSettings(); }
|
||||
|
||||
const std::string& GetRunningPath() const { return m_running_game_path; }
|
||||
const std::string& GetRunningCode() const { return m_running_game_code; }
|
||||
const std::string& GetRunningTitle() const { return m_running_game_title; }
|
||||
|
||||
float GetFPS() const { return m_fps; }
|
||||
float GetVPS() const { return m_vps; }
|
||||
float GetEmulationSpeed() const { return m_speed; }
|
||||
float GetAverageFrameTime() const { return m_average_frame_time; }
|
||||
float GetWorstFrameTime() const { return m_worst_frame_time; }
|
||||
float GetThrottleFrequency() const { return m_throttle_frequency; }
|
||||
|
||||
bool Boot(const SystemBootParameters& params);
|
||||
void Reset();
|
||||
|
||||
bool LoadState(ByteStream* state);
|
||||
bool SaveState(ByteStream* state, u32 screenshot_size = 128);
|
||||
|
||||
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
|
||||
bool RecreateGPU(GPURenderer renderer);
|
||||
|
||||
/// Updates GPU settings, without recreating the renderer.
|
||||
void UpdateGPUSettings();
|
||||
|
||||
/// Forcibly changes the CPU execution mode, ignoring settings.
|
||||
void SetCPUExecutionMode(CPUExecutionMode mode);
|
||||
|
||||
void RunFrame();
|
||||
|
||||
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
|
||||
void SetThrottleFrequency(float frequency);
|
||||
|
||||
/// Updates the throttle period, call when target emulation speed changes.
|
||||
void UpdateThrottlePeriod();
|
||||
|
||||
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
|
||||
void Throttle();
|
||||
|
||||
void UpdatePerformanceCounters();
|
||||
void ResetPerformanceCounters();
|
||||
|
||||
bool LoadEXE(const char* filename, std::vector<u8>& bios_image);
|
||||
bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size, std::vector<u8>& bios_image);
|
||||
bool LoadPSF(const char* filename, std::vector<u8>& bios_image);
|
||||
bool SetExpansionROM(const char* filename);
|
||||
|
||||
// Adds ticks to the global tick counter, simulating the CPU being stalled.
|
||||
void StallCPU(TickCount ticks);
|
||||
|
||||
// Access controllers for simulating input.
|
||||
Controller* GetController(u32 slot) const;
|
||||
void UpdateControllers();
|
||||
void UpdateControllerSettings();
|
||||
void ResetControllers();
|
||||
void UpdateMemoryCards();
|
||||
|
||||
bool HasMedia() const;
|
||||
bool InsertMedia(const char* path);
|
||||
void RemoveMedia();
|
||||
|
||||
/// Creates a new event.
|
||||
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
|
||||
TimingEventCallback callback, bool activate);
|
||||
|
||||
/// Returns the number of entries in the media/disc playlist.
|
||||
ALWAYS_INLINE u32 GetMediaPlaylistCount() const { return static_cast<u32>(m_media_playlist.size()); }
|
||||
|
||||
/// Returns the current image from the media/disc playlist.
|
||||
u32 GetMediaPlaylistIndex() const;
|
||||
|
||||
/// Returns the path to the specified playlist index.
|
||||
const std::string& GetMediaPlaylistPath(u32 index) const { return m_media_playlist[index]; }
|
||||
|
||||
/// Adds a new path to the media playlist.
|
||||
bool AddMediaPathToPlaylist(const std::string_view& path);
|
||||
|
||||
/// Removes a path from the media playlist.
|
||||
bool RemoveMediaPathFromPlaylist(const std::string_view& path);
|
||||
bool RemoveMediaPathFromPlaylist(u32 index);
|
||||
|
||||
/// Changes a path from the media playlist.
|
||||
bool ReplaceMediaPathFromPlaylist(u32 index, const std::string_view& path);
|
||||
|
||||
/// Switches to the specified media/disc playlist index.
|
||||
bool SwitchMediaFromPlaylist(u32 index);
|
||||
|
||||
private:
|
||||
System(HostInterface* host_interface);
|
||||
|
||||
/// Opens CD image, preloading if needed.
|
||||
std::unique_ptr<CDImage> OpenCDImage(const char* path, bool force_preload);
|
||||
|
||||
bool DoLoadState(ByteStream* stream, bool init_components, bool force_software_renderer);
|
||||
bool DoState(StateWrapper& sw);
|
||||
bool CreateGPU(GPURenderer renderer);
|
||||
|
||||
bool InitializeComponents(bool force_software_renderer);
|
||||
void DestroyComponents();
|
||||
|
||||
// Active event management
|
||||
void AddActiveEvent(TimingEvent* event);
|
||||
void RemoveActiveEvent(TimingEvent* event);
|
||||
void SortEvents();
|
||||
|
||||
// Runs any pending events. Call when CPU downcount is zero.
|
||||
void RunEvents();
|
||||
|
||||
// Updates the downcount of the CPU (event scheduling).
|
||||
void UpdateCPUDowncount();
|
||||
|
||||
bool DoEventsState(StateWrapper& sw);
|
||||
|
||||
// Event lookup, use with care.
|
||||
// If you modify an event, call SortEvents afterwards.
|
||||
TimingEvent* FindActiveEvent(const char* name);
|
||||
|
||||
// Event enumeration, use with care.
|
||||
// Don't remove an event while enumerating the list, as it will invalidate the iterator.
|
||||
template<typename T>
|
||||
void EnumerateActiveEvents(T callback) const
|
||||
{
|
||||
for (const TimingEvent* ev : m_events)
|
||||
callback(ev);
|
||||
}
|
||||
|
||||
void UpdateRunningGame(const char* path, CDImage* image);
|
||||
|
||||
HostInterface* m_host_interface;
|
||||
std::unique_ptr<CPU::Core> m_cpu;
|
||||
std::unique_ptr<CPU::CodeCache> m_cpu_code_cache;
|
||||
std::unique_ptr<Bus> m_bus;
|
||||
std::unique_ptr<DMA> m_dma;
|
||||
std::unique_ptr<InterruptController> m_interrupt_controller;
|
||||
std::unique_ptr<GPU> m_gpu;
|
||||
std::unique_ptr<CDROM> m_cdrom;
|
||||
std::unique_ptr<Pad> m_pad;
|
||||
std::unique_ptr<Timers> m_timers;
|
||||
std::unique_ptr<SPU> m_spu;
|
||||
std::unique_ptr<MDEC> m_mdec;
|
||||
std::unique_ptr<SIO> m_sio;
|
||||
ConsoleRegion m_region = ConsoleRegion::NTSC_U;
|
||||
CPUExecutionMode m_cpu_execution_mode = CPUExecutionMode::Interpreter;
|
||||
u32 m_frame_number = 1;
|
||||
u32 m_internal_frame_number = 1;
|
||||
u32 m_global_tick_counter = 0;
|
||||
|
||||
std::vector<TimingEvent*> m_events;
|
||||
u32 m_last_event_run_time = 0;
|
||||
bool m_running_events = false;
|
||||
bool m_events_need_sorting = false;
|
||||
bool m_frame_done = false;
|
||||
|
||||
std::string m_running_game_path;
|
||||
std::string m_running_game_code;
|
||||
std::string m_running_game_title;
|
||||
|
||||
float m_throttle_frequency = 60.0f;
|
||||
s32 m_throttle_period = 0;
|
||||
u64 m_last_throttle_time = 0;
|
||||
Common::Timer m_throttle_timer;
|
||||
Common::Timer m_speed_lost_time_timestamp;
|
||||
|
||||
float m_average_frame_time_accumulator = 0.0f;
|
||||
float m_worst_frame_time_accumulator = 0.0f;
|
||||
|
||||
float m_vps = 0.0f;
|
||||
float m_fps = 0.0f;
|
||||
float m_speed = 0.0f;
|
||||
float m_worst_frame_time = 0.0f;
|
||||
float m_average_frame_time = 0.0f;
|
||||
u32 m_last_frame_number = 0;
|
||||
u32 m_last_internal_frame_number = 0;
|
||||
u32 m_last_global_tick_counter = 0;
|
||||
Common::Timer m_fps_timer;
|
||||
Common::Timer m_frame_timer;
|
||||
|
||||
// Playlist of disc images.
|
||||
std::vector<std::string> m_media_playlist;
|
||||
// 5 megabytes is sufficient for now, at the moment they're around 4.2MB.
|
||||
MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024
|
||||
};
|
||||
|
||||
enum class State
|
||||
{
|
||||
Shutdown,
|
||||
Starting,
|
||||
Running,
|
||||
Paused
|
||||
};
|
||||
|
||||
/// Returns the preferred console type for a disc.
|
||||
ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
|
||||
|
||||
State GetState();
|
||||
void SetState(State new_state);
|
||||
bool IsRunning();
|
||||
bool IsPaused();
|
||||
bool IsShutdown();
|
||||
bool IsValid();
|
||||
|
||||
ConsoleRegion GetRegion();
|
||||
bool IsPALRegion();
|
||||
u32 GetFrameNumber();
|
||||
u32 GetInternalFrameNumber();
|
||||
void FrameDone();
|
||||
void IncrementInternalFrameNumber();
|
||||
|
||||
const std::string& GetRunningPath();
|
||||
const std::string& GetRunningCode();
|
||||
const std::string& GetRunningTitle();
|
||||
|
||||
float GetFPS();
|
||||
float GetVPS();
|
||||
float GetEmulationSpeed();
|
||||
float GetAverageFrameTime();
|
||||
float GetWorstFrameTime();
|
||||
float GetThrottleFrequency();
|
||||
|
||||
bool Boot(const SystemBootParameters& params);
|
||||
void Reset();
|
||||
void Shutdown();
|
||||
|
||||
bool LoadState(ByteStream* state);
|
||||
bool SaveState(ByteStream* state, u32 screenshot_size = 128);
|
||||
|
||||
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
|
||||
bool RecreateGPU(GPURenderer renderer);
|
||||
|
||||
void RunFrame();
|
||||
|
||||
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
|
||||
void SetThrottleFrequency(float frequency);
|
||||
|
||||
/// Updates the throttle period, call when target emulation speed changes.
|
||||
void UpdateThrottlePeriod();
|
||||
|
||||
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
|
||||
void Throttle();
|
||||
|
||||
void UpdatePerformanceCounters();
|
||||
void ResetPerformanceCounters();
|
||||
|
||||
// Access controllers for simulating input.
|
||||
Controller* GetController(u32 slot);
|
||||
void UpdateControllers();
|
||||
void UpdateControllerSettings();
|
||||
void ResetControllers();
|
||||
void UpdateMemoryCards();
|
||||
|
||||
bool HasMedia();
|
||||
bool InsertMedia(const char* path);
|
||||
void RemoveMedia();
|
||||
|
||||
/// Returns the number of entries in the media/disc playlist.
|
||||
u32 GetMediaPlaylistCount();
|
||||
|
||||
/// Returns the current image from the media/disc playlist.
|
||||
u32 GetMediaPlaylistIndex();
|
||||
|
||||
/// Returns the path to the specified playlist index.
|
||||
const std::string& GetMediaPlaylistPath(u32 index);
|
||||
|
||||
/// Adds a new path to the media playlist.
|
||||
bool AddMediaPathToPlaylist(const std::string_view& path);
|
||||
|
||||
/// Removes a path from the media playlist.
|
||||
bool RemoveMediaPathFromPlaylist(const std::string_view& path);
|
||||
bool RemoveMediaPathFromPlaylist(u32 index);
|
||||
|
||||
/// Changes a path from the media playlist.
|
||||
bool ReplaceMediaPathFromPlaylist(u32 index, const std::string_view& path);
|
||||
|
||||
/// Switches to the specified media/disc playlist index.
|
||||
bool SwitchMediaFromPlaylist(u32 index);
|
||||
|
||||
} // namespace System
|
||||
|
|
|
@ -7,17 +7,22 @@
|
|||
#include <imgui.h>
|
||||
Log_SetChannel(Timers);
|
||||
|
||||
Timers g_timers;
|
||||
|
||||
Timers::Timers() = default;
|
||||
|
||||
Timers::~Timers() = default;
|
||||
|
||||
void Timers::Initialize(System* system, InterruptController* interrupt_controller, GPU* gpu)
|
||||
void Timers::Initialize()
|
||||
{
|
||||
m_system = system;
|
||||
m_interrupt_controller = interrupt_controller;
|
||||
m_gpu = gpu;
|
||||
m_sysclk_event = system->CreateTimingEvent("Timer SysClk Interrupt", 1, 1,
|
||||
std::bind(&Timers::AddSysClkTicks, this, std::placeholders::_1), false);
|
||||
m_sysclk_event = TimingEvents::CreateTimingEvent(
|
||||
"Timer SysClk Interrupt", 1, 1, std::bind(&Timers::AddSysClkTicks, this, std::placeholders::_1), false);
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Timers::Shutdown()
|
||||
{
|
||||
m_sysclk_event.reset();
|
||||
}
|
||||
|
||||
void Timers::Reset()
|
||||
|
@ -182,8 +187,8 @@ u32 Timers::ReadRegister(u32 offset)
|
|||
if (timer_index < 2 && cs.external_counting_enabled)
|
||||
{
|
||||
// timers 0/1 depend on the GPU
|
||||
if (timer_index == 0 || m_gpu->IsCRTCScanlinePending())
|
||||
m_gpu->SynchronizeCRTC();
|
||||
if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
|
||||
g_gpu->SynchronizeCRTC();
|
||||
}
|
||||
|
||||
m_sysclk_event->InvokeEarly();
|
||||
|
@ -196,8 +201,8 @@ u32 Timers::ReadRegister(u32 offset)
|
|||
if (timer_index < 2 && cs.external_counting_enabled)
|
||||
{
|
||||
// timers 0/1 depend on the GPU
|
||||
if (timer_index == 0 || m_gpu->IsCRTCScanlinePending())
|
||||
m_gpu->SynchronizeCRTC();
|
||||
if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
|
||||
g_gpu->SynchronizeCRTC();
|
||||
}
|
||||
|
||||
m_sysclk_event->InvokeEarly();
|
||||
|
@ -227,8 +232,8 @@ void Timers::WriteRegister(u32 offset, u32 value)
|
|||
if (timer_index < 2 && cs.external_counting_enabled)
|
||||
{
|
||||
// timers 0/1 depend on the GPU
|
||||
if (timer_index == 0 || m_gpu->IsCRTCScanlinePending())
|
||||
m_gpu->SynchronizeCRTC();
|
||||
if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
|
||||
g_gpu->SynchronizeCRTC();
|
||||
}
|
||||
|
||||
m_sysclk_event->InvokeEarly();
|
||||
|
@ -311,7 +316,7 @@ void Timers::UpdateIRQ(u32 index)
|
|||
|
||||
Log_DebugPrintf("Raising timer %u IRQ", index);
|
||||
cs.irq_done = true;
|
||||
m_interrupt_controller->InterruptRequest(
|
||||
g_interrupt_controller.InterruptRequest(
|
||||
static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + index));
|
||||
}
|
||||
|
||||
|
@ -367,7 +372,7 @@ void Timers::DrawDebugStateWindow()
|
|||
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 100.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("Timer State", &m_system->GetSettings().debugging.show_timers_state))
|
||||
if (!ImGui::Begin("Timer State", &g_settings.debugging.show_timers_state))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
|
|
|
@ -6,18 +6,17 @@
|
|||
|
||||
class StateWrapper;
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class InterruptController;
|
||||
class GPU;
|
||||
|
||||
class Timers
|
||||
class Timers final
|
||||
{
|
||||
public:
|
||||
Timers();
|
||||
~Timers();
|
||||
|
||||
void Initialize(System* system, InterruptController* interrupt_controller, GPU* gpu);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
|
@ -26,10 +25,10 @@ public:
|
|||
void DrawDebugStateWindow();
|
||||
|
||||
// dot clock/hblank/sysclk div 8
|
||||
bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
|
||||
ALWAYS_INLINE bool IsUsingExternalClock(u32 timer) const { return m_states[timer].external_counting_enabled; }
|
||||
|
||||
// queries for GPU
|
||||
bool IsExternalIRQEnabled(u32 timer) const
|
||||
ALWAYS_INLINE bool IsExternalIRQEnabled(u32 timer) const
|
||||
{
|
||||
const CounterState& cs = m_states[timer];
|
||||
return (cs.external_counting_enabled && (cs.mode.bits & ((1u << 4) | (1u << 5))) != 0);
|
||||
|
@ -42,9 +41,6 @@ public:
|
|||
u32 ReadRegister(u32 offset);
|
||||
void WriteRegister(u32 offset, u32 value);
|
||||
|
||||
// changing interfaces
|
||||
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
||||
|
||||
private:
|
||||
static constexpr u32 NUM_TIMERS = 3;
|
||||
|
||||
|
@ -93,11 +89,10 @@ private:
|
|||
TickCount GetTicksUntilNextInterrupt() const;
|
||||
void UpdateSysClkEvent();
|
||||
|
||||
System* m_system = nullptr;
|
||||
InterruptController* m_interrupt_controller = nullptr;
|
||||
GPU* m_gpu = nullptr;
|
||||
std::unique_ptr<TimingEvent> m_sysclk_event;
|
||||
|
||||
std::array<CounterState, NUM_TIMERS> m_states{};
|
||||
u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8
|
||||
};
|
||||
|
||||
extern Timers g_timers;
|
|
@ -1,34 +1,268 @@
|
|||
#include "timing_event.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "cpu_core.h"
|
||||
#include "system.h"
|
||||
Log_SetChannel(TimingEvents);
|
||||
|
||||
TimingEvent::TimingEvent(System* system, std::string name, TickCount period, TickCount interval,
|
||||
TimingEventCallback callback)
|
||||
namespace TimingEvents {
|
||||
|
||||
static std::vector<TimingEvent*> s_events;
|
||||
static u32 s_global_tick_counter = 0;
|
||||
static u32 s_last_event_run_time = 0;
|
||||
static bool s_running_events = false;
|
||||
static bool s_events_need_sorting = false;
|
||||
|
||||
u32 GetGlobalTickCounter()
|
||||
{
|
||||
return s_global_tick_counter;
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
s_global_tick_counter = 0;
|
||||
s_last_event_run_time = 0;
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
Assert(s_events.empty());
|
||||
}
|
||||
|
||||
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
|
||||
TimingEventCallback callback, bool activate)
|
||||
{
|
||||
std::unique_ptr<TimingEvent> event =
|
||||
std::make_unique<TimingEvent>(std::move(name), period, interval, std::move(callback));
|
||||
if (activate)
|
||||
event->Activate();
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void UpdateCPUDowncount()
|
||||
{
|
||||
if (!CPU::g_state.frame_done)
|
||||
CPU::g_state.downcount = s_events[0]->GetDowncount();
|
||||
}
|
||||
|
||||
static bool CompareEvents(const TimingEvent* lhs, const TimingEvent* rhs)
|
||||
{
|
||||
return lhs->GetDowncount() > rhs->GetDowncount();
|
||||
}
|
||||
|
||||
static void AddActiveEvent(TimingEvent* event)
|
||||
{
|
||||
s_events.push_back(event);
|
||||
if (!s_running_events)
|
||||
{
|
||||
std::push_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_events_need_sorting = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void RemoveActiveEvent(TimingEvent* event)
|
||||
{
|
||||
auto iter = std::find_if(s_events.begin(), s_events.end(), [event](const auto& it) { return event == it; });
|
||||
if (iter == s_events.end())
|
||||
{
|
||||
Panic("Attempt to remove inactive event");
|
||||
return;
|
||||
}
|
||||
|
||||
s_events.erase(iter);
|
||||
if (!s_running_events)
|
||||
{
|
||||
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
if (!s_events.empty())
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_events_need_sorting = true;
|
||||
}
|
||||
}
|
||||
|
||||
static TimingEvent* FindActiveEvent(const char* name)
|
||||
{
|
||||
auto iter =
|
||||
std::find_if(s_events.begin(), s_events.end(), [&name](auto& ev) { return ev->GetName().compare(name) == 0; });
|
||||
|
||||
return (iter != s_events.end()) ? *iter : nullptr;
|
||||
}
|
||||
|
||||
static void SortEvents()
|
||||
{
|
||||
if (!s_running_events)
|
||||
{
|
||||
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_events_need_sorting = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RunEvents()
|
||||
{
|
||||
DebugAssert(!s_running_events && !s_events.empty());
|
||||
|
||||
s_running_events = true;
|
||||
|
||||
TickCount pending_ticks = (s_global_tick_counter + CPU::GetPendingTicks()) - s_last_event_run_time;
|
||||
CPU::ResetPendingTicks();
|
||||
while (pending_ticks > 0)
|
||||
{
|
||||
const TickCount time = std::min(pending_ticks, s_events[0]->GetDowncount());
|
||||
s_global_tick_counter += static_cast<u32>(time);
|
||||
pending_ticks -= time;
|
||||
|
||||
// Apply downcount to all events.
|
||||
// This will result in a negative downcount for those events which are late.
|
||||
for (TimingEvent* evt : s_events)
|
||||
{
|
||||
evt->m_downcount -= time;
|
||||
evt->m_time_since_last_run += time;
|
||||
}
|
||||
|
||||
// Now we can actually run the callbacks.
|
||||
while (s_events.front()->m_downcount <= 0)
|
||||
{
|
||||
TimingEvent* evt = s_events.front();
|
||||
std::pop_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
|
||||
// Factor late time into the time for the next invocation.
|
||||
const TickCount ticks_late = -evt->m_downcount;
|
||||
const TickCount ticks_to_execute = evt->m_time_since_last_run;
|
||||
evt->m_downcount += evt->m_interval;
|
||||
evt->m_time_since_last_run = 0;
|
||||
|
||||
// The cycles_late is only an indicator, it doesn't modify the cycles to execute.
|
||||
evt->m_callback(ticks_to_execute, ticks_late);
|
||||
|
||||
// Place it in the appropriate position in the queue.
|
||||
if (s_events_need_sorting)
|
||||
{
|
||||
// Another event may have been changed by this event, or the interval/downcount changed.
|
||||
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
s_events_need_sorting = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep the event list in a heap. The event we just serviced will be in the last place,
|
||||
// so we can use push_here instead of make_heap, which should be faster.
|
||||
std::push_heap(s_events.begin(), s_events.end(), CompareEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_last_event_run_time = s_global_tick_counter;
|
||||
s_running_events = false;
|
||||
UpdateCPUDowncount();
|
||||
}
|
||||
|
||||
bool DoState(StateWrapper& sw, u32 global_tick_counter)
|
||||
{
|
||||
if (sw.IsReading())
|
||||
{
|
||||
s_global_tick_counter = global_tick_counter;
|
||||
|
||||
// Load timestamps for the clock events.
|
||||
// Any oneshot events should be recreated by the load state method, so we can fix up their times here.
|
||||
u32 event_count = 0;
|
||||
sw.Do(&event_count);
|
||||
|
||||
for (u32 i = 0; i < event_count; i++)
|
||||
{
|
||||
std::string event_name;
|
||||
TickCount downcount, time_since_last_run, period, interval;
|
||||
sw.Do(&event_name);
|
||||
sw.Do(&downcount);
|
||||
sw.Do(&time_since_last_run);
|
||||
sw.Do(&period);
|
||||
sw.Do(&interval);
|
||||
if (sw.HasError())
|
||||
return false;
|
||||
|
||||
TimingEvent* event = FindActiveEvent(event_name.c_str());
|
||||
if (!event)
|
||||
{
|
||||
Log_WarningPrintf("Save state has event '%s', but couldn't find this event when loading.", event_name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Using reschedule is safe here since we call sort afterwards.
|
||||
event->m_downcount = downcount;
|
||||
event->m_time_since_last_run = time_since_last_run;
|
||||
event->m_period = period;
|
||||
event->m_interval = interval;
|
||||
}
|
||||
|
||||
sw.Do(&s_last_event_run_time);
|
||||
|
||||
Log_DevPrintf("Loaded %u events from save state.", event_count);
|
||||
SortEvents();
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 event_count = static_cast<u32>(s_events.size());
|
||||
sw.Do(&event_count);
|
||||
|
||||
for (TimingEvent* evt : s_events)
|
||||
{
|
||||
sw.Do(&evt->m_name);
|
||||
sw.Do(&evt->m_downcount);
|
||||
sw.Do(&evt->m_time_since_last_run);
|
||||
sw.Do(&evt->m_period);
|
||||
sw.Do(&evt->m_interval);
|
||||
}
|
||||
|
||||
sw.Do(&s_last_event_run_time);
|
||||
|
||||
Log_DevPrintf("Wrote %u events to save state.", event_count);
|
||||
}
|
||||
|
||||
return !sw.HasError();
|
||||
}
|
||||
|
||||
} // namespace TimingEvents
|
||||
|
||||
TimingEvent::TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback)
|
||||
: m_downcount(interval), m_time_since_last_run(0), m_period(period), m_interval(interval),
|
||||
m_callback(std::move(callback)), m_system(system), m_name(std::move(name)), m_active(false)
|
||||
m_callback(std::move(callback)), m_name(std::move(name)), m_active(false)
|
||||
{
|
||||
}
|
||||
|
||||
TimingEvent::~TimingEvent()
|
||||
{
|
||||
if (m_active)
|
||||
m_system->RemoveActiveEvent(this);
|
||||
TimingEvents::RemoveActiveEvent(this);
|
||||
}
|
||||
|
||||
TickCount TimingEvent::GetTicksSinceLastExecution() const
|
||||
{
|
||||
return m_system->m_cpu->GetPendingTicks() + m_time_since_last_run;
|
||||
return CPU::GetPendingTicks() + m_time_since_last_run;
|
||||
}
|
||||
|
||||
TickCount TimingEvent::GetTicksUntilNextExecution() const
|
||||
{
|
||||
return std::max(m_downcount - m_system->m_cpu->GetPendingTicks(), static_cast<TickCount>(0));
|
||||
return std::max(m_downcount - CPU::GetPendingTicks(), static_cast<TickCount>(0));
|
||||
}
|
||||
|
||||
void TimingEvent::Schedule(TickCount ticks)
|
||||
{
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
m_downcount = pending_ticks + ticks;
|
||||
|
||||
if (!m_active)
|
||||
|
@ -36,13 +270,13 @@ void TimingEvent::Schedule(TickCount ticks)
|
|||
// Event is going active, so we want it to only execute ticks from the current timestamp.
|
||||
m_time_since_last_run = -pending_ticks;
|
||||
m_active = true;
|
||||
m_system->AddActiveEvent(this);
|
||||
TimingEvents::AddActiveEvent(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Event is already active, so we leave the time since last run alone, and just modify the downcount.
|
||||
// If this is a call from an IO handler for example, re-sort the event queue.
|
||||
m_system->SortEvents();
|
||||
TimingEvents::SortEvents();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +300,7 @@ void TimingEvent::Reset()
|
|||
|
||||
m_downcount = m_interval;
|
||||
m_time_since_last_run = 0;
|
||||
m_system->SortEvents();
|
||||
TimingEvents::SortEvents();
|
||||
}
|
||||
|
||||
void TimingEvent::InvokeEarly(bool force /* = false */)
|
||||
|
@ -74,7 +308,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
|
|||
if (!m_active)
|
||||
return;
|
||||
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
const TickCount ticks_to_execute = m_time_since_last_run + pending_ticks;
|
||||
if (!force && ticks_to_execute < m_period)
|
||||
return;
|
||||
|
@ -84,7 +318,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
|
|||
m_callback(ticks_to_execute, 0);
|
||||
|
||||
// Since we've changed the downcount, we need to re-sort the events.
|
||||
m_system->SortEvents();
|
||||
TimingEvents::SortEvents();
|
||||
}
|
||||
|
||||
void TimingEvent::Activate()
|
||||
|
@ -93,12 +327,12 @@ void TimingEvent::Activate()
|
|||
return;
|
||||
|
||||
// leave the downcount intact
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
m_downcount += pending_ticks;
|
||||
m_time_since_last_run -= pending_ticks;
|
||||
|
||||
m_active = true;
|
||||
m_system->AddActiveEvent(this);
|
||||
TimingEvents::AddActiveEvent(this);
|
||||
}
|
||||
|
||||
void TimingEvent::Deactivate()
|
||||
|
@ -106,10 +340,10 @@ void TimingEvent::Deactivate()
|
|||
if (!m_active)
|
||||
return;
|
||||
|
||||
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
|
||||
const TickCount pending_ticks = CPU::GetPendingTicks();
|
||||
m_downcount -= pending_ticks;
|
||||
m_time_since_last_run += pending_ticks;
|
||||
|
||||
m_active = false;
|
||||
m_system->RemoveActiveEvent(this);
|
||||
TimingEvents::RemoveActiveEvent(this);
|
||||
}
|
||||
|
|
|
@ -6,29 +6,24 @@
|
|||
|
||||
#include "types.h"
|
||||
|
||||
class System;
|
||||
class TimingEvent;
|
||||
class StateWrapper;
|
||||
|
||||
// Event callback type. Second parameter is the number of cycles the event was executed "late".
|
||||
using TimingEventCallback = std::function<void(TickCount ticks, TickCount ticks_late)>;
|
||||
|
||||
class TimingEvent
|
||||
{
|
||||
friend System;
|
||||
|
||||
public:
|
||||
TimingEvent(System* system, std::string name, TickCount period, TickCount interval, TimingEventCallback callback);
|
||||
TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback);
|
||||
~TimingEvent();
|
||||
|
||||
System* GetSystem() const { return m_system; }
|
||||
const std::string& GetName() const { return m_name; }
|
||||
bool IsActive() const { return m_active; }
|
||||
|
||||
// Returns the number of ticks between each event.
|
||||
TickCount GetPeriod() const { return m_period; }
|
||||
TickCount GetInterval() const { return m_interval; }
|
||||
|
||||
TickCount GetDowncount() const { return m_downcount; }
|
||||
ALWAYS_INLINE TickCount GetPeriod() const { return m_period; }
|
||||
ALWAYS_INLINE TickCount GetInterval() const { return m_interval; }
|
||||
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
|
||||
|
||||
// Includes pending time.
|
||||
TickCount GetTicksSinceLastExecution() const;
|
||||
|
@ -61,14 +56,35 @@ public:
|
|||
void SetInterval(TickCount interval) { m_interval = interval; }
|
||||
void SetPeriod(TickCount period) { m_period = period; }
|
||||
|
||||
private:
|
||||
TickCount m_downcount;
|
||||
TickCount m_time_since_last_run;
|
||||
TickCount m_period;
|
||||
TickCount m_interval;
|
||||
|
||||
TimingEventCallback m_callback;
|
||||
System* m_system;
|
||||
std::string m_name;
|
||||
bool m_active;
|
||||
};
|
||||
|
||||
namespace TimingEvents {
|
||||
|
||||
u32 GetGlobalTickCounter();
|
||||
|
||||
void Initialize();
|
||||
void Reset();
|
||||
void Shutdown();
|
||||
|
||||
/// Creates a new event.
|
||||
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
|
||||
TimingEventCallback callback, bool activate);
|
||||
|
||||
/// Serialization.
|
||||
bool DoState(StateWrapper& sw, u32 global_tick_counter);
|
||||
|
||||
void RunEvents();
|
||||
|
||||
void UpdateCPUDowncount();
|
||||
|
||||
|
||||
|
||||
} // namespace TimingEventManager
|
|
@ -162,13 +162,13 @@ void LibretroHostInterface::AddOSDMessage(std::string message, float duration /*
|
|||
{
|
||||
retro_message msg = {};
|
||||
msg.msg = message.c_str();
|
||||
msg.frames = static_cast<u32>(duration * (m_system ? m_system->GetThrottleFrequency() : 60.0f));
|
||||
msg.frames = static_cast<u32>(duration * (System::IsShutdown() ? 60.0f : System::GetThrottleFrequency()));
|
||||
g_retro_environment_callback(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
|
||||
}
|
||||
|
||||
void LibretroHostInterface::retro_get_system_av_info(struct retro_system_av_info* info)
|
||||
{
|
||||
const bool use_resolution_scale = (m_settings.gpu_renderer != GPURenderer::Software);
|
||||
const bool use_resolution_scale = (g_settings.gpu_renderer != GPURenderer::Software);
|
||||
GetSystemAVInfo(info, use_resolution_scale);
|
||||
|
||||
Log_InfoPrintf("base = %ux%u, max = %ux%u, aspect ratio = %.2f, fps = %.2f", info->geometry.base_width,
|
||||
|
@ -178,18 +178,18 @@ void LibretroHostInterface::retro_get_system_av_info(struct retro_system_av_info
|
|||
|
||||
void LibretroHostInterface::GetSystemAVInfo(struct retro_system_av_info* info, bool use_resolution_scale)
|
||||
{
|
||||
const u32 resolution_scale = use_resolution_scale ? m_settings.gpu_resolution_scale : 1u;
|
||||
Assert(m_system);
|
||||
const u32 resolution_scale = use_resolution_scale ? g_settings.gpu_resolution_scale : 1u;
|
||||
Assert(System::IsValid());
|
||||
|
||||
std::memset(info, 0, sizeof(*info));
|
||||
|
||||
info->geometry.aspect_ratio = Settings::GetDisplayAspectRatioValue(m_settings.display_aspect_ratio);
|
||||
info->geometry.aspect_ratio = Settings::GetDisplayAspectRatioValue(g_settings.display_aspect_ratio);
|
||||
info->geometry.base_width = 320;
|
||||
info->geometry.base_height = 240;
|
||||
info->geometry.max_width = GPU::VRAM_WIDTH * resolution_scale;
|
||||
info->geometry.max_height = GPU::VRAM_HEIGHT * resolution_scale;
|
||||
|
||||
info->timing.fps = m_system->GetThrottleFrequency();
|
||||
info->timing.fps = System::GetThrottleFrequency();
|
||||
info->timing.sample_rate = static_cast<double>(AUDIO_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ void LibretroHostInterface::UpdateSystemAVInfo(bool use_resolution_scale)
|
|||
void LibretroHostInterface::UpdateGeometry()
|
||||
{
|
||||
struct retro_system_av_info avi;
|
||||
const bool use_resolution_scale = (m_settings.gpu_renderer != GPURenderer::Software);
|
||||
const bool use_resolution_scale = (g_settings.gpu_renderer != GPURenderer::Software);
|
||||
GetSystemAVInfo(&avi, use_resolution_scale);
|
||||
|
||||
Log_InfoPrintf("base = %ux%u, max = %ux%u, aspect ratio = %.2f", avi.geometry.base_width, avi.geometry.base_height,
|
||||
|
@ -221,12 +221,12 @@ void LibretroHostInterface::UpdateGeometry()
|
|||
|
||||
void LibretroHostInterface::UpdateLogging()
|
||||
{
|
||||
Log::SetFilterLevel(m_settings.log_level);
|
||||
Log::SetFilterLevel(g_settings.log_level);
|
||||
|
||||
if (s_libretro_log_callback_valid)
|
||||
Log::SetConsoleOutputParams(false);
|
||||
else
|
||||
Log::SetConsoleOutputParams(true, nullptr, m_settings.log_level);
|
||||
Log::SetConsoleOutputParams(true, nullptr, g_settings.log_level);
|
||||
}
|
||||
|
||||
bool LibretroHostInterface::retro_load_game(const struct retro_game_info* game)
|
||||
|
@ -239,7 +239,7 @@ bool LibretroHostInterface::retro_load_game(const struct retro_game_info* game)
|
|||
if (!BootSystem(bp))
|
||||
return false;
|
||||
|
||||
if (m_settings.gpu_renderer != GPURenderer::Software)
|
||||
if (g_settings.gpu_renderer != GPURenderer::Software)
|
||||
{
|
||||
if (!m_hw_render_callback_valid)
|
||||
RequestHardwareRendererContext();
|
||||
|
@ -252,25 +252,25 @@ bool LibretroHostInterface::retro_load_game(const struct retro_game_info* game)
|
|||
|
||||
void LibretroHostInterface::retro_run_frame()
|
||||
{
|
||||
Assert(m_system);
|
||||
Assert(!System::IsShutdown());
|
||||
|
||||
if (HasCoreVariablesChanged())
|
||||
UpdateSettings();
|
||||
|
||||
UpdateControllers();
|
||||
|
||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
|
||||
m_system->RunFrame();
|
||||
System::RunFrame();
|
||||
|
||||
m_system->GetGPU()->ResetGraphicsAPIState();
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
|
||||
m_display->Render();
|
||||
}
|
||||
|
||||
unsigned LibretroHostInterface::retro_get_region()
|
||||
{
|
||||
return m_system->IsPALRegion() ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
|
||||
return System::IsPALRegion() ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
|
||||
}
|
||||
|
||||
size_t LibretroHostInterface::retro_serialize_size()
|
||||
|
@ -281,7 +281,7 @@ size_t LibretroHostInterface::retro_serialize_size()
|
|||
bool LibretroHostInterface::retro_serialize(void* data, size_t size)
|
||||
{
|
||||
std::unique_ptr<ByteStream> stream = ByteStream_CreateMemoryStream(data, static_cast<u32>(size));
|
||||
if (!m_system->SaveState(stream.get(), 0))
|
||||
if (!System::SaveState(stream.get(), 0))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to save state to memory stream");
|
||||
return false;
|
||||
|
@ -293,7 +293,7 @@ bool LibretroHostInterface::retro_serialize(void* data, size_t size)
|
|||
bool LibretroHostInterface::retro_unserialize(const void* data, size_t size)
|
||||
{
|
||||
std::unique_ptr<ByteStream> stream = ByteStream_CreateReadOnlyMemoryStream(data, static_cast<u32>(size));
|
||||
if (!m_system->LoadState(stream.get()))
|
||||
if (!System::LoadState(stream.get()))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to load save state from memory stream");
|
||||
return false;
|
||||
|
@ -307,7 +307,7 @@ void* LibretroHostInterface::retro_get_memory_data(unsigned id)
|
|||
switch (id)
|
||||
{
|
||||
case RETRO_MEMORY_SYSTEM_RAM:
|
||||
return m_system ? m_system->GetBus()->GetRAM() : nullptr;
|
||||
return System::IsShutdown() ? nullptr : Bus::g_ram;
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
|
@ -552,27 +552,27 @@ bool LibretroHostInterface::HasCoreVariablesChanged()
|
|||
void LibretroHostInterface::LoadSettings()
|
||||
{
|
||||
LibretroSettingsInterface si;
|
||||
m_settings.Load(si);
|
||||
g_settings.Load(si);
|
||||
|
||||
// Assume BIOS files are located in system directory.
|
||||
const char* system_directory = nullptr;
|
||||
if (!g_retro_environment_callback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_directory) || !system_directory)
|
||||
system_directory = "bios";
|
||||
m_settings.bios_path =
|
||||
g_settings.bios_path =
|
||||
StringUtil::StdStringFromFormat("%s%cscph1001.bin", system_directory, FS_OSPATH_SEPERATOR_CHARACTER);
|
||||
|
||||
// Ensure we don't use the standalone memcard directory in shared mode.
|
||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||
m_settings.memory_card_paths[i] = GetSharedMemoryCardPath(i);
|
||||
g_settings.memory_card_paths[i] = GetSharedMemoryCardPath(i);
|
||||
}
|
||||
|
||||
void LibretroHostInterface::UpdateSettings()
|
||||
{
|
||||
Settings old_settings(std::move(m_settings));
|
||||
Settings old_settings(std::move(g_settings));
|
||||
LoadSettings();
|
||||
|
||||
if (m_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale &&
|
||||
m_settings.gpu_renderer != GPURenderer::Software)
|
||||
if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale &&
|
||||
g_settings.gpu_renderer != GPURenderer::Software)
|
||||
{
|
||||
ReportMessage("Resolution changed, updating system AV info...");
|
||||
|
||||
|
@ -588,14 +588,14 @@ void LibretroHostInterface::UpdateSettings()
|
|||
SwitchToHardwareRenderer();
|
||||
|
||||
// Don't let the base class mess with the GPU.
|
||||
old_settings.gpu_resolution_scale = m_settings.gpu_resolution_scale;
|
||||
old_settings.gpu_resolution_scale = g_settings.gpu_resolution_scale;
|
||||
}
|
||||
|
||||
if (m_settings.gpu_renderer != old_settings.gpu_renderer)
|
||||
if (g_settings.gpu_renderer != old_settings.gpu_renderer)
|
||||
{
|
||||
ReportFormattedMessage("Switch to %s renderer pending, please restart the core to apply.",
|
||||
Settings::GetRendererDisplayName(m_settings.gpu_renderer));
|
||||
m_settings.gpu_renderer = old_settings.gpu_renderer;
|
||||
Settings::GetRendererDisplayName(g_settings.gpu_renderer));
|
||||
g_settings.gpu_renderer = old_settings.gpu_renderer;
|
||||
}
|
||||
|
||||
CheckForSettingsChanges(old_settings);
|
||||
|
@ -605,10 +605,10 @@ void LibretroHostInterface::CheckForSettingsChanges(const Settings& old_settings
|
|||
{
|
||||
HostInterface::CheckForSettingsChanges(old_settings);
|
||||
|
||||
if (m_settings.display_aspect_ratio != old_settings.display_aspect_ratio)
|
||||
if (g_settings.display_aspect_ratio != old_settings.display_aspect_ratio)
|
||||
UpdateGeometry();
|
||||
|
||||
if (m_settings.log_level != old_settings.log_level)
|
||||
if (g_settings.log_level != old_settings.log_level)
|
||||
UpdateLogging();
|
||||
}
|
||||
|
||||
|
@ -618,7 +618,7 @@ void LibretroHostInterface::UpdateControllers()
|
|||
|
||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||
{
|
||||
switch (m_settings.controller_types[i])
|
||||
switch (g_settings.controller_types[i])
|
||||
{
|
||||
case ControllerType::None:
|
||||
break;
|
||||
|
@ -633,7 +633,7 @@ void LibretroHostInterface::UpdateControllers()
|
|||
|
||||
default:
|
||||
ReportFormattedError("Unhandled controller type '%s'.",
|
||||
Settings::GetControllerTypeDisplayName(m_settings.controller_types[i]));
|
||||
Settings::GetControllerTypeDisplayName(g_settings.controller_types[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -641,7 +641,7 @@ void LibretroHostInterface::UpdateControllers()
|
|||
|
||||
void LibretroHostInterface::UpdateControllersDigitalController(u32 index)
|
||||
{
|
||||
DigitalController* controller = static_cast<DigitalController*>(m_system->GetController(index));
|
||||
DigitalController* controller = static_cast<DigitalController*>(System::GetController(index));
|
||||
DebugAssert(controller);
|
||||
|
||||
static constexpr std::array<std::pair<DigitalController::Button, u32>, 14> mapping = {
|
||||
|
@ -669,7 +669,7 @@ void LibretroHostInterface::UpdateControllersDigitalController(u32 index)
|
|||
|
||||
void LibretroHostInterface::UpdateControllersAnalogController(u32 index)
|
||||
{
|
||||
AnalogController* controller = static_cast<AnalogController*>(m_system->GetController(index));
|
||||
AnalogController* controller = static_cast<AnalogController*>(System::GetController(index));
|
||||
DebugAssert(controller);
|
||||
|
||||
static constexpr std::array<std::pair<AnalogController::Button, u32>, 16> button_mapping = {
|
||||
|
@ -875,8 +875,8 @@ void LibretroHostInterface::SwitchToHardwareRenderer()
|
|||
wi.surface_width = avi.geometry.base_width;
|
||||
wi.surface_height = avi.geometry.base_height;
|
||||
wi.surface_scale = 1.0f;
|
||||
if (!display || !display->CreateRenderDevice(wi, {}, g_libretro_host_interface.m_settings.gpu_use_debug_device) ||
|
||||
!display->InitializeRenderDevice({}, m_settings.gpu_use_debug_device))
|
||||
if (!display || !display->CreateRenderDevice(wi, {}, g_settings.gpu_use_debug_device) ||
|
||||
!display->InitializeRenderDevice({}, g_settings.gpu_use_debug_device))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create hardware host display");
|
||||
return;
|
||||
|
@ -884,7 +884,7 @@ void LibretroHostInterface::SwitchToHardwareRenderer()
|
|||
}
|
||||
|
||||
std::swap(display, g_libretro_host_interface.m_display);
|
||||
g_libretro_host_interface.m_system->RecreateGPU(renderer.value());
|
||||
System::RecreateGPU(renderer.value());
|
||||
display->DestroyRenderDevice();
|
||||
m_using_hardware_renderer = true;
|
||||
}
|
||||
|
@ -917,13 +917,12 @@ void LibretroHostInterface::SwitchToSoftwareRenderer()
|
|||
}
|
||||
|
||||
m_display = std::make_unique<LibretroHostDisplay>();
|
||||
m_system->RecreateGPU(GPURenderer::Software);
|
||||
System::RecreateGPU(GPURenderer::Software);
|
||||
}
|
||||
|
||||
bool LibretroHostInterface::DiskControlSetEjectState(bool ejected)
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("DiskControlSetEjectState() - no system");
|
||||
return false;
|
||||
|
@ -933,51 +932,48 @@ bool LibretroHostInterface::DiskControlSetEjectState(bool ejected)
|
|||
|
||||
if (ejected)
|
||||
{
|
||||
if (!system->HasMedia())
|
||||
if (!System::HasMedia())
|
||||
return false;
|
||||
|
||||
system->RemoveMedia();
|
||||
System::RemoveMedia();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 image_to_insert = P_THIS->m_next_disc_index.value_or(0);
|
||||
Log_DevPrintf("Inserting image %u", image_to_insert);
|
||||
return system->SwitchMediaFromPlaylist(image_to_insert);
|
||||
return System::SwitchMediaFromPlaylist(image_to_insert);
|
||||
}
|
||||
}
|
||||
|
||||
bool LibretroHostInterface::DiskControlGetEjectState()
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("DiskControlGetEjectState() - no system");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_DevPrintf("DiskControlGetEjectState() -> %u", static_cast<unsigned>(system->HasMedia()));
|
||||
return system->HasMedia();
|
||||
Log_DevPrintf("DiskControlGetEjectState() -> %u", static_cast<unsigned>(System::HasMedia()));
|
||||
return System::HasMedia();
|
||||
}
|
||||
|
||||
unsigned LibretroHostInterface::DiskControlGetImageIndex()
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("DiskControlGetImageIndex() - no system");
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 index = P_THIS->m_next_disc_index.value_or(system->GetMediaPlaylistIndex());
|
||||
const u32 index = P_THIS->m_next_disc_index.value_or(System::GetMediaPlaylistIndex());
|
||||
Log_DevPrintf("DiskControlGetImageIndex() -> %u", index);
|
||||
return index;
|
||||
}
|
||||
|
||||
bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index)
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("DiskControlSetImageIndex() - no system");
|
||||
return false;
|
||||
|
@ -985,7 +981,7 @@ bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index)
|
|||
|
||||
Log_DevPrintf("DiskControlSetImageIndex(%u)", index);
|
||||
|
||||
if (index >= system->GetMediaPlaylistCount())
|
||||
if (index >= System::GetMediaPlaylistCount())
|
||||
return false;
|
||||
|
||||
P_THIS->m_next_disc_index = index;
|
||||
|
@ -994,21 +990,19 @@ bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index)
|
|||
|
||||
unsigned LibretroHostInterface::DiskControlGetNumImages()
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("DiskControlGetNumImages() - no system");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_DevPrintf("DiskControlGetNumImages() -> %u", system->GetMediaPlaylistCount());
|
||||
return static_cast<unsigned>(system->GetMediaPlaylistCount());
|
||||
Log_DevPrintf("DiskControlGetNumImages() -> %u", System::GetMediaPlaylistCount());
|
||||
return static_cast<unsigned>(System::GetMediaPlaylistCount());
|
||||
}
|
||||
|
||||
bool LibretroHostInterface::DiskControlReplaceImageIndex(unsigned index, const retro_game_info* info)
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("DiskControlReplaceImageIndex() - no system");
|
||||
return false;
|
||||
|
@ -1016,22 +1010,21 @@ bool LibretroHostInterface::DiskControlReplaceImageIndex(unsigned index, const r
|
|||
|
||||
Log_DevPrintf("DiskControlReplaceImageIndex(%u, %s)", index, info ? info->path : "null");
|
||||
if (info && info->path)
|
||||
return system->ReplaceMediaPathFromPlaylist(index, info->path);
|
||||
return System::ReplaceMediaPathFromPlaylist(index, info->path);
|
||||
else
|
||||
return system->RemoveMediaPathFromPlaylist(index);
|
||||
return System::RemoveMediaPathFromPlaylist(index);
|
||||
}
|
||||
|
||||
bool LibretroHostInterface::DiskControlAddImageIndex()
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("DiskControlAddImageIndex() - no system");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_DevPrintf("DiskControlAddImageIndex() -> %zu", system->GetMediaPlaylistCount());
|
||||
system->AddMediaPathToPlaylist({});
|
||||
Log_DevPrintf("DiskControlAddImageIndex() -> %zu", System::GetMediaPlaylistCount());
|
||||
System::AddMediaPathToPlaylist({});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1044,11 +1037,10 @@ bool LibretroHostInterface::DiskControlSetInitialImage(unsigned index, const cha
|
|||
|
||||
bool LibretroHostInterface::DiskControlGetImagePath(unsigned index, char* path, size_t len)
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system || index >= system->GetMediaPlaylistCount())
|
||||
if (System::IsShutdown() || index >= System::GetMediaPlaylistCount())
|
||||
return false;
|
||||
|
||||
const std::string& image_path = system->GetMediaPlaylistPath(index);
|
||||
const std::string& image_path = System::GetMediaPlaylistPath(index);
|
||||
Log_DevPrintf("DiskControlGetImagePath(%u) -> %s", index, image_path.c_str());
|
||||
if (image_path.empty())
|
||||
return false;
|
||||
|
@ -1059,11 +1051,10 @@ bool LibretroHostInterface::DiskControlGetImagePath(unsigned index, char* path,
|
|||
|
||||
bool LibretroHostInterface::DiskControlGetImageLabel(unsigned index, char* label, size_t len)
|
||||
{
|
||||
System* system = P_THIS->GetSystem();
|
||||
if (!system || index >= system->GetMediaPlaylistCount())
|
||||
if (System::IsShutdown() || index >= System::GetMediaPlaylistCount())
|
||||
return false;
|
||||
|
||||
const std::string& image_path = system->GetMediaPlaylistPath(index);
|
||||
const std::string& image_path = System::GetMediaPlaylistPath(index);
|
||||
if (image_path.empty())
|
||||
return false;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ public:
|
|||
static bool HasCoreVariablesChanged();
|
||||
static void InitDiskControlInterface();
|
||||
|
||||
ALWAYS_INLINE u32 GetResolutionScale() const { return m_settings.gpu_resolution_scale; }
|
||||
ALWAYS_INLINE u32 GetResolutionScale() const { return g_settings.gpu_resolution_scale; }
|
||||
|
||||
bool Initialize() override;
|
||||
void Shutdown() override;
|
||||
|
|
|
@ -269,7 +269,7 @@ void QtHostInterface::setDefaultSettings()
|
|||
return;
|
||||
}
|
||||
|
||||
Settings old_settings(std::move(m_settings));
|
||||
Settings old_settings(std::move(g_settings));
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_settings_mutex);
|
||||
SetDefaultSettings(*m_settings_interface.get());
|
||||
|
@ -289,7 +289,7 @@ void QtHostInterface::applySettings()
|
|||
return;
|
||||
}
|
||||
|
||||
Settings old_settings(std::move(m_settings));
|
||||
Settings old_settings(std::move(g_settings));
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_settings_mutex);
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
|
@ -298,7 +298,7 @@ void QtHostInterface::applySettings()
|
|||
CheckForSettingsChanges(old_settings);
|
||||
|
||||
// detect when render-to-main flag changes
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
const bool render_to_main = m_settings_interface->GetBoolValue("Main", "RenderToMainWindow", true);
|
||||
if (m_display && !m_is_fullscreen && render_to_main != m_is_rendering_to_main)
|
||||
|
@ -422,7 +422,7 @@ void QtHostInterface::onHostDisplayWindowResized(int width, int height)
|
|||
m_display->ResizeRenderWindow(width, height);
|
||||
|
||||
// re-render the display, since otherwise it will be out of date and stretched if paused
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
renderDisplay();
|
||||
}
|
||||
|
||||
|
@ -434,7 +434,7 @@ void QtHostInterface::redrawDisplayWindow()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!m_display || !m_system)
|
||||
if (!m_display || System::IsShutdown())
|
||||
return;
|
||||
|
||||
renderDisplay();
|
||||
|
@ -458,8 +458,8 @@ bool QtHostInterface::AcquireHostDisplay()
|
|||
m_is_rendering_to_main = m_settings_interface->GetBoolValue("Main", "RenderToMainWindow", true);
|
||||
|
||||
QtDisplayWidget* display_widget =
|
||||
createDisplayRequested(m_worker_thread, QString::fromStdString(m_settings.gpu_adapter),
|
||||
m_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main);
|
||||
createDisplayRequested(m_worker_thread, QString::fromStdString(g_settings.gpu_adapter),
|
||||
g_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main);
|
||||
if (!display_widget || !m_display->HasRenderDevice())
|
||||
{
|
||||
emit destroyDisplayRequested();
|
||||
|
@ -470,7 +470,7 @@ bool QtHostInterface::AcquireHostDisplay()
|
|||
createImGuiContext(display_widget->devicePixelRatioFromScreen());
|
||||
|
||||
if (!m_display->MakeRenderContextCurrent() ||
|
||||
!m_display->InitializeRenderDevice(GetShaderCacheBasePath(), m_settings.gpu_use_debug_device))
|
||||
!m_display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device))
|
||||
{
|
||||
destroyImGuiContext();
|
||||
m_display->DestroyRenderDevice();
|
||||
|
@ -486,7 +486,7 @@ bool QtHostInterface::AcquireHostDisplay()
|
|||
|
||||
HostDisplay* QtHostInterface::createHostDisplay()
|
||||
{
|
||||
switch (m_settings.gpu_renderer)
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
case GPURenderer::HardwareVulkan:
|
||||
m_display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
|
||||
|
@ -630,20 +630,19 @@ void QtHostInterface::OnSystemPerformanceCountersUpdated()
|
|||
{
|
||||
HostInterface::OnSystemPerformanceCountersUpdated();
|
||||
|
||||
DebugAssert(m_system);
|
||||
emit systemPerformanceCountersUpdated(m_system->GetEmulationSpeed(), m_system->GetFPS(), m_system->GetVPS(),
|
||||
m_system->GetAverageFrameTime(), m_system->GetWorstFrameTime());
|
||||
emit systemPerformanceCountersUpdated(System::GetEmulationSpeed(), System::GetFPS(), System::GetVPS(),
|
||||
System::GetAverageFrameTime(), System::GetWorstFrameTime());
|
||||
}
|
||||
|
||||
void QtHostInterface::OnRunningGameChanged()
|
||||
{
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
emit runningGameChanged(QString::fromStdString(m_system->GetRunningPath()),
|
||||
QString::fromStdString(m_system->GetRunningCode()),
|
||||
QString::fromStdString(m_system->GetRunningTitle()));
|
||||
emit runningGameChanged(QString::fromStdString(System::GetRunningPath()),
|
||||
QString::fromStdString(System::GetRunningCode()),
|
||||
QString::fromStdString(System::GetRunningTitle()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -653,7 +652,7 @@ void QtHostInterface::OnRunningGameChanged()
|
|||
|
||||
void QtHostInterface::OnSystemStateSaved(bool global, s32 slot)
|
||||
{
|
||||
emit stateSaved(QString::fromStdString(m_system->GetRunningCode()), global, slot);
|
||||
emit stateSaved(QString::fromStdString(System::GetRunningCode()), global, slot);
|
||||
}
|
||||
|
||||
void QtHostInterface::LoadSettings()
|
||||
|
@ -731,10 +730,10 @@ void QtHostInterface::powerOffSystem()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return;
|
||||
|
||||
if (m_settings.save_state_on_exit)
|
||||
if (g_settings.save_state_on_exit)
|
||||
SaveResumeSaveState();
|
||||
|
||||
DestroySystem();
|
||||
|
@ -756,7 +755,7 @@ void QtHostInterface::resetSystem()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
{
|
||||
Log_ErrorPrintf("resetSystem() called without system");
|
||||
return;
|
||||
|
@ -784,13 +783,13 @@ void QtHostInterface::changeDisc(const QString& new_disc_filename)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return;
|
||||
|
||||
if (!new_disc_filename.isEmpty())
|
||||
m_system->InsertMedia(new_disc_filename.toStdString().c_str());
|
||||
System::InsertMedia(new_disc_filename.toStdString().c_str());
|
||||
else
|
||||
m_system->RemoveMedia();
|
||||
System::RemoveMedia();
|
||||
}
|
||||
|
||||
static QString FormatTimestampForSaveStateMenu(u64 timestamp)
|
||||
|
@ -925,7 +924,7 @@ void QtHostInterface::saveState(bool global, qint32 slot, bool block_until_done
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
SaveState(global, slot);
|
||||
}
|
||||
|
||||
|
@ -940,7 +939,7 @@ void QtHostInterface::setAudioOutputVolume(int value)
|
|||
if (m_audio_stream)
|
||||
m_audio_stream->SetOutputVolume(value);
|
||||
|
||||
m_settings.audio_output_volume = value;
|
||||
g_settings.audio_output_volume = value;
|
||||
}
|
||||
|
||||
void QtHostInterface::setAudioOutputMuted(bool muted)
|
||||
|
@ -952,9 +951,9 @@ void QtHostInterface::setAudioOutputMuted(bool muted)
|
|||
}
|
||||
|
||||
if (m_audio_stream)
|
||||
m_audio_stream->SetOutputVolume(muted ? 0 : m_settings.audio_output_volume);
|
||||
m_audio_stream->SetOutputVolume(muted ? 0 : g_settings.audio_output_volume);
|
||||
|
||||
m_settings.audio_output_muted = muted;
|
||||
g_settings.audio_output_muted = muted;
|
||||
}
|
||||
|
||||
void QtHostInterface::startDumpingAudio()
|
||||
|
@ -1058,14 +1057,14 @@ void QtHostInterface::threadEntryPoint()
|
|||
// TODO: Event which flags the thread as ready
|
||||
while (!m_shutdown_flag.load())
|
||||
{
|
||||
if (!m_system || m_paused)
|
||||
if (!System::IsRunning())
|
||||
{
|
||||
// wait until we have a system before running
|
||||
m_worker_thread_event_loop->exec();
|
||||
continue;
|
||||
}
|
||||
|
||||
m_system->RunFrame();
|
||||
System::RunFrame();
|
||||
UpdateControllerRumble();
|
||||
if (m_frame_step_request)
|
||||
{
|
||||
|
@ -1075,10 +1074,10 @@ void QtHostInterface::threadEntryPoint()
|
|||
|
||||
renderDisplay();
|
||||
|
||||
m_system->UpdatePerformanceCounters();
|
||||
System::UpdatePerformanceCounters();
|
||||
|
||||
if (m_speed_limiter_enabled)
|
||||
m_system->Throttle();
|
||||
System::Throttle();
|
||||
|
||||
m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents);
|
||||
PollAndUpdate();
|
||||
|
@ -1095,14 +1094,14 @@ void QtHostInterface::threadEntryPoint()
|
|||
|
||||
void QtHostInterface::renderDisplay()
|
||||
{
|
||||
m_system->GetGPU()->ResetGraphicsAPIState();
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
|
||||
DrawImGuiWindows();
|
||||
|
||||
m_display->Render();
|
||||
ImGui::NewFrame();
|
||||
|
||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
}
|
||||
|
||||
void QtHostInterface::wakeThread()
|
||||
|
|
|
@ -108,7 +108,7 @@ bool SDLHostInterface::CreateDisplay()
|
|||
}
|
||||
|
||||
std::unique_ptr<HostDisplay> display;
|
||||
switch (m_settings.gpu_renderer)
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
case GPURenderer::HardwareVulkan:
|
||||
display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
|
||||
|
@ -130,8 +130,8 @@ bool SDLHostInterface::CreateDisplay()
|
|||
}
|
||||
|
||||
Assert(display);
|
||||
if (!display->CreateRenderDevice(wi.value(), m_settings.gpu_adapter, m_settings.gpu_use_debug_device) ||
|
||||
!display->InitializeRenderDevice(GetShaderCacheBasePath(), m_settings.gpu_use_debug_device))
|
||||
if (!display->CreateRenderDevice(wi.value(), g_settings.gpu_adapter, g_settings.gpu_use_debug_device) ||
|
||||
!display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device))
|
||||
{
|
||||
ReportError("Failed to create/initialize display render device");
|
||||
return false;
|
||||
|
@ -209,7 +209,7 @@ bool SDLHostInterface::AcquireHostDisplay()
|
|||
// Handle renderer switch if required.
|
||||
const HostDisplay::RenderAPI render_api = m_display->GetRenderAPI();
|
||||
bool needs_switch = false;
|
||||
switch (m_settings.gpu_renderer)
|
||||
switch (g_settings.gpu_renderer)
|
||||
{
|
||||
#ifdef WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
|
@ -321,8 +321,8 @@ void SDLHostInterface::OnRunningGameChanged()
|
|||
{
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
|
||||
if (m_system && !m_system->GetRunningTitle().empty())
|
||||
SDL_SetWindowTitle(m_window, m_system->GetRunningTitle().c_str());
|
||||
if (!System::GetRunningTitle().empty())
|
||||
SDL_SetWindowTitle(m_window, System::GetRunningTitle().c_str());
|
||||
else
|
||||
SDL_SetWindowTitle(m_window, GetWindowTitle());
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ void SDLHostInterface::SaveAndUpdateSettings()
|
|||
{
|
||||
m_settings_copy.Save(*m_settings_interface.get());
|
||||
|
||||
Settings old_settings(std::move(m_settings));
|
||||
Settings old_settings(std::move(g_settings));
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CheckForSettingsChanges(old_settings);
|
||||
|
||||
|
@ -451,7 +451,7 @@ void SDLHostInterface::LoadSettings()
|
|||
// Settings need to be loaded prior to creating the window for OpenGL bits.
|
||||
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
m_settings_copy = m_settings;
|
||||
m_settings_copy = g_settings;
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReportError(const char* message)
|
||||
|
@ -604,7 +604,7 @@ void SDLHostInterface::DrawImGuiWindows()
|
|||
|
||||
CommonHostInterface::DrawImGuiWindows();
|
||||
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
DrawPoweredOffWindow();
|
||||
|
||||
if (m_settings_window_open)
|
||||
|
@ -621,7 +621,7 @@ void SDLHostInterface::DrawMainMenuBar()
|
|||
if (!ImGui::BeginMainMenuBar())
|
||||
return;
|
||||
|
||||
const bool system_enabled = static_cast<bool>(m_system);
|
||||
const bool system_enabled = static_cast<bool>(!System::IsShutdown());
|
||||
|
||||
if (ImGui::BeginMenu("System"))
|
||||
{
|
||||
|
@ -653,9 +653,9 @@ void SDLHostInterface::DrawMainMenuBar()
|
|||
ClearImGuiFocus();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Pause", nullptr, m_paused, system_enabled))
|
||||
if (ImGui::MenuItem("Pause", nullptr, System::IsPaused(), system_enabled))
|
||||
{
|
||||
RunLater([this]() { PauseSystem(!m_paused); });
|
||||
RunLater([this]() { PauseSystem(!System::IsPaused()); });
|
||||
ClearImGuiFocus();
|
||||
}
|
||||
|
||||
|
@ -669,7 +669,7 @@ void SDLHostInterface::DrawMainMenuBar()
|
|||
|
||||
if (ImGui::MenuItem("Remove Disc", nullptr, false, system_enabled))
|
||||
{
|
||||
RunLater([this]() { m_system->RemoveMedia(); });
|
||||
RunLater([this]() { System::RemoveMedia(); });
|
||||
ClearImGuiFocus();
|
||||
}
|
||||
|
||||
|
@ -752,21 +752,26 @@ void SDLHostInterface::DrawMainMenuBar()
|
|||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
|
||||
if (!m_paused)
|
||||
if (System::IsPaused())
|
||||
{
|
||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (50.0f * framebuffer_scale));
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Paused");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (420.0f * framebuffer_scale));
|
||||
ImGui::Text("Average: %.2fms", m_system->GetAverageFrameTime());
|
||||
ImGui::Text("Average: %.2fms", System::GetAverageFrameTime());
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (310.0f * framebuffer_scale));
|
||||
ImGui::Text("Worst: %.2fms", m_system->GetWorstFrameTime());
|
||||
ImGui::Text("Worst: %.2fms", System::GetWorstFrameTime());
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (210.0f * framebuffer_scale));
|
||||
|
||||
const float speed = m_system->GetEmulationSpeed();
|
||||
const float speed = System::GetEmulationSpeed();
|
||||
const u32 rounded_speed = static_cast<u32>(std::round(speed));
|
||||
if (speed < 90.0f)
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
||||
|
@ -776,15 +781,10 @@ void SDLHostInterface::DrawMainMenuBar()
|
|||
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (165.0f * framebuffer_scale));
|
||||
ImGui::Text("FPS: %.2f", m_system->GetFPS());
|
||||
ImGui::Text("FPS: %.2f", System::GetFPS());
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (80.0f * framebuffer_scale));
|
||||
ImGui::Text("VPS: %.2f", m_system->GetVPS());
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (50.0f * framebuffer_scale));
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Paused");
|
||||
ImGui::Text("VPS: %.2f", System::GetVPS());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -800,7 +800,7 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
|
||||
if (ImGui::BeginMenu("CPU Execution Mode"))
|
||||
{
|
||||
const CPUExecutionMode current = m_settings.cpu_execution_mode;
|
||||
const CPUExecutionMode current = g_settings.cpu_execution_mode;
|
||||
for (u32 i = 0; i < static_cast<u32>(CPUExecutionMode::Count); i++)
|
||||
{
|
||||
if (ImGui::MenuItem(Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i)), nullptr,
|
||||
|
@ -868,7 +868,7 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Dump Audio", nullptr, IsDumpingAudio(), HasSystem()))
|
||||
if (ImGui::MenuItem("Dump Audio", nullptr, IsDumpingAudio(), System::IsValid()))
|
||||
{
|
||||
if (!IsDumpingAudio())
|
||||
StartDumpingAudio();
|
||||
|
@ -885,7 +885,7 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
|
||||
void SDLHostInterface::DrawDebugMenu()
|
||||
{
|
||||
Settings::DebugSettings& debug_settings = m_settings.debugging;
|
||||
Settings::DebugSettings& debug_settings = g_settings.debugging;
|
||||
bool settings_changed = false;
|
||||
|
||||
if (ImGui::BeginMenu("Log Level"))
|
||||
|
@ -893,7 +893,7 @@ void SDLHostInterface::DrawDebugMenu()
|
|||
for (u32 i = LOGLEVEL_NONE; i < LOGLEVEL_COUNT; i++)
|
||||
{
|
||||
if (ImGui::MenuItem(Settings::GetLogLevelDisplayName(static_cast<LOGLEVEL>(i)), nullptr,
|
||||
m_settings.log_level == static_cast<LOGLEVEL>(i)))
|
||||
g_settings.log_level == static_cast<LOGLEVEL>(i)))
|
||||
{
|
||||
m_settings_copy.log_level = static_cast<LOGLEVEL>(i);
|
||||
settings_changed = true;
|
||||
|
@ -1455,7 +1455,7 @@ void SDLHostInterface::ClearImGuiFocus()
|
|||
|
||||
void SDLHostInterface::DoStartDisc()
|
||||
{
|
||||
Assert(!m_system);
|
||||
Assert(System::IsShutdown());
|
||||
|
||||
nfdchar_t* path = nullptr;
|
||||
if (!NFD_OpenDialog("bin,img,cue,chd,exe,psexe,psf", nullptr, &path) || !path || std::strlen(path) == 0)
|
||||
|
@ -1470,18 +1470,18 @@ void SDLHostInterface::DoStartDisc()
|
|||
|
||||
void SDLHostInterface::DoChangeDisc()
|
||||
{
|
||||
Assert(m_system);
|
||||
Assert(!System::IsShutdown());
|
||||
|
||||
nfdchar_t* path = nullptr;
|
||||
if (!NFD_OpenDialog("bin,img,cue,chd", nullptr, &path) || !path || std::strlen(path) == 0)
|
||||
return;
|
||||
|
||||
if (m_system->InsertMedia(path))
|
||||
if (System::InsertMedia(path))
|
||||
AddFormattedOSDMessage(2.0f, "Switched CD to '%s'", path);
|
||||
else
|
||||
AddOSDMessage("Failed to switch CD. The log may contain further information.");
|
||||
|
||||
m_system->ResetPerformanceCounters();
|
||||
System::ResetPerformanceCounters();
|
||||
}
|
||||
|
||||
void SDLHostInterface::Run()
|
||||
|
@ -1490,9 +1490,9 @@ void SDLHostInterface::Run()
|
|||
{
|
||||
PollAndUpdate();
|
||||
|
||||
if (m_system && !m_paused)
|
||||
if (System::IsRunning())
|
||||
{
|
||||
m_system->RunFrame();
|
||||
System::RunFrame();
|
||||
UpdateControllerRumble();
|
||||
if (m_frame_step_request)
|
||||
{
|
||||
|
@ -1505,28 +1505,28 @@ void SDLHostInterface::Run()
|
|||
{
|
||||
DrawImGuiWindows();
|
||||
|
||||
if (m_system)
|
||||
m_system->GetGPU()->ResetGraphicsAPIState();
|
||||
if (System::IsRunning())
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
|
||||
m_display->Render();
|
||||
ImGui_ImplSDL2_NewFrame(m_window);
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (m_system)
|
||||
if (System::IsRunning())
|
||||
{
|
||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||
m_system->UpdatePerformanceCounters();
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
System::UpdatePerformanceCounters();
|
||||
|
||||
if (m_speed_limiter_enabled)
|
||||
m_system->Throttle();
|
||||
System::Throttle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save state on exit so it can be resumed
|
||||
if (m_system)
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
if (m_settings.save_state_on_exit)
|
||||
if (g_settings.save_state_on_exit)
|
||||
SaveResumeSaveState();
|
||||
DestroySystem();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
class System;
|
||||
class AudioStream;
|
||||
|
||||
class INISettingsInterface;
|
||||
|
@ -61,8 +60,6 @@ protected:
|
|||
void UpdateInputMap() override;
|
||||
|
||||
private:
|
||||
bool HasSystem() const { return static_cast<bool>(m_system); }
|
||||
|
||||
bool CreateSDLWindow();
|
||||
void DestroySDLWindow();
|
||||
bool CreateDisplay();
|
||||
|
|
|
@ -61,9 +61,9 @@ bool CommonHostInterface::Initialize()
|
|||
Log_ErrorPrintf("Failed to set working directory to '%s'", m_user_directory.c_str());
|
||||
|
||||
LoadSettings();
|
||||
UpdateLogSettings(m_settings.log_level, m_settings.log_filter.empty() ? nullptr : m_settings.log_filter.c_str(),
|
||||
m_settings.log_to_console, m_settings.log_to_debug, m_settings.log_to_window,
|
||||
m_settings.log_to_file);
|
||||
UpdateLogSettings(g_settings.log_level, g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(),
|
||||
g_settings.log_to_console, g_settings.log_to_debug, g_settings.log_to_window,
|
||||
g_settings.log_to_file);
|
||||
|
||||
m_game_list = std::make_unique<GameList>();
|
||||
m_game_list->SetCacheFilename(GetUserDirectoryRelativePath("cache/gamelist.cache"));
|
||||
|
@ -102,7 +102,7 @@ void CommonHostInterface::Shutdown()
|
|||
ShutdownDiscordPresence();
|
||||
#endif
|
||||
|
||||
m_system.reset();
|
||||
System::Shutdown();
|
||||
m_audio_stream.reset();
|
||||
if (m_display)
|
||||
ReleaseHostDisplay();
|
||||
|
@ -155,13 +155,13 @@ bool CommonHostInterface::BootSystem(const SystemBootParameters& parameters)
|
|||
}
|
||||
|
||||
// enter fullscreen if requested in the parameters
|
||||
if (!m_settings.start_paused && ((parameters.override_fullscreen.has_value() && *parameters.override_fullscreen) ||
|
||||
(!parameters.override_fullscreen.has_value() && m_settings.start_fullscreen)))
|
||||
if (!g_settings.start_paused && ((parameters.override_fullscreen.has_value() && *parameters.override_fullscreen) ||
|
||||
(!parameters.override_fullscreen.has_value() && g_settings.start_fullscreen)))
|
||||
{
|
||||
SetFullscreen(true);
|
||||
}
|
||||
|
||||
if (m_settings.audio_dump_on_boot)
|
||||
if (g_settings.audio_dump_on_boot)
|
||||
StartDumpingAudio();
|
||||
|
||||
UpdateSpeedLimiterState();
|
||||
|
@ -170,30 +170,28 @@ bool CommonHostInterface::BootSystem(const SystemBootParameters& parameters)
|
|||
|
||||
void CommonHostInterface::PauseSystem(bool paused)
|
||||
{
|
||||
if (paused == m_paused || !m_system)
|
||||
if (paused == System::IsPaused() || System::IsShutdown())
|
||||
return;
|
||||
|
||||
m_paused = paused;
|
||||
m_audio_stream->PauseOutput(m_paused);
|
||||
System::SetState(paused ? System::State::Paused : System::State::Running);
|
||||
m_audio_stream->PauseOutput(paused);
|
||||
OnSystemPaused(paused);
|
||||
UpdateSpeedLimiterState();
|
||||
|
||||
if (!paused)
|
||||
m_system->ResetPerformanceCounters();
|
||||
System::ResetPerformanceCounters();
|
||||
}
|
||||
|
||||
void CommonHostInterface::DestroySystem()
|
||||
{
|
||||
SetTimerResolutionIncreased(false);
|
||||
|
||||
m_paused = false;
|
||||
|
||||
HostInterface::DestroySystem();
|
||||
}
|
||||
|
||||
void CommonHostInterface::PowerOffSystem()
|
||||
{
|
||||
if (m_settings.save_state_on_exit)
|
||||
if (g_settings.save_state_on_exit)
|
||||
SaveResumeSaveState();
|
||||
|
||||
HostInterface::PowerOffSystem();
|
||||
|
@ -469,20 +467,20 @@ std::unique_ptr<ControllerInterface> CommonHostInterface::CreateControllerInterf
|
|||
|
||||
bool CommonHostInterface::LoadState(bool global, s32 slot)
|
||||
{
|
||||
if (!global && (!m_system || m_system->GetRunningCode().empty()))
|
||||
if (!global && (System::IsShutdown() || System::GetRunningCode().empty()))
|
||||
{
|
||||
ReportFormattedError("Can't save per-game state without a running game code.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string save_path =
|
||||
global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(m_system->GetRunningCode().c_str(), slot);
|
||||
global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(System::GetRunningCode().c_str(), slot);
|
||||
return LoadState(save_path.c_str());
|
||||
}
|
||||
|
||||
bool CommonHostInterface::SaveState(bool global, s32 slot)
|
||||
{
|
||||
const std::string& code = m_system->GetRunningCode();
|
||||
const std::string& code = System::GetRunningCode();
|
||||
if (!global && code.empty())
|
||||
{
|
||||
ReportFormattedError("Can't save per-game state without a running game code.");
|
||||
|
@ -504,7 +502,7 @@ bool CommonHostInterface::ResumeSystemFromState(const char* filename, bool boot_
|
|||
if (!BootSystem(boot_params))
|
||||
return false;
|
||||
|
||||
const bool global = m_system->GetRunningCode().empty();
|
||||
const bool global = System::GetRunningCode().empty();
|
||||
if (global)
|
||||
{
|
||||
ReportFormattedError("Cannot resume system with undetectable game code from '%s'.", filename);
|
||||
|
@ -516,7 +514,7 @@ bool CommonHostInterface::ResumeSystemFromState(const char* filename, bool boot_
|
|||
}
|
||||
else
|
||||
{
|
||||
const std::string path = GetGameSaveStateFileName(m_system->GetRunningCode().c_str(), -1);
|
||||
const std::string path = GetGameSaveStateFileName(System::GetRunningCode().c_str(), -1);
|
||||
if (FileSystem::FileExists(path.c_str()))
|
||||
{
|
||||
if (!LoadState(path.c_str()) && !boot_on_failure)
|
||||
|
@ -527,8 +525,8 @@ bool CommonHostInterface::ResumeSystemFromState(const char* filename, bool boot_
|
|||
}
|
||||
else if (!boot_on_failure)
|
||||
{
|
||||
ReportFormattedError("Resume save state not found for '%s' ('%s').", m_system->GetRunningCode().c_str(),
|
||||
m_system->GetRunningTitle().c_str());
|
||||
ReportFormattedError("Resume save state not found for '%s' ('%s').", System::GetRunningCode().c_str(),
|
||||
System::GetRunningTitle().c_str());
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
@ -551,34 +549,36 @@ bool CommonHostInterface::ResumeSystemFromMostRecentState()
|
|||
|
||||
void CommonHostInterface::UpdateSpeedLimiterState()
|
||||
{
|
||||
if (!m_system || !m_audio_stream || !m_display)
|
||||
return;
|
||||
m_speed_limiter_enabled = g_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled;
|
||||
|
||||
m_speed_limiter_enabled = m_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled;
|
||||
|
||||
const bool is_non_standard_speed = (std::abs(m_settings.emulation_speed - 1.0f) > 0.05f);
|
||||
const bool is_non_standard_speed = (std::abs(g_settings.emulation_speed - 1.0f) > 0.05f);
|
||||
const bool audio_sync_enabled =
|
||||
m_paused || (m_speed_limiter_enabled && m_settings.audio_sync_enabled && !is_non_standard_speed);
|
||||
!System::IsRunning() || (m_speed_limiter_enabled && g_settings.audio_sync_enabled && !is_non_standard_speed);
|
||||
const bool video_sync_enabled =
|
||||
m_paused || (m_speed_limiter_enabled && m_settings.video_sync_enabled && !is_non_standard_speed);
|
||||
!System::IsRunning() || (m_speed_limiter_enabled && g_settings.video_sync_enabled && !is_non_standard_speed);
|
||||
Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "",
|
||||
(audio_sync_enabled && video_sync_enabled) ? " and video" : (video_sync_enabled ? "video" : ""));
|
||||
|
||||
m_audio_stream->SetSync(audio_sync_enabled);
|
||||
if (audio_sync_enabled)
|
||||
m_audio_stream->EmptyBuffers();
|
||||
if (m_audio_stream)
|
||||
{
|
||||
m_audio_stream->SetSync(audio_sync_enabled);
|
||||
if (audio_sync_enabled)
|
||||
m_audio_stream->EmptyBuffers();
|
||||
}
|
||||
|
||||
m_display->SetVSync(video_sync_enabled);
|
||||
if (m_display)
|
||||
m_display->SetVSync(video_sync_enabled);
|
||||
|
||||
if (m_settings.increase_timer_resolution)
|
||||
if (g_settings.increase_timer_resolution)
|
||||
SetTimerResolutionIncreased(m_speed_limiter_enabled);
|
||||
|
||||
m_system->ResetPerformanceCounters();
|
||||
if (System::IsValid())
|
||||
System::ResetPerformanceCounters();
|
||||
}
|
||||
|
||||
void CommonHostInterface::RecreateSystem()
|
||||
{
|
||||
const bool was_paused = m_paused;
|
||||
const bool was_paused = System::IsPaused();
|
||||
HostInterface::RecreateSystem();
|
||||
if (was_paused)
|
||||
PauseSystem(true);
|
||||
|
@ -588,12 +588,12 @@ void CommonHostInterface::UpdateLogSettings(LOGLEVEL level, const char* filter,
|
|||
bool log_to_window, bool log_to_file)
|
||||
{
|
||||
Log::SetFilterLevel(level);
|
||||
Log::SetConsoleOutputParams(m_settings.log_to_console, filter, level);
|
||||
Log::SetDebugOutputParams(m_settings.log_to_debug, filter, level);
|
||||
Log::SetConsoleOutputParams(g_settings.log_to_console, filter, level);
|
||||
Log::SetDebugOutputParams(g_settings.log_to_debug, filter, level);
|
||||
|
||||
if (log_to_file)
|
||||
{
|
||||
Log::SetFileOutputParams(m_settings.log_to_file, GetUserDirectoryRelativePath("duckstation.log").c_str(), true,
|
||||
Log::SetFileOutputParams(g_settings.log_to_file, GetUserDirectoryRelativePath("duckstation.log").c_str(), true,
|
||||
filter, level);
|
||||
}
|
||||
else
|
||||
|
@ -707,7 +707,7 @@ void CommonHostInterface::OnControllerTypeChanged(u32 slot)
|
|||
|
||||
void CommonHostInterface::DrawImGuiWindows()
|
||||
{
|
||||
if (m_system)
|
||||
if (System::IsValid())
|
||||
{
|
||||
DrawDebugWindows();
|
||||
DrawFPSWindow();
|
||||
|
@ -721,7 +721,7 @@ void CommonHostInterface::DrawImGuiWindows()
|
|||
|
||||
void CommonHostInterface::DrawFPSWindow()
|
||||
{
|
||||
if (!(m_settings.display_show_fps | m_settings.display_show_vps | m_settings.display_show_speed))
|
||||
if (!(g_settings.display_show_fps | g_settings.display_show_vps | g_settings.display_show_speed))
|
||||
return;
|
||||
|
||||
const ImVec2 window_size =
|
||||
|
@ -739,12 +739,12 @@ void CommonHostInterface::DrawFPSWindow()
|
|||
}
|
||||
|
||||
bool first = true;
|
||||
if (m_settings.display_show_fps)
|
||||
if (g_settings.display_show_fps)
|
||||
{
|
||||
ImGui::Text("%.2f", m_system->GetFPS());
|
||||
ImGui::Text("%.2f", System::GetFPS());
|
||||
first = false;
|
||||
}
|
||||
if (m_settings.display_show_vps)
|
||||
if (g_settings.display_show_vps)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
|
@ -757,9 +757,9 @@ void CommonHostInterface::DrawFPSWindow()
|
|||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
ImGui::Text("%.2f", m_system->GetVPS());
|
||||
ImGui::Text("%.2f", System::GetVPS());
|
||||
}
|
||||
if (m_settings.display_show_speed)
|
||||
if (g_settings.display_show_speed)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
|
@ -772,7 +772,7 @@ void CommonHostInterface::DrawFPSWindow()
|
|||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
const float speed = m_system->GetEmulationSpeed();
|
||||
const float speed = System::GetEmulationSpeed();
|
||||
const u32 rounded_speed = static_cast<u32>(std::round(speed));
|
||||
if (speed < 90.0f)
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
||||
|
@ -823,7 +823,7 @@ void CommonHostInterface::DrawOSDMessages()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!m_settings.display_show_osd_messages)
|
||||
if (!g_settings.display_show_osd_messages)
|
||||
continue;
|
||||
|
||||
const float opacity = std::min(time_remaining, 1.0f);
|
||||
|
@ -848,28 +848,25 @@ void CommonHostInterface::DrawOSDMessages()
|
|||
|
||||
void CommonHostInterface::DrawDebugWindows()
|
||||
{
|
||||
const Settings::DebugSettings& debug_settings = m_system->GetSettings().debugging;
|
||||
|
||||
if (debug_settings.show_gpu_state)
|
||||
m_system->GetGPU()->DrawDebugStateWindow();
|
||||
if (debug_settings.show_cdrom_state)
|
||||
m_system->GetCDROM()->DrawDebugWindow();
|
||||
if (debug_settings.show_timers_state)
|
||||
m_system->GetTimers()->DrawDebugStateWindow();
|
||||
if (debug_settings.show_spu_state)
|
||||
m_system->GetSPU()->DrawDebugStateWindow();
|
||||
if (debug_settings.show_mdec_state)
|
||||
m_system->GetMDEC()->DrawDebugStateWindow();
|
||||
if (g_settings.debugging.show_gpu_state)
|
||||
g_gpu->DrawDebugStateWindow();
|
||||
if (g_settings.debugging.show_cdrom_state)
|
||||
g_cdrom.DrawDebugWindow();
|
||||
if (g_settings.debugging.show_timers_state)
|
||||
g_timers.DrawDebugStateWindow();
|
||||
if (g_settings.debugging.show_spu_state)
|
||||
g_spu.DrawDebugStateWindow();
|
||||
if (g_settings.debugging.show_mdec_state)
|
||||
g_mdec.DrawDebugStateWindow();
|
||||
}
|
||||
|
||||
void CommonHostInterface::DoFrameStep()
|
||||
{
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return;
|
||||
|
||||
m_frame_step_request = true;
|
||||
if (m_paused)
|
||||
PauseSystem(false);
|
||||
PauseSystem(false);
|
||||
}
|
||||
|
||||
std::optional<CommonHostInterface::HostKeyCode>
|
||||
|
@ -926,11 +923,9 @@ void CommonHostInterface::AddControllerRumble(u32 controller_index, u32 num_moto
|
|||
|
||||
void CommonHostInterface::UpdateControllerRumble()
|
||||
{
|
||||
DebugAssert(m_system);
|
||||
|
||||
for (ControllerRumbleState& rumble : m_controller_vibration_motors)
|
||||
{
|
||||
Controller* controller = m_system->GetController(rumble.controller_index);
|
||||
Controller* controller = System::GetController(rumble.controller_index);
|
||||
if (!controller)
|
||||
continue;
|
||||
|
||||
|
@ -984,7 +979,7 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
|
|||
|
||||
for (u32 controller_index = 0; controller_index < 2; controller_index++)
|
||||
{
|
||||
const ControllerType ctype = m_settings.controller_types[controller_index];
|
||||
const ControllerType ctype = g_settings.controller_types[controller_index];
|
||||
if (ctype == ControllerType::None)
|
||||
continue;
|
||||
|
||||
|
@ -1004,10 +999,10 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
|
|||
continue;
|
||||
|
||||
AddButtonToInputMap(binding, device, button, [this, controller_index, button_code](bool pressed) {
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return;
|
||||
|
||||
Controller* controller = m_system->GetController(controller_index);
|
||||
Controller* controller = System::GetController(controller_index);
|
||||
if (controller)
|
||||
controller->SetButtonState(button_code, pressed);
|
||||
});
|
||||
|
@ -1029,10 +1024,10 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
|
|||
continue;
|
||||
|
||||
AddAxisToInputMap(binding, device, axis, [this, controller_index, axis_code](float value) {
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return;
|
||||
|
||||
Controller* controller = m_system->GetController(controller_index);
|
||||
Controller* controller = System::GetController(controller_index);
|
||||
if (controller)
|
||||
controller->SetAxisState(axis_code, value);
|
||||
});
|
||||
|
@ -1253,23 +1248,23 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
|||
|
||||
RegisterHotkey(StaticString("General"), StaticString("TogglePause"), StaticString("Toggle Pause"),
|
||||
[this](bool pressed) {
|
||||
if (!pressed)
|
||||
PauseSystem(!m_paused);
|
||||
if (System::IsValid() && !pressed)
|
||||
PauseSystem(!System::IsPaused());
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("General"), StaticString("PowerOff"), StaticString("Power Off System"),
|
||||
[this](bool pressed) {
|
||||
if (!pressed && m_system)
|
||||
if (!pressed && System::IsValid())
|
||||
{
|
||||
if (m_settings.confim_power_off && !m_batch_mode)
|
||||
if (g_settings.confim_power_off && !m_batch_mode)
|
||||
{
|
||||
SmallString confirmation_message("Are you sure you want to stop emulation?");
|
||||
if (m_settings.save_state_on_exit)
|
||||
if (g_settings.save_state_on_exit)
|
||||
confirmation_message.AppendString("\n\nThe current state will be saved.");
|
||||
|
||||
if (!ConfirmMessage(confirmation_message))
|
||||
{
|
||||
m_system->ResetPerformanceCounters();
|
||||
System::ResetPerformanceCounters();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1280,7 +1275,7 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
|||
|
||||
RegisterHotkey(StaticString("General"), StaticString("Screenshot"), StaticString("Save Screenshot"),
|
||||
[this](bool pressed) {
|
||||
if (!pressed && m_system)
|
||||
if (!pressed && System::IsValid())
|
||||
SaveScreenshot();
|
||||
});
|
||||
|
||||
|
@ -1363,33 +1358,33 @@ void CommonHostInterface::RegisterSaveStateHotkeys()
|
|||
void CommonHostInterface::RegisterAudioHotkeys()
|
||||
{
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioMute"), StaticString("Toggle Mute"), [this](bool pressed) {
|
||||
if (m_system && !pressed)
|
||||
if (System::IsValid() && !pressed)
|
||||
{
|
||||
m_settings.audio_output_muted = !m_settings.audio_output_muted;
|
||||
m_audio_stream->SetOutputVolume(m_settings.audio_output_muted ? 0 : m_settings.audio_output_volume);
|
||||
if (m_settings.audio_output_muted)
|
||||
g_settings.audio_output_muted = !g_settings.audio_output_muted;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_muted ? 0 : g_settings.audio_output_volume);
|
||||
if (g_settings.audio_output_muted)
|
||||
AddOSDMessage("Volume: Muted", 2.0f);
|
||||
else
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", m_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeUp"), StaticString("Volume Up"), [this](bool pressed) {
|
||||
if (m_system && pressed)
|
||||
if (System::IsValid() && pressed)
|
||||
{
|
||||
m_settings.audio_output_volume = std::min<s32>(m_settings.audio_output_volume + 10, 100);
|
||||
m_settings.audio_output_muted = false;
|
||||
m_audio_stream->SetOutputVolume(m_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", m_settings.audio_output_volume);
|
||||
g_settings.audio_output_volume = std::min<s32>(g_settings.audio_output_volume + 10, 100);
|
||||
g_settings.audio_output_muted = false;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeDown"), StaticString("Volume Down"),
|
||||
[this](bool pressed) {
|
||||
if (m_system && pressed)
|
||||
if (System::IsValid() && pressed)
|
||||
{
|
||||
m_settings.audio_output_volume = std::max<s32>(m_settings.audio_output_volume - 10, 0);
|
||||
m_settings.audio_output_muted = false;
|
||||
m_audio_stream->SetOutputVolume(m_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", m_settings.audio_output_volume);
|
||||
g_settings.audio_output_volume = std::max<s32>(g_settings.audio_output_volume - 10, 0);
|
||||
g_settings.audio_output_muted = false;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1443,7 +1438,7 @@ void CommonHostInterface::ClearAllControllerBindings(SettingsInterface& si)
|
|||
{
|
||||
for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++)
|
||||
{
|
||||
const ControllerType ctype = m_settings.controller_types[controller_index - 1];
|
||||
const ControllerType ctype = g_settings.controller_types[controller_index - 1];
|
||||
if (ctype == ControllerType::None)
|
||||
continue;
|
||||
|
||||
|
@ -1483,7 +1478,7 @@ void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsIn
|
|||
return;
|
||||
}
|
||||
|
||||
m_settings.controller_types[controller_index - 1] = *ctype;
|
||||
g_settings.controller_types[controller_index - 1] = *ctype;
|
||||
HostInterface::OnControllerTypeChanged(controller_index - 1);
|
||||
|
||||
si.SetStringValue(section_name, "Type", Settings::GetControllerTypeName(*ctype));
|
||||
|
@ -1520,8 +1515,8 @@ void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsIn
|
|||
}
|
||||
}
|
||||
|
||||
if (m_system)
|
||||
m_system->UpdateControllers();
|
||||
if (System::IsValid())
|
||||
System::UpdateControllers();
|
||||
|
||||
UpdateInputMap(si);
|
||||
|
||||
|
@ -1540,7 +1535,7 @@ bool CommonHostInterface::SaveInputProfile(const char* profile_path, SettingsInt
|
|||
|
||||
for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++)
|
||||
{
|
||||
const ControllerType ctype = m_settings.controller_types[controller_index - 1];
|
||||
const ControllerType ctype = g_settings.controller_types[controller_index - 1];
|
||||
if (ctype == ControllerType::None)
|
||||
continue;
|
||||
|
||||
|
@ -1802,32 +1797,27 @@ void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
{
|
||||
HostInterface::CheckForSettingsChanges(old_settings);
|
||||
|
||||
if (m_system)
|
||||
if (System::IsValid())
|
||||
{
|
||||
if (m_settings.audio_backend != old_settings.audio_backend ||
|
||||
m_settings.audio_buffer_size != old_settings.audio_buffer_size)
|
||||
{
|
||||
m_audio_stream->PauseOutput(m_paused);
|
||||
UpdateSpeedLimiterState();
|
||||
}
|
||||
|
||||
if (m_settings.video_sync_enabled != old_settings.video_sync_enabled ||
|
||||
m_settings.audio_sync_enabled != old_settings.audio_sync_enabled ||
|
||||
m_settings.speed_limiter_enabled != old_settings.speed_limiter_enabled ||
|
||||
m_settings.increase_timer_resolution != old_settings.increase_timer_resolution ||
|
||||
m_settings.emulation_speed != old_settings.emulation_speed)
|
||||
if (g_settings.audio_backend != old_settings.audio_backend ||
|
||||
g_settings.audio_buffer_size != old_settings.audio_buffer_size ||
|
||||
g_settings.video_sync_enabled != old_settings.video_sync_enabled ||
|
||||
g_settings.audio_sync_enabled != old_settings.audio_sync_enabled ||
|
||||
g_settings.speed_limiter_enabled != old_settings.speed_limiter_enabled ||
|
||||
g_settings.increase_timer_resolution != old_settings.increase_timer_resolution ||
|
||||
g_settings.emulation_speed != old_settings.emulation_speed)
|
||||
{
|
||||
UpdateSpeedLimiterState();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_settings.log_level != old_settings.log_level || m_settings.log_filter != old_settings.log_filter ||
|
||||
m_settings.log_to_console != old_settings.log_to_console ||
|
||||
m_settings.log_to_window != old_settings.log_to_window || m_settings.log_to_file != old_settings.log_to_file)
|
||||
if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||
|
||||
g_settings.log_to_console != old_settings.log_to_console ||
|
||||
g_settings.log_to_window != old_settings.log_to_window || g_settings.log_to_file != old_settings.log_to_file)
|
||||
{
|
||||
UpdateLogSettings(m_settings.log_level, m_settings.log_filter.empty() ? nullptr : m_settings.log_filter.c_str(),
|
||||
m_settings.log_to_console, m_settings.log_to_debug, m_settings.log_to_window,
|
||||
m_settings.log_to_file);
|
||||
UpdateLogSettings(g_settings.log_level, g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(),
|
||||
g_settings.log_to_console, g_settings.log_to_debug, g_settings.log_to_window,
|
||||
g_settings.log_to_file);
|
||||
}
|
||||
|
||||
UpdateInputMap();
|
||||
|
@ -1912,27 +1902,27 @@ void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::str
|
|||
|
||||
bool CommonHostInterface::SaveResumeSaveState()
|
||||
{
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return false;
|
||||
|
||||
const bool global = m_system->GetRunningCode().empty();
|
||||
const bool global = System::GetRunningCode().empty();
|
||||
return SaveState(global, -1);
|
||||
}
|
||||
|
||||
bool CommonHostInterface::IsDumpingAudio() const
|
||||
{
|
||||
return m_system ? m_system->GetSPU()->IsDumpingAudio() : false;
|
||||
return g_spu.IsDumpingAudio();
|
||||
}
|
||||
|
||||
bool CommonHostInterface::StartDumpingAudio(const char* filename)
|
||||
{
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return false;
|
||||
|
||||
std::string auto_filename;
|
||||
if (!filename)
|
||||
{
|
||||
const auto& code = m_system->GetRunningCode();
|
||||
const auto& code = System::GetRunningCode();
|
||||
if (code.empty())
|
||||
{
|
||||
auto_filename = GetUserDirectoryRelativePath("dump/audio/%s.wav", GetTimestampStringForFileName().GetCharArray());
|
||||
|
@ -1946,7 +1936,7 @@ bool CommonHostInterface::StartDumpingAudio(const char* filename)
|
|||
filename = auto_filename.c_str();
|
||||
}
|
||||
|
||||
if (m_system->GetSPU()->StartDumpingAudio(filename))
|
||||
if (g_spu.StartDumpingAudio(filename))
|
||||
{
|
||||
AddFormattedOSDMessage(5.0f, "Started dumping audio to '%s'.", filename);
|
||||
return true;
|
||||
|
@ -1960,7 +1950,7 @@ bool CommonHostInterface::StartDumpingAudio(const char* filename)
|
|||
|
||||
void CommonHostInterface::StopDumpingAudio()
|
||||
{
|
||||
if (!m_system || !m_system->GetSPU()->StopDumpingAudio())
|
||||
if (System::IsShutdown() || !g_spu.StopDumpingAudio())
|
||||
return;
|
||||
|
||||
AddOSDMessage("Stopped dumping audio.", 5.0f);
|
||||
|
@ -1969,13 +1959,13 @@ void CommonHostInterface::StopDumpingAudio()
|
|||
bool CommonHostInterface::SaveScreenshot(const char* filename /* = nullptr */, bool full_resolution /* = true */,
|
||||
bool apply_aspect_ratio /* = true */)
|
||||
{
|
||||
if (!m_system)
|
||||
if (System::IsShutdown())
|
||||
return false;
|
||||
|
||||
std::string auto_filename;
|
||||
if (!filename)
|
||||
{
|
||||
const auto& code = m_system->GetRunningCode();
|
||||
const auto& code = System::GetRunningCode();
|
||||
const char* extension = "png";
|
||||
if (code.empty())
|
||||
{
|
||||
|
@ -1991,9 +1981,9 @@ bool CommonHostInterface::SaveScreenshot(const char* filename /* = nullptr */, b
|
|||
filename = auto_filename.c_str();
|
||||
}
|
||||
|
||||
m_system->GetGPU()->ResetGraphicsAPIState();
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
const bool screenshot_saved = m_display->WriteDisplayTextureToFile(filename, full_resolution, apply_aspect_ratio);
|
||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||
g_gpu->RestoreGraphicsAPIState();
|
||||
if (!screenshot_saved)
|
||||
{
|
||||
AddFormattedOSDMessage(10.0f, "Failed to save screenshot to '%s'", filename);
|
||||
|
@ -2052,10 +2042,10 @@ void CommonHostInterface::UpdateDiscordPresence()
|
|||
rp.startTimestamp = std::time(nullptr);
|
||||
|
||||
SmallString details_string;
|
||||
if (m_system)
|
||||
if (System::IsValid())
|
||||
{
|
||||
details_string.AppendFormattedString("%s (%s)", m_system->GetRunningTitle().c_str(),
|
||||
m_system->GetRunningCode().c_str());
|
||||
details_string.AppendFormattedString("%s (%s)", System::GetRunningTitle().c_str(),
|
||||
System::GetRunningCode().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -280,7 +280,6 @@ protected:
|
|||
std::deque<OSDMessage> m_osd_messages;
|
||||
std::mutex m_osd_messages_lock;
|
||||
|
||||
bool m_paused = false;
|
||||
bool m_frame_step_request = false;
|
||||
bool m_speed_limiter_temp_disabled = false;
|
||||
bool m_speed_limiter_enabled = false;
|
||||
|
|
|
@ -21,17 +21,6 @@ void ControllerInterface::Shutdown()
|
|||
m_host_interface = nullptr;
|
||||
}
|
||||
|
||||
System* ControllerInterface::GetSystem() const
|
||||
{
|
||||
return m_host_interface->GetSystem();
|
||||
}
|
||||
|
||||
Controller* ControllerInterface::GetController(u32 slot) const
|
||||
{
|
||||
System* system = GetSystem();
|
||||
return system ? system->GetController(slot) : nullptr;
|
||||
}
|
||||
|
||||
void ControllerInterface::SetHook(Hook::Callback callback)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <mutex>
|
||||
|
||||
class HostInterface;
|
||||
class System;
|
||||
class Controller;
|
||||
|
||||
class ControllerInterface
|
||||
|
@ -75,8 +74,6 @@ public:
|
|||
void ClearHook();
|
||||
|
||||
protected:
|
||||
System* GetSystem() const;
|
||||
Controller* GetController(u32 slot) const;
|
||||
bool DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value);
|
||||
|
||||
void OnControllerConnected(int host_id);
|
||||
|
|
|
@ -42,13 +42,12 @@ void SaveStateSelectorUI::RefreshList()
|
|||
{
|
||||
ClearList();
|
||||
|
||||
const System* system = m_host_interface->GetSystem();
|
||||
if (system && !system->GetRunningCode().empty())
|
||||
if (!System::GetRunningCode().empty())
|
||||
{
|
||||
for (s32 i = 1; i <= CommonHostInterface::GLOBAL_SAVE_STATE_SLOTS; i++)
|
||||
{
|
||||
std::optional<CommonHostInterface::ExtendedSaveStateInfo> ssi =
|
||||
m_host_interface->GetExtendedSaveStateInfo(system->GetRunningCode().c_str(), i);
|
||||
m_host_interface->GetExtendedSaveStateInfo(System::GetRunningCode().c_str(), i);
|
||||
|
||||
ListEntry li;
|
||||
if (ssi)
|
||||
|
|
Loading…
Reference in New Issue