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:
Connor McLaughlin 2020-07-31 17:09:18 +10:00 committed by GitHub
parent 1f9fc6ab74
commit b6f871d2b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
88 changed files with 4993 additions and 5045 deletions

View file

@ -9,9 +9,9 @@
#include "core/gpu.h" #include "core/gpu.h"
#include "core/host_display.h" #include "core/host_display.h"
#include "core/system.h" #include "core/system.h"
#include "frontend-common/imgui_styles.h"
#include "frontend-common/opengl_host_display.h" #include "frontend-common/opengl_host_display.h"
#include "frontend-common/vulkan_host_display.h" #include "frontend-common/vulkan_host_display.h"
#include "frontend-common/imgui_styles.h"
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
#include <cmath> #include <cmath>
#include <imgui.h> #include <imgui.h>
@ -242,27 +242,23 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf
m_callback_mutex.unlock(); m_callback_mutex.unlock();
// simulate the system if not paused // simulate the system if not paused
if (m_system && !m_paused) if (System::IsRunning())
m_system->RunFrame(); System::RunFrame();
// rendering // rendering
{ {
DrawImGuiWindows(); DrawImGuiWindows();
if (m_system) g_gpu->ResetGraphicsAPIState();
m_system->GetGPU()->ResetGraphicsAPIState();
m_display->Render(); m_display->Render();
ImGui::NewFrame(); ImGui::NewFrame();
if (m_system) g_gpu->RestoreGraphicsAPIState();
{ System::UpdatePerformanceCounters();
m_system->GetGPU()->RestoreGraphicsAPIState();
m_system->UpdatePerformanceCounters();
if (m_speed_limiter_enabled) if (m_speed_limiter_enabled)
m_system->Throttle(); System::Throttle();
}
} }
} }
@ -280,7 +276,7 @@ bool AndroidHostInterface::AcquireHostDisplay()
wi.surface_height = ANativeWindow_getHeight(m_surface); wi.surface_height = ANativeWindow_getHeight(m_surface);
std::unique_ptr<HostDisplay> display; std::unique_ptr<HostDisplay> display;
switch (m_settings.gpu_renderer) switch (g_settings.gpu_renderer)
{ {
case GPURenderer::HardwareVulkan: case GPURenderer::HardwareVulkan:
display = std::make_unique<FrontendCommon::VulkanHostDisplay>(); display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
@ -292,8 +288,8 @@ bool AndroidHostInterface::AcquireHostDisplay()
break; break;
} }
if (!display->CreateRenderDevice(wi, {}, m_settings.gpu_use_debug_device) || if (!display->CreateRenderDevice(wi, {}, g_settings.gpu_use_debug_device) ||
!display->InitializeRenderDevice(GetShaderCacheBasePath(), m_settings.gpu_use_debug_device)) !display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device))
{ {
ReportError("Failed to acquire host display."); ReportError("Failed to acquire host display.");
return false; return false;
@ -363,15 +359,15 @@ void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_na
if (!IsEmulationThreadRunning()) if (!IsEmulationThreadRunning())
{ {
m_settings.controller_types[index] = type; g_settings.controller_types[index] = type;
return; return;
} }
RunOnEmulationThread( RunOnEmulationThread(
[this, index, type]() { [this, index, type]() {
Log_InfoPrintf("Changing controller slot %d to %s", index, Settings::GetControllerTypeName(type)); Log_InfoPrintf("Changing controller slot %d to %s", index, Settings::GetControllerTypeName(type));
m_settings.controller_types[index] = type; g_settings.controller_types[index] = type;
m_system->UpdateControllers(); System::UpdateControllers();
}, },
false); false);
} }
@ -383,7 +379,7 @@ void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code,
RunOnEmulationThread( RunOnEmulationThread(
[this, index, button_code, pressed]() { [this, index, button_code, pressed]() {
Controller* controller = m_system->GetController(index); Controller* controller = System::GetController(index);
if (!controller) if (!controller)
return; return;
@ -399,7 +395,7 @@ void AndroidHostInterface::SetControllerAxisState(u32 index, s32 button_code, fl
RunOnEmulationThread( RunOnEmulationThread(
[this, index, button_code, value]() { [this, index, button_code, value]() {
Controller* controller = m_system->GetController(index); Controller* controller = System::GetController(index);
if (!controller) if (!controller)
return; return;
@ -416,7 +412,7 @@ void AndroidHostInterface::RefreshGameList(bool invalidate_cache, bool invalidat
void AndroidHostInterface::ApplySettings() void AndroidHostInterface::ApplySettings()
{ {
Settings old_settings = std::move(m_settings); Settings old_settings = std::move(g_settings);
CommonHostInterface::LoadSettings(m_settings_interface); CommonHostInterface::LoadSettings(m_settings_interface);
CheckForSettingsChanges(old_settings); CheckForSettingsChanges(old_settings);
} }
@ -574,12 +570,14 @@ DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerAxisCode, jobject
return code.value_or(-1); 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); 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"}}; static std::array<const char*, 4> names = {{"NTSC_J", "NTSC_U", "PAL", "Other"}};
return names[static_cast<int>(region)]; 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); AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
if (hi->IsEmulationThreadRunning()) if (hi->IsEmulationThreadRunning())
{ {
hi->RunOnEmulationThread([hi]() { hi->RunOnEmulationThread([hi]() { hi->ApplySettings(); });
hi->ApplySettings();
});
} }
else 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) DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_resetSystem, jobject obj, jboolean global, jint slot)
{ {
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj); AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
hi->RunOnEmulationThread([hi]() { hi->RunOnEmulationThread([hi]() { hi->ResetSystem(); });
hi->ResetSystem();
});
} }
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_loadState, jobject obj, jboolean global, jint slot) DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_loadState, jobject obj, jboolean global, jint slot)
{ {
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj); AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
hi->RunOnEmulationThread([hi, global, slot]() { hi->RunOnEmulationThread([hi, global, slot]() { hi->LoadState(global, slot); });
hi->LoadState(global, slot);
});
} }
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_saveState, jobject obj, jboolean global, jint slot) DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_saveState, jobject obj, jboolean global, jint slot)
{ {
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj); AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
hi->RunOnEmulationThread([hi, global, slot]() { hi->RunOnEmulationThread([hi, global, slot]() { hi->SaveState(global, slot); });
hi->SaveState(global, slot);
});
} }

View file

@ -10,8 +10,29 @@
#include <sys/mman.h> #include <sys/mman.h>
#endif #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; m_total_size = size + far_code_size;
#if defined(WIN32) #if defined(WIN32)
@ -22,6 +43,10 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz
#else #else
m_code_ptr = nullptr; m_code_ptr = nullptr;
#endif #endif
if (!m_code_ptr)
return false;
m_free_code_ptr = m_code_ptr; m_free_code_ptr = m_code_ptr;
m_code_size = size; m_code_size = size;
m_code_used = 0; m_code_used = 0;
@ -31,11 +56,75 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz
m_far_code_size = far_code_size; m_far_code_size = far_code_size;
m_far_code_used = 0; m_far_code_used = 0;
if (!m_code_ptr) m_old_protection = 0;
Panic("Failed to allocate code space."); 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)
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__)
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) #if defined(WIN32)
VirtualFree(m_code_ptr, 0, MEM_RELEASE); VirtualFree(m_code_ptr, 0, MEM_RELEASE);
@ -43,6 +132,16 @@ JitCodeBuffer::~JitCodeBuffer()
munmap(m_code_ptr, m_total_size); munmap(m_code_ptr, m_total_size);
#endif #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) void JitCodeBuffer::CommitCode(u32 length)
{ {
@ -76,18 +175,18 @@ void JitCodeBuffer::CommitFarCode(u32 length)
void JitCodeBuffer::Reset() void JitCodeBuffer::Reset()
{ {
std::memset(m_code_ptr, 0, m_code_size); m_free_code_ptr = m_code_ptr + m_guard_size;
FlushInstructionCache(m_code_ptr, m_code_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) 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_code_ptr = m_code_ptr;
m_code_used = 0;
m_free_far_code_ptr = m_far_code_ptr; m_free_far_code_ptr = m_far_code_ptr;
m_far_code_used = 0; 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);
}
} }
void JitCodeBuffer::Align(u32 alignment, u8 padding_value) void JitCodeBuffer::Align(u32 alignment, u8 padding_value)

View file

@ -4,9 +4,14 @@
class JitCodeBuffer class JitCodeBuffer
{ {
public: 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(); ~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(); void Reset();
u8* GetFreeCodePointer() const { return m_free_code_ptr; } u8* GetFreeCodePointer() const { return m_free_code_ptr; }
@ -25,16 +30,19 @@ public:
static void FlushInstructionCache(void* address, u32 size); static void FlushInstructionCache(void* address, u32 size);
private: private:
u8* m_code_ptr; u8* m_code_ptr = nullptr;
u8* m_free_code_ptr; u8* m_free_code_ptr = nullptr;
u32 m_code_size; u32 m_code_size = 0;
u32 m_code_used; u32 m_code_used = 0;
u8* m_far_code_ptr; u8* m_far_code_ptr = nullptr;
u8* m_free_far_code_ptr; u8* m_free_far_code_ptr = nullptr;
u32 m_far_code_size; u32 m_far_code_size = 0;
u32 m_far_code_used; 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;
}; };

View file

@ -5,7 +5,6 @@ add_library(core
bios.h bios.h
bus.cpp bus.cpp
bus.h bus.h
bus.inl
cdrom.cpp cdrom.cpp
cdrom.h cdrom.h
cdrom_async_reader.cpp cdrom_async_reader.cpp
@ -16,7 +15,6 @@ add_library(core
cpu_code_cache.h cpu_code_cache.h
cpu_core.cpp cpu_core.cpp
cpu_core.h cpu_core.h
cpu_core.inl
cpu_disasm.cpp cpu_disasm.cpp
cpu_disasm.h cpu_disasm.h
cpu_types.cpp cpu_types.cpp
@ -42,7 +40,6 @@ add_library(core
gpu_sw.h gpu_sw.h
gte.cpp gte.cpp
gte.h gte.h
gte.inl
gte_types.h gte_types.h
host_display.cpp host_display.cpp
host_display.h host_display.h
@ -90,7 +87,6 @@ set(RECOMPILER_SRCS
cpu_recompiler_code_generator_generic.cpp cpu_recompiler_code_generator_generic.cpp
cpu_recompiler_register_cache.cpp cpu_recompiler_register_cache.cpp
cpu_recompiler_register_cache.h cpu_recompiler_register_cache.h
cpu_recompiler_thunks.cpp
cpu_recompiler_thunks.h cpu_recompiler_thunks.h
cpu_recompiler_types.h cpu_recompiler_types.h
) )

View file

@ -6,9 +6,10 @@
#include "system.h" #include "system.h"
Log_SetChannel(AnalogController); 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); m_axis_state.fill(0x80);
Reset();
} }
AnalogController::~AnalogController() = default; AnalogController::~AnalogController() = default;
@ -52,7 +53,7 @@ bool AnalogController::DoState(StateWrapper& sw)
if (old_analog_mode != m_analog_mode) if (old_analog_mode != m_analog_mode)
{ {
m_system->GetHostInterface()->AddFormattedOSDMessage(2.0f, "Controller %u switched to %s mode.", m_index + 1u, g_host_interface->AddFormattedOSDMessage(2.0f, "Controller %u switched to %s mode.", m_index + 1u,
m_analog_mode ? "analog" : "digital"); m_analog_mode ? "analog" : "digital");
} }
} }
@ -94,8 +95,8 @@ void AnalogController::SetButtonState(Button button, bool pressed)
{ {
if (m_analog_locked) if (m_analog_locked)
{ {
m_system->GetHostInterface()->AddFormattedOSDMessage(2.0f, "Controller %u is locked to %s mode by the game.", g_host_interface->AddFormattedOSDMessage(2.0f, "Controller %u is locked to %s mode by the game.", m_index + 1u,
m_index + 1u, m_analog_mode ? "analog" : "digital"); m_analog_mode ? "analog" : "digital");
} }
else else
{ {
@ -154,7 +155,7 @@ void AnalogController::SetAnalogMode(bool enabled)
return; return;
Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital"); 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, g_host_interface->AddFormattedOSDMessage(2.0f, "Controller %u switched to %s mode.", m_index + 1u,
enabled ? "analog" : "digital"); enabled ? "analog" : "digital");
m_analog_mode = enabled; m_analog_mode = enabled;
} }
@ -397,9 +398,9 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
return ack; 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) std::optional<s32> AnalogController::StaticGetAxisCodeByName(std::string_view axis_name)
@ -492,8 +493,8 @@ Controller::SettingList AnalogController::StaticGetSettings()
return SettingList(settings.begin(), settings.end()); 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); Controller::LoadSettings(section);
m_auto_enable_analog = host_interface->GetBoolSettingValue(section, "AutoEnableAnalog", false); m_auto_enable_analog = g_host_interface->GetBoolSettingValue(section, "AutoEnableAnalog", false);
} }

View file

@ -41,10 +41,10 @@ public:
static constexpr u8 NUM_MOTORS = 2; static constexpr u8 NUM_MOTORS = 2;
AnalogController(System* system, u32 index); AnalogController(u32 index);
~AnalogController() override; ~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> StaticGetAxisCodeByName(std::string_view axis_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name); static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames(); static AxisList StaticGetAxisNames();
@ -71,7 +71,7 @@ public:
u32 GetVibrationMotorCount() const override; u32 GetVibrationMotorCount() const override;
float GetVibrationMotorStrength(u32 motor) override; float GetVibrationMotorStrength(u32 motor) override;
void LoadSettings(HostInterface* host_interface, const char* section) override; void LoadSettings(const char* section) override;
private: private:
using MotorState = std::array<u8, NUM_MOTORS>; using MotorState = std::array<u8, NUM_MOTORS>;
@ -132,7 +132,6 @@ private:
void SetAnalogMode(bool enabled); void SetAnalogMode(bool enabled);
void SetMotorState(u8 motor, u8 value); void SetMotorState(u8 motor, u8 value);
System* m_system;
u32 m_index; u32 m_index;
bool m_auto_enable_analog = false; bool m_auto_enable_analog = false;

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "common/bitfield.h" #include "common/bitfield.h"
#include "cpu_code_cache.h"
#include "types.h" #include "types.h"
#include <array> #include <array>
#include <bitset> #include <bitset>
@ -8,27 +9,8 @@
class StateWrapper; class StateWrapper;
namespace CPU { namespace Bus {
class Core;
class CodeCache;
} // namespace CPU
class DMA;
class InterruptController;
class GPU;
class CDROM;
class Pad;
class Timers;
class SPU;
class MDEC;
class SIO;
class System;
class Bus
{
friend DMA;
public:
enum : u32 enum : u32
{ {
RAM_BASE = 0x00000000, RAM_BASE = 0x00000000,
@ -49,7 +31,7 @@ public:
SIO_MASK = SIO_SIZE - 1, SIO_MASK = SIO_SIZE - 1,
MEMCTRL2_BASE = 0x1F801060, MEMCTRL2_BASE = 0x1F801060,
MEMCTRL2_SIZE = 0x10, MEMCTRL2_SIZE = 0x10,
MEMCTRL2_MASK = MEMCTRL_SIZE - 1, MEMCTRL2_MASK = MEMCTRL2_SIZE - 1,
INTERRUPT_CONTROLLER_BASE = 0x1F801070, INTERRUPT_CONTROLLER_BASE = 0x1F801070,
INTERRUPT_CONTROLLER_SIZE = 0x10, INTERRUPT_CONTROLLER_SIZE = 0x10,
INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1, INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1,
@ -75,39 +57,29 @@ public:
EXP2_SIZE = 0x2000, EXP2_SIZE = 0x2000,
EXP2_MASK = EXP2_SIZE - 1, EXP2_MASK = EXP2_SIZE - 1,
BIOS_BASE = 0x1FC00000, BIOS_BASE = 0x1FC00000,
BIOS_SIZE = 0x80000 BIOS_SIZE = 0x80000,
BIOS_MASK = 0x7FFFF,
}; };
Bus(); enum : u32
~Bus(); {
MEMCTRL_REG_COUNT = 9
};
void Initialize(CPU::Core* cpu, CPU::CodeCache* cpu_code_cache, DMA* dma, InterruptController* interrupt_controller, void Initialize();
GPU* gpu, CDROM* cdrom, Pad* pad, Timers* timers, SPU* spu, MDEC* mdec, SIO* sio); void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); 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 SetExpansionROM(std::vector<u8> data);
void SetBIOS(const std::vector<u8>& image); void SetBIOS(const std::vector<u8>& image);
// changing interfaces extern std::bitset<CPU_CODE_CACHE_PAGE_COUNT> m_ram_code_bits;
void SetGPU(GPU* gpu) { m_gpu = gpu; } 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). /// Returns the address which should be used for code caching (i.e. removes mirrors).
ALWAYS_INLINE static PhysicalMemoryAddress UnmirrorAddress(PhysicalMemoryAddress address) ALWAYS_INLINE PhysicalMemoryAddress UnmirrorAddress(PhysicalMemoryAddress address)
{ {
// RAM // RAM
if (address < 0x800000) if (address < 0x800000)
@ -117,13 +89,29 @@ public:
} }
/// Returns true if the address specified is cacheable (RAM or BIOS). /// Returns true if the address specified is cacheable (RAM or BIOS).
ALWAYS_INLINE static bool IsCacheableAddress(PhysicalMemoryAddress address) ALWAYS_INLINE bool IsCacheableAddress(PhysicalMemoryAddress address)
{ {
return (address < RAM_MIRROR_END) || (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE)); 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). /// Returns true if the address specified is writable (RAM).
ALWAYS_INLINE static bool IsRAMAddress(PhysicalMemoryAddress address) { return address < RAM_MIRROR_END; } ALWAYS_INLINE bool IsRAMAddress(PhysicalMemoryAddress address) { return address < RAM_MIRROR_END; }
/// Flags a RAM region as code, so we know when to invalidate blocks. /// 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; } ALWAYS_INLINE void SetRAMCodePage(u32 index) { m_ram_code_bits[index] = true; }
@ -134,115 +122,8 @@ public:
/// Clears all code bits for RAM regions. /// Clears all code bits for RAM regions.
ALWAYS_INLINE void ClearRAMCodePageFlags() { m_ram_code_bits.reset(); } 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. /// Returns the number of cycles stolen by DMA RAM access.
ALWAYS_INLINE static TickCount GetDMARAMTickCount(u32 word_count) 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 // 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 // 17 clks per 16 words, due to required row address loading, probably plus some further minimal overload due to
@ -259,37 +140,8 @@ private:
for (u32 page = start_page; page <= end_page; page++) for (u32 page = start_page; page <= end_page; page++)
{ {
if (m_ram_code_bits[page]) if (m_ram_code_bits[page])
DoInvalidateCodeCache(page); CPU::CodeCache::InvalidateBlocksWithPageIndex(page);
} }
} }
CPU::Core* m_cpu = nullptr; } // namespace Bus
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;
};
#include "bus.inl"

View file

@ -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);
}
}

View file

@ -275,23 +275,31 @@ static std::array<CommandInfo, 255> s_command_info = {{
0 // Unknown 0 // Unknown
}}; }};
CDROM g_cdrom;
CDROM::CDROM() = default; CDROM::CDROM() = default;
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_command_event =
m_system->CreateTimingEvent("CDROM Command Event", 1, 1, std::bind(&CDROM::ExecuteCommand, this), false); TimingEvents::CreateTimingEvent("CDROM Command Event", 1, 1, std::bind(&CDROM::ExecuteCommand, this), false);
m_drive_event = m_system->CreateTimingEvent("CDROM Drive Event", 1, 1, m_drive_event = TimingEvents::CreateTimingEvent("CDROM Drive Event", 1, 1,
std::bind(&CDROM::ExecuteDrive, this, std::placeholders::_2), false); 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(); m_reader.StartThread();
Reset();
}
void CDROM::Shutdown()
{
m_command_event.reset();
m_drive_event.reset();
m_reader.StopThread();
m_reader.RemoveMedia();
} }
void CDROM::Reset() 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 // set the region from the system area of the disc
m_disc_region = GameList::GetRegionForImage(media.get()); m_disc_region = GameList::GetRegionForImage(media.get());
Log_InfoPrintf("Inserting new media, disc region: %s, console region: %s", Settings::GetDiscRegionName(m_disc_region), 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 // motor automatically spins up
if (m_drive_state != DriveState::ShellOpening) if (m_drive_state != DriveState::ShellOpening)
@ -787,7 +795,7 @@ void CDROM::UpdateStatusRegister()
m_status.DRQSTS = !m_data_fifo.IsEmpty(); m_status.DRQSTS = !m_data_fifo.IsEmpty();
m_status.BUSYSTS = HasPendingCommand(); 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() void CDROM::UpdateInterruptRequest()
@ -795,7 +803,7 @@ void CDROM::UpdateInterruptRequest()
if ((m_interrupt_flag_register & m_interrupt_enable_register) == 0) if ((m_interrupt_flag_register & m_interrupt_enable_register) == 0)
return; return;
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::CDROM); g_interrupt_controller.InterruptRequest(InterruptController::IRQ::CDROM);
} }
TickCount CDROM::GetAckDelayForCommand(Command command) TickCount CDROM::GetAckDelayForCommand(Command command)
@ -1421,7 +1429,7 @@ void CDROM::ExecuteTestCommand(u8 subcommand)
{ {
Log_DebugPrintf("Get CDROM region ID string"); Log_DebugPrintf("Get CDROM region ID string");
switch (m_system->GetRegion()) switch (System::GetRegion())
{ {
case ConsoleRegion::NTSC_J: case ConsoleRegion::NTSC_J:
{ {
@ -1860,9 +1868,9 @@ void CDROM::DoIDRead()
m_current_lba = 0; m_current_lba = 0;
m_reader.QueueReadSector(0); m_reader.QueueReadSector(0);
if (m_system->GetSettings().cdrom_region_check && if (g_settings.cdrom_region_check &&
(m_disc_region == DiscRegion::Other || (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; stat_byte |= STAT_ID_ERROR;
flags_byte |= (1 << 7); // Unlicensed 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) if (m_muted || m_adpcm_muted)
return; return;
m_spu->GeneratePendingSamples(); g_spu.GeneratePendingSamples();
if (m_last_sector_subheader.codinginfo.IsStereo()) if (m_last_sector_subheader.codinginfo.IsStereo())
{ {
@ -2282,7 +2290,7 @@ void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ&
if (m_muted) if (m_muted)
return; return;
m_spu->GeneratePendingSamples(); g_spu.GeneratePendingSamples();
constexpr bool is_stereo = true; constexpr bool is_stereo = true;
constexpr u32 num_samples = CDImage::RAW_SECTOR_SIZE / sizeof(s16) / (is_stereo ? 2 : 1); 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; const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver); 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(); ImGui::End();
return; return;

View file

@ -12,20 +12,16 @@
#include <vector> #include <vector>
class StateWrapper; class StateWrapper;
class System;
class TimingEvent; class TimingEvent;
class DMA;
class InterruptController;
class SPU;
class CDROM class CDROM final
{ {
public: public:
CDROM(); CDROM();
~CDROM(); ~CDROM();
void Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, SPU* spu); void Initialize();
void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -276,10 +272,6 @@ private:
template<bool STEREO, bool SAMPLE_RATE> template<bool STEREO, bool SAMPLE_RATE>
void ResampleXAADPCM(const s16* frames_in, u32 num_frames_in); 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_command_event;
std::unique_ptr<TimingEvent> m_drive_event; std::unique_ptr<TimingEvent> m_drive_event;
@ -347,5 +339,7 @@ private:
CDROMAsyncReader m_reader; CDROMAsyncReader m_reader;
// two 16-bit samples packed in 32-bits // 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;

View file

@ -39,14 +39,14 @@ float Controller::GetVibrationMotorStrength(u32 motor)
return 0.0f; 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) bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale)
{ {
return false; 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) switch (type)
{ {
@ -54,13 +54,13 @@ std::unique_ptr<Controller> Controller::Create(System* system, ControllerType ty
return DigitalController::Create(); return DigitalController::Create();
case ControllerType::AnalogController: case ControllerType::AnalogController:
return AnalogController::Create(system, index); return AnalogController::Create(index);
case ControllerType::NamcoGunCon: case ControllerType::NamcoGunCon:
return NamcoGunCon::Create(system); return NamcoGunCon::Create();
case ControllerType::PlayStationMouse: case ControllerType::PlayStationMouse:
return PlayStationMouse::Create(system); return PlayStationMouse::Create();
case ControllerType::NeGcon: case ControllerType::NeGcon:
return NeGcon::Create(); return NeGcon::Create();

View file

@ -9,7 +9,6 @@
#include <vector> #include <vector>
class StateWrapper; class StateWrapper;
class System;
class HostInterface; class HostInterface;
class Controller class Controller
@ -53,13 +52,13 @@ public:
virtual float GetVibrationMotorStrength(u32 motor); virtual float GetVibrationMotorStrength(u32 motor);
/// Loads/refreshes any per-controller settings. /// 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. /// Returns the software cursor to use for this controller, if any.
virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale); virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale);
/// Creates a new controller of the specified type. /// 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. /// Gets the integer code for an axis in the specified controller type.
static std::optional<s32> GetAxisCodeByName(ControllerType type, std::string_view axis_name); static std::optional<s32> GetAxisCodeByName(ControllerType type, std::string_view axis_name);

View file

@ -57,7 +57,6 @@
<ClCompile Include="cpu_recompiler_code_generator_generic.cpp" /> <ClCompile Include="cpu_recompiler_code_generator_generic.cpp" />
<ClCompile Include="cpu_recompiler_code_generator_x64.cpp" /> <ClCompile Include="cpu_recompiler_code_generator_x64.cpp" />
<ClCompile Include="cpu_recompiler_register_cache.cpp" /> <ClCompile Include="cpu_recompiler_register_cache.cpp" />
<ClCompile Include="cpu_recompiler_thunks.cpp" />
<ClCompile Include="cpu_types.cpp" /> <ClCompile Include="cpu_types.cpp" />
<ClCompile Include="digital_controller.cpp" /> <ClCompile Include="digital_controller.cpp" />
<ClCompile Include="game_list.cpp" /> <ClCompile Include="game_list.cpp" />
@ -150,11 +149,6 @@
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project> <Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="cpu_core.inl" />
<None Include="bus.inl" />
<None Include="gte.inl" />
</ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{868B98C8-65A1-494B-8346-250A73A48C0A}</ProjectGuid> <ProjectGuid>{868B98C8-65A1-494B-8346-250A73A48C0A}</ProjectGuid>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>

View file

@ -27,7 +27,6 @@
<ClCompile Include="bios.cpp" /> <ClCompile Include="bios.cpp" />
<ClCompile Include="cpu_code_cache.cpp" /> <ClCompile Include="cpu_code_cache.cpp" />
<ClCompile Include="cpu_recompiler_register_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_x64.cpp" />
<ClCompile Include="cpu_recompiler_code_generator.cpp" /> <ClCompile Include="cpu_recompiler_code_generator.cpp" />
<ClCompile Include="cpu_recompiler_code_generator_generic.cpp" /> <ClCompile Include="cpu_recompiler_code_generator_generic.cpp" />
@ -64,7 +63,6 @@
<ClInclude Include="interrupt_controller.h" /> <ClInclude Include="interrupt_controller.h" />
<ClInclude Include="cdrom.h" /> <ClInclude Include="cdrom.h" />
<ClInclude Include="gte.h" /> <ClInclude Include="gte.h" />
<ClInclude Include="gte_types.h" />
<ClInclude Include="pad.h" /> <ClInclude Include="pad.h" />
<ClInclude Include="digital_controller.h" /> <ClInclude Include="digital_controller.h" />
<ClInclude Include="timers.h" /> <ClInclude Include="timers.h" />
@ -95,10 +93,6 @@
<ClInclude Include="gpu_hw_vulkan.h" /> <ClInclude Include="gpu_hw_vulkan.h" />
<ClInclude Include="resources.h" /> <ClInclude Include="resources.h" />
<ClInclude Include="host_interface_progress_callback.h" /> <ClInclude Include="host_interface_progress_callback.h" />
</ItemGroup> <ClInclude Include="gte_types.h" />
<ItemGroup>
<None Include="cpu_core.inl" />
<None Include="bus.inl" />
<None Include="gte.inl" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,64 +1,104 @@
#include "cpu_code_cache.h" #include "cpu_code_cache.h"
#include "bus.h"
#include "common/log.h" #include "common/log.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "cpu_disasm.h" #include "cpu_disasm.h"
#include "system.h" #include "system.h"
#include "timing_event.h"
Log_SetChannel(CPU::CodeCache); Log_SetChannel(CPU::CodeCache);
#ifdef WITH_RECOMPILER #ifdef WITH_RECOMPILER
#include "cpu_recompiler_code_generator.h" #include "cpu_recompiler_code_generator.h"
#include "cpu_recompiler_thunks.h"
#endif #endif
namespace CPU { namespace CPU::CodeCache {
constexpr bool USE_BLOCK_LINKING = true; constexpr bool USE_BLOCK_LINKING = true;
#ifdef WITH_RECOMPILER
static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 32 * 1024 * 1024; 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_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) Assert(s_blocks.empty());
Flush();
}
void CodeCache::Initialize(System* system, Core* core, Bus* bus, bool use_recompiler)
{
m_system = system;
m_core = core;
m_bus = bus;
#ifdef WITH_RECOMPILER #ifdef WITH_RECOMPILER
m_use_recompiler = use_recompiler; s_use_recompiler = use_recompiler;
m_code_buffer = std::make_unique<JitCodeBuffer>(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE); if (!s_code_buffer.Initialize(s_code_storage, sizeof(s_code_storage), RECOMPILER_FAR_CODE_CACHE_SIZE,
m_asm_functions = std::make_unique<Recompiler::ASMFunctions>(); RECOMPILER_GUARD_SIZE))
m_asm_functions->Generate(m_code_buffer.get()); {
Panic("Failed to initialize code space");
}
#else #else
m_use_recompiler = false; s_use_recompiler = false;
#endif #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()
{ {
if (m_core->HasPendingInterrupt()) CodeBlockKey next_block_key;
g_state.frame_done = false;
while (!g_state.frame_done)
{
TimingEvents::UpdateCPUDowncount();
next_block_key = GetNextBlockKey();
while (g_state.pending_ticks < g_state.downcount)
{
if (HasPendingInterrupt())
{ {
// TODO: Fill in m_next_instruction... // TODO: Fill in m_next_instruction...
m_core->SafeReadMemoryWord(m_core->m_regs.pc, &m_core->m_next_instruction.bits); SafeReadMemoryWord(g_state.regs.pc, &g_state.next_instruction.bits);
m_core->DispatchInterrupt(); DispatchInterrupt();
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
} }
CodeBlock* block = LookupBlock(next_block_key); CodeBlock* block = LookupBlock(next_block_key);
if (!block) if (!block)
{ {
Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", m_core->GetRegs().pc); Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", g_state.regs.pc);
InterpretUncachedBlock(); InterpretUncachedBlock();
continue; continue;
} }
@ -66,7 +106,7 @@ void CodeCache::Execute()
reexecute_block: reexecute_block:
#if 0 #if 0
const u32 tick = m_system->GetGlobalTickCounter() + m_core->GetPendingTicks(); const u32 tick = g_system->GetGlobalTickCounter() + m_core->GetPendingTicks();
if (tick == 61033207) if (tick == 61033207)
__debugbreak(); __debugbreak();
#endif #endif
@ -75,14 +115,14 @@ void CodeCache::Execute()
LogCurrentState(); LogCurrentState();
#endif #endif
if (m_use_recompiler) if (s_use_recompiler)
block->host_code(m_core); block->host_code();
else else
InterpretCachedBlock(*block); InterpretCachedBlock(*block);
if (m_core->m_pending_ticks >= m_core->m_downcount) if (g_state.pending_ticks >= g_state.downcount)
break; break;
else if (m_core->HasPendingInterrupt() || !USE_BLOCK_LINKING) else if (HasPendingInterrupt() || !USE_BLOCK_LINKING)
continue; continue;
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
@ -125,63 +165,65 @@ void CodeCache::Execute()
} }
} }
// in case we switch to interpreter... TimingEvents::RunEvents();
m_core->m_regs.npc = m_core->m_regs.pc;
} }
void CodeCache::SetUseRecompiler(bool enable) // in case we switch to interpreter...
g_state.regs.npc = g_state.regs.pc;
}
void SetUseRecompiler(bool enable)
{ {
#ifdef WITH_RECOMPILER #ifdef WITH_RECOMPILER
if (m_use_recompiler == enable) if (s_use_recompiler == enable)
return; return;
m_use_recompiler = enable; s_use_recompiler = enable;
Flush(); Flush();
#endif #endif
} }
void CodeCache::Flush() void Flush()
{ {
m_bus->ClearRAMCodePageFlags(); Bus::ClearRAMCodePageFlags();
for (auto& it : m_ram_block_map) for (auto& it : m_ram_block_map)
it.clear(); it.clear();
for (const auto& it : m_blocks) for (const auto& it : s_blocks)
delete it.second; delete it.second;
m_blocks.clear(); s_blocks.clear();
#ifdef WITH_RECOMPILER #ifdef WITH_RECOMPILER
m_code_buffer->Reset(); s_code_buffer.Reset();
#endif #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 " 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 " "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 " "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", "ldv=%08X\n",
m_system->GetGlobalTickCounter() + m_core->GetPendingTicks(), regs.pc, regs.zero, regs.at, TimingEvents::GetGlobalTickCounter() + GetPendingTicks(), regs.pc, regs.zero, regs.at, regs.v0,
regs.v0, regs.v1, regs.a0, regs.a1, regs.a2, regs.a3, regs.t0, regs.t1, regs.t2, regs.t3, regs.t4, regs.v1, regs.a0, regs.a1, regs.a2, regs.a3, regs.t0, regs.t1, regs.t2, regs.t3, regs.t4, regs.t5,
regs.t5, regs.t6, regs.t7, regs.s0, regs.s1, regs.s2, regs.s3, regs.s4, regs.s5, regs.s6, regs.s7, regs.t6, regs.t7, regs.s0, regs.s1, regs.s2, regs.s3, regs.s4, regs.s5, regs.s6, regs.s7, regs.t8,
regs.t8, regs.t9, regs.k0, regs.k1, regs.gp, regs.sp, regs.fp, regs.ra, regs.t9, regs.k0, regs.k1, regs.gp, regs.sp, regs.fp, regs.ra,
(m_core->m_next_load_delay_reg == Reg::count) ? "NONE" : (g_state.next_load_delay_reg == Reg::count) ? "NONE" : GetRegName(g_state.next_load_delay_reg),
GetRegName(m_core->m_next_load_delay_reg), (g_state.next_load_delay_reg == Reg::count) ? 0 : g_state.next_load_delay_value);
(m_core->m_next_load_delay_reg == Reg::count) ? 0 : m_core->m_next_load_delay_value);
} }
CodeBlockKey CodeCache::GetNextBlockKey() const CodeBlockKey GetNextBlockKey()
{ {
CodeBlockKey key = {}; CodeBlockKey key = {};
key.SetPC(m_core->GetRegs().pc); key.SetPC(g_state.regs.pc);
key.user_mode = m_core->InUserMode(); key.user_mode = InUserMode();
return key; return key;
} }
CodeBlock* CodeCache::LookupBlock(CodeBlockKey key) CodeBlock* LookupBlock(CodeBlockKey key)
{ {
BlockMap::iterator iter = m_blocks.find(key.bits); BlockMap::iterator iter = s_blocks.find(key.bits);
if (iter != m_blocks.end()) if (iter != s_blocks.end())
{ {
// ensure it hasn't been invalidated // ensure it hasn't been invalidated
CodeBlock* existing_block = iter->second; CodeBlock* existing_block = iter->second;
@ -202,17 +244,15 @@ CodeBlock* CodeCache::LookupBlock(CodeBlockKey key)
block = nullptr; block = nullptr;
} }
iter = m_blocks.emplace(key.bits, block).first; iter = s_blocks.emplace(key.bits, block).first;
return block; return block;
} }
bool CodeCache::RevalidateBlock(CodeBlock* block) bool RevalidateBlock(CodeBlock* block)
{ {
for (const CodeBlockInstruction& cbi : block->instructions) for (const CodeBlockInstruction& cbi : block->instructions)
{ {
u32 new_code = 0; u32 new_code = Bus::ReadCacheableAddress(cbi.pc & PHYSICAL_MEMORY_ADDRESS_MASK);
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(cbi.pc & PHYSICAL_MEMORY_ADDRESS_MASK,
new_code);
if (cbi.instruction.bits != new_code) if (cbi.instruction.bits != new_code)
{ {
Log_DebugPrintf("Block 0x%08X changed at PC 0x%08X - %08X to %08X - recompiling.", block->GetPC(), cbi.pc, 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; return true;
} }
bool CodeCache::CompileBlock(CodeBlock* block) bool CompileBlock(CodeBlock* block)
{ {
u32 pc = block->GetPC(); u32 pc = block->GetPC();
bool is_branch_delay_slot = false; bool is_branch_delay_slot = false;
@ -258,12 +298,12 @@ bool CodeCache::CompileBlock(CodeBlock* block)
CodeBlockInstruction cbi = {}; CodeBlockInstruction cbi = {};
const PhysicalMemoryAddress phys_addr = pc & PHYSICAL_MEMORY_ADDRESS_MASK; const PhysicalMemoryAddress phys_addr = pc & PHYSICAL_MEMORY_ADDRESS_MASK;
if (!m_bus->IsCacheableAddress(phys_addr) || if (!Bus::IsCacheableAddress(phys_addr))
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(phys_addr, cbi.instruction.bits) < 0 || break;
!IsInvalidInstruction(cbi.instruction))
{ cbi.instruction.bits = Bus::ReadCacheableAddress(phys_addr);
if (!IsInvalidInstruction(cbi.instruction))
break; break;
}
cbi.pc = pc; cbi.pc = pc;
cbi.is_branch_delay_slot = is_branch_delay_slot; 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_load_instruction = IsMemoryLoadInstruction(cbi.instruction);
cbi.is_store_instruction = IsMemoryStoreInstruction(cbi.instruction); cbi.is_store_instruction = IsMemoryStoreInstruction(cbi.instruction);
cbi.has_load_delay = InstructionHasLoadDelay(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 // instruction is decoded now
block->instructions.push_back(cbi); block->instructions.push_back(cbi);
@ -316,19 +356,19 @@ bool CodeCache::CompileBlock(CodeBlock* block)
} }
#ifdef WITH_RECOMPILER #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. // 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) || (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)) (block->instructions.size() * Recompiler::MAX_FAR_HOST_BYTES_PER_INSTRUCTION))
{ {
Log_WarningPrintf("Out of code space, flushing all blocks."); Log_WarningPrintf("Out of code space, flushing all blocks.");
Flush(); 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)) 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()); 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; return true;
} }
void CodeCache::InvalidateBlocksWithPageIndex(u32 page_index) void InvalidateBlocksWithPageIndex(u32 page_index)
{ {
DebugAssert(page_index < CPU_CODE_CACHE_PAGE_COUNT); DebugAssert(page_index < CPU_CODE_CACHE_PAGE_COUNT);
auto& blocks = m_ram_block_map[page_index]; 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. // Block will be re-added next execution.
blocks.clear(); 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()); BlockMap::iterator iter = s_blocks.find(block->key.GetPC());
Assert(iter != m_blocks.end() && iter->second == block); Assert(iter != s_blocks.end() && iter->second == block);
Log_DevPrintf("Flushing block at address 0x%08X", block->GetPC()); Log_DevPrintf("Flushing block at address 0x%08X", block->GetPC());
// if it's been invalidated it won't be in the page map // if it's been invalidated it won't be in the page map
if (block->invalidated) if (block->invalidated)
RemoveBlockFromPageMap(block); RemoveBlockFromPageMap(block);
m_blocks.erase(iter); UnlinkBlock(block);
s_blocks.erase(iter);
delete block; delete block;
} }
void CodeCache::AddBlockToPageMap(CodeBlock* block) void AddBlockToPageMap(CodeBlock* block)
{ {
if (!block->IsInRAM()) if (!block->IsInRAM())
return; return;
@ -380,11 +422,11 @@ void CodeCache::AddBlockToPageMap(CodeBlock* block)
for (u32 page = start_page; page <= end_page; page++) for (u32 page = start_page; page <= end_page; page++)
{ {
m_ram_block_map[page].push_back(block); 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()) if (!block->IsInRAM())
return; 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()); Log_DebugPrintf("Linking block %p(%08x) to %p(%08x)", from, from->GetPC(), to, to->GetPC());
from->link_successors.push_back(to); from->link_successors.push_back(to);
to->link_predecessors.push_back(from); to->link_predecessors.push_back(from);
} }
void CodeCache::UnlinkBlock(CodeBlock* block) void UnlinkBlock(CodeBlock* block)
{ {
for (CodeBlock* predecessor : block->link_predecessors) for (CodeBlock* predecessor : block->link_predecessors)
{ {
@ -426,82 +468,4 @@ void CodeCache::UnlinkBlock(CodeBlock* block)
block->link_successors.clear(); block->link_successors.clear();
} }
void CodeCache::InterpretCachedBlock(const CodeBlock& block) } // namespace CPU::CodeCache
{
// 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

View file

@ -1,22 +1,13 @@
#pragma once #pragma once
#include "common/bitfield.h" #include "common/bitfield.h"
#include "common/jit_code_buffer.h"
#include "cpu_types.h" #include "cpu_types.h"
#include <array> #include <array>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
class JitCodeBuffer;
class Bus;
class System;
namespace CPU { namespace CPU {
class Core;
namespace Recompiler {
class ASMFunctions;
}
union CodeBlockKey union CodeBlockKey
{ {
@ -58,7 +49,7 @@ struct CodeBlockInstruction
struct CodeBlock struct CodeBlock
{ {
using HostCodePointer = void (*)(Core*); using HostCodePointer = void (*)();
CodeBlock(const CodeBlockKey key_) : key(key_) {} CodeBlock(const CodeBlockKey key_) : key(key_) {}
@ -86,13 +77,10 @@ struct CodeBlock
} }
}; };
class CodeCache namespace CodeCache {
{
public:
CodeCache();
~CodeCache();
void Initialize(System* system, Core* core, Bus* bus, bool use_recompiler); void Initialize(bool use_recompiler);
void Shutdown();
void Execute(); void Execute();
/// Flushes the code cache, forcing all blocks to be recompiled. /// Flushes the code cache, forcing all blocks to be recompiled.
@ -104,49 +92,9 @@ public:
/// Invalidates all blocks which are in the range of the specified code page. /// Invalidates all blocks which are in the range of the specified code page.
void InvalidateBlocksWithPageIndex(u32 page_index); void InvalidateBlocksWithPageIndex(u32 page_index);
private:
using BlockMap = std::unordered_map<u32, CodeBlock*>;
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 InterpretCachedBlock(const CodeBlock& block);
void InterpretUncachedBlock(); void InterpretUncachedBlock();
System* m_system = nullptr; }; // namespace CodeCache
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 CPU } // namespace CPU

File diff suppressed because it is too large Load diff

View file

@ -1,64 +1,82 @@
#pragma once #pragma once
#include "common/bitfield.h" #include "common/bitfield.h"
#include "cpu_types.h" #include "cpu_types.h"
#include "gte.h" #include "gte_types.h"
#include "types.h" #include "types.h"
#include <array> #include <array>
#include <optional> #include <optional>
class StateWrapper; class StateWrapper;
class Bus;
class System;
namespace CPU { namespace CPU {
class CodeCache; enum : VirtualMemoryAddress
namespace Recompiler {
class CodeGenerator;
class Thunks;
} // namespace Recompiler
class Core
{ {
public: RESET_VECTOR = UINT32_C(0xBFC00000)
static constexpr VirtualMemoryAddress RESET_VECTOR = UINT32_C(0xBFC00000); };
static constexpr PhysicalMemoryAddress DCACHE_LOCATION = UINT32_C(0x1F800000); enum : PhysicalMemoryAddress
static constexpr PhysicalMemoryAddress DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00); {
static constexpr PhysicalMemoryAddress DCACHE_OFFSET_MASK = UINT32_C(0x000003FF); DCACHE_LOCATION = UINT32_C(0x1F800000),
static constexpr PhysicalMemoryAddress DCACHE_SIZE = UINT32_C(0x00000400); DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00),
DCACHE_OFFSET_MASK = UINT32_C(0x000003FF),
DCACHE_SIZE = UINT32_C(0x00000400)
};
friend CodeCache; struct State
friend Recompiler::CodeGenerator; {
friend Recompiler::Thunks; // ticks the CPU has executed
TickCount pending_ticks = 0;
TickCount downcount = MAX_SLICE_SIZE;
Core(); Registers regs = {};
~Core(); Cop0Registers cop0_regs = {};
Instruction next_instruction = {};
void Initialize(Bus* bus); // 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(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
/// Executes interpreter loop.
void Execute(); void Execute();
ALWAYS_INLINE Bus* GetBus() const { return m_bus; } ALWAYS_INLINE Registers& GetRegs() { return g_state.regs; }
ALWAYS_INLINE const Registers& GetRegs() const { return m_regs; } ALWAYS_INLINE TickCount GetPendingTicks() { return g_state.pending_ticks; }
ALWAYS_INLINE Registers& GetRegs() { return m_regs; } ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; }
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { g_state.pending_ticks += ticks; }
ALWAYS_INLINE TickCount GetPendingTicks() const { return m_pending_ticks; } // state helpers
ALWAYS_INLINE void ResetPendingTicks() { m_pending_ticks = 0; } ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; }
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { m_pending_ticks += ticks; } ALWAYS_INLINE bool InKernelMode() { return !g_state.cop0_regs.sr.KUc; }
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. // Memory reads variants which do not raise exceptions.
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value); bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
@ -71,116 +89,17 @@ public:
// External IRQs // External IRQs
void SetExternalInterrupt(u8 bit); void SetExternalInterrupt(u8 bit);
void ClearExternalInterrupt(u8 bit); void ClearExternalInterrupt(u8 bit);
bool HasPendingInterrupt();
private: void DispatchInterrupt();
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 DisassembleAndPrint(u32 addr);
void DisassembleAndLog(u32 addr); void DisassembleAndLog(u32 addr);
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after); void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
// Updates load delays - call after each instruction // Write to CPU execution log file.
ALWAYS_INLINE void UpdateLoadDelay() void WriteToExecutionLog(const char* format, ...);
{
// 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;
};
extern bool TRACE_EXECUTION; extern bool TRACE_EXECUTION;
extern bool LOG_EXECUTION; extern bool LOG_EXECUTION;
// Write to CPU execution log file.
void WriteToExecutionLog(const char* format, ...);
} // namespace CPU } // namespace CPU
#include "cpu_core.inl"

View file

@ -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

View file

@ -1,5 +1,6 @@
#include "cpu_disasm.h" #include "cpu_disasm.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "common/assert.h"
#include <array> #include <array>
namespace CPU { 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 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(); dest->Clear();
@ -185,10 +186,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
if (std::strncmp(str, "rs", 2) == 0) if (std::strncmp(str, "rs", 2) == 0)
{ {
dest->AppendString(GetRegName(inst.r.rs)); dest->AppendString(GetRegName(inst.r.rs));
if (state) if (regs)
{ {
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rs), 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; 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) else if (std::strncmp(str, "rt", 2) == 0)
{ {
dest->AppendString(GetRegName(inst.r.rt)); dest->AppendString(GetRegName(inst.r.rt));
if (state) if (regs)
{ {
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rt), 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; 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) else if (std::strncmp(str, "rd", 2) == 0)
{ {
dest->AppendString(GetRegName(inst.r.rd)); dest->AppendString(GetRegName(inst.r.rd));
if (state) if (regs)
{ {
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rd), 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; 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()); const s32 offset = static_cast<s32>(inst.i.imm_sext32());
dest->AppendFormattedString("%d(%s)", offset, GetRegName(inst.i.rs)); dest->AppendFormattedString("%d(%s)", offset, GetRegName(inst.i.rs));
if (state) if (regs)
{ {
comment.AppendFormattedString("%saddr=0x%08X", comment.IsEmpty() ? "" : ", ", 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; str += 8;
@ -291,14 +292,14 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
} }
template<typename T> 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) const std::pair<T, const char*>* table, size_t table_size, T table_key)
{ {
for (size_t i = 0; i < table_size; i++) for (size_t i = 0; i < table_size; i++)
{ {
if (table[i].first == table_key) if (table[i].first == table_key)
{ {
FormatInstruction(dest, inst, pc, state, table[i].second); FormatInstruction(dest, inst, pc, regs, table[i].second);
return; 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()); 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}; const Instruction inst{bits};
switch (inst.op) switch (inst.op)
{ {
case InstructionOp::funct: 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; return;
case InstructionOp::cop0: case InstructionOp::cop0:
@ -322,7 +323,7 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
{ {
if (inst.cop.IsCommonInstruction()) 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()); inst.cop.CommonOp());
} }
else else
@ -331,7 +332,7 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
{ {
case InstructionOp::cop0: 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; break;
@ -355,14 +356,14 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
const bool bgez = ConvertToBoolUnchecked(rt & u8(1)); const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1)); const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1));
if (link) 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 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; break;
default: 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; break;
} }
} }

View file

@ -3,7 +3,5 @@
#include "cpu_types.h" #include "cpu_types.h"
namespace CPU { namespace CPU {
class Core; void DisassembleInstruction(String* dest, u32 pc, u32 bits, Registers* regs = nullptr);
void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state = nullptr);
} // namespace CPU } // namespace CPU

View file

@ -2,6 +2,7 @@
#include "common/log.h" #include "common/log.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "cpu_disasm.h" #include "cpu_disasm.h"
#include "gte.h"
Log_SetChannel(CPU::Recompiler); Log_SetChannel(CPU::Recompiler);
// TODO: Turn load+sext/zext into a single signed/unsigned load // TODO: Turn load+sext/zext into a single signed/unsigned load
@ -12,7 +13,7 @@ namespace CPU::Recompiler {
u32 CodeGenerator::CalculateRegisterOffset(Reg reg) 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, 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.FlushAllGuestRegisters(true, true);
m_register_cache.FlushLoadDelay(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; return;
} }
@ -814,7 +815,7 @@ void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Excep
EmitBranch(GetCurrentFarCodePointer()); EmitBranch(GetCurrentFarCodePointer());
SwitchToFarCode(); SwitchToFarCode();
EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), epc, ri_bits); EmitFunctionCall(nullptr, &Thunks::RaiseException, epc, ri_bits);
EmitExceptionExit(); EmitExceptionExit();
SwitchToNearCode(); SwitchToNearCode();
@ -825,7 +826,7 @@ void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Excep
void CodeGenerator::BlockPrologue() 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 // 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 // TODO: Pull load delay into register cache
@ -859,21 +860,21 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou
if (m_branch_was_taken_dirty) if (m_branch_was_taken_dirty)
{ {
Value temp = m_register_cache.AllocateScratch(RegSize_8); Value temp = m_register_cache.AllocateScratch(RegSize_8);
EmitLoadCPUStructField(temp.host_reg, RegSize_8, offsetof(Core, m_branch_was_taken)); EmitLoadCPUStructField(temp.host_reg, RegSize_8, offsetof(State, branch_was_taken));
EmitStoreCPUStructField(offsetof(Core, m_current_instruction_was_branch_taken), temp); EmitStoreCPUStructField(offsetof(State, current_instruction_was_branch_taken), temp);
EmitStoreCPUStructField(offsetof(Core, m_branch_was_taken), Value::FromConstantU8(0)); EmitStoreCPUStructField(offsetof(State, branch_was_taken), Value::FromConstantU8(0));
m_current_instruction_was_branch_taken_dirty = true; m_current_instruction_was_branch_taken_dirty = true;
m_branch_was_taken_dirty = false; m_branch_was_taken_dirty = false;
} }
else if (m_current_instruction_was_branch_taken_dirty) 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; m_current_instruction_was_branch_taken_dirty = false;
} }
if (m_current_instruction_in_branch_delay_slot_dirty && !cbi.is_branch_delay_slot) 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; 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) if (cbi.is_branch_delay_slot)
{ {
// m_current_instruction_in_branch_delay_slot = true // 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; m_current_instruction_in_branch_delay_slot_dirty = true;
} }
@ -931,7 +932,7 @@ void CodeGenerator::AddPendingCycles(bool commit)
if (m_delayed_cycles_add == 0) if (m_delayed_cycles_add == 0)
return; 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) if (commit)
m_delayed_cycles_add = 0; m_delayed_cycles_add = 0;
@ -939,7 +940,7 @@ void CodeGenerator::AddPendingCycles(bool commit)
void CodeGenerator::SetCurrentInstructionPC(const CodeBlockInstruction& cbi) 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) bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
@ -954,19 +955,19 @@ bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
m_register_cache.WriteLoadDelayToCPU(true); 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 // emit the function call
if (CanInstructionTrap(cbi.instruction, m_block->key.user_mode)) if (CanInstructionTrap(cbi.instruction, m_block->key.user_mode))
{ {
// TODO: Use carry flag or something here too // TODO: Use carry flag or something here too
Value return_value = m_register_cache.AllocateScratch(RegSize_8); 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); EmitExceptionExitOnBool(return_value);
} }
else 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; 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); EmitBindLabel(&branch_okay);
SwitchToFarCode(); SwitchToFarCode();
EmitFunctionCall(nullptr, &Thunks::RaiseAddressException, m_register_cache.GetCPUPtr(), branch_target, EmitFunctionCall(nullptr, &Thunks::RaiseAddressException, branch_target, Value::FromConstantU8(0),
Value::FromConstantU8(0), Value::FromConstantU8(1)); Value::FromConstantU8(1));
EmitExceptionExit(); EmitExceptionExit();
SwitchToNearCode(); SwitchToNearCode();
@ -1550,53 +1551,53 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
switch (reg) switch (reg)
{ {
case Cop0Reg::BPC: case Cop0Reg::BPC:
offset = offsetof(Core, m_cop0_regs.BPC); offset = offsetof(State, cop0_regs.BPC);
break; break;
case Cop0Reg::BPCM: case Cop0Reg::BPCM:
offset = offsetof(Core, m_cop0_regs.BPCM); offset = offsetof(State, cop0_regs.BPCM);
break; break;
case Cop0Reg::BDA: case Cop0Reg::BDA:
offset = offsetof(Core, m_cop0_regs.BDA); offset = offsetof(State, cop0_regs.BDA);
break; break;
case Cop0Reg::BDAM: case Cop0Reg::BDAM:
offset = offsetof(Core, m_cop0_regs.BDAM); offset = offsetof(State, cop0_regs.BDAM);
break; break;
case Cop0Reg::DCIC: case Cop0Reg::DCIC:
offset = offsetof(Core, m_cop0_regs.dcic.bits); offset = offsetof(State, cop0_regs.dcic.bits);
write_mask = Cop0Registers::DCIC::WRITE_MASK; write_mask = Cop0Registers::DCIC::WRITE_MASK;
break; break;
case Cop0Reg::JUMPDEST: case Cop0Reg::JUMPDEST:
offset = offsetof(Core, m_cop0_regs.TAR); offset = offsetof(State, cop0_regs.TAR);
write_mask = 0; write_mask = 0;
break; break;
case Cop0Reg::BadVaddr: case Cop0Reg::BadVaddr:
offset = offsetof(Core, m_cop0_regs.BadVaddr); offset = offsetof(State, cop0_regs.BadVaddr);
write_mask = 0; write_mask = 0;
break; break;
case Cop0Reg::SR: case Cop0Reg::SR:
offset = offsetof(Core, m_cop0_regs.sr.bits); offset = offsetof(State, cop0_regs.sr.bits);
write_mask = Cop0Registers::SR::WRITE_MASK; write_mask = Cop0Registers::SR::WRITE_MASK;
break; break;
case Cop0Reg::CAUSE: case Cop0Reg::CAUSE:
offset = offsetof(Core, m_cop0_regs.cause.bits); offset = offsetof(State, cop0_regs.cause.bits);
write_mask = Cop0Registers::CAUSE::WRITE_MASK; write_mask = Cop0Registers::CAUSE::WRITE_MASK;
break; break;
case Cop0Reg::EPC: case Cop0Reg::EPC:
offset = offsetof(Core, m_cop0_regs.EPC); offset = offsetof(State, cop0_regs.EPC);
write_mask = 0; write_mask = 0;
break; break;
case Cop0Reg::PRID: case Cop0Reg::PRID:
offset = offsetof(Core, m_cop0_regs.PRID); offset = offsetof(State, cop0_regs.PRID);
write_mask = 0; write_mask = 0;
break; 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) // m_cop0_regs.sr.IEc && ((m_cop0_regs.cause.Ip & m_cop0_regs.sr.Im) != 0)
LabelType no_interrupt; LabelType no_interrupt;
EmitLoadCPUStructField(sr_value.host_reg, sr_value.size, offsetof(Core, m_cop0_regs.sr.bits)); EmitLoadCPUStructField(sr_value.host_reg, sr_value.size, offsetof(State, cop0_regs.sr.bits));
EmitLoadCPUStructField(cause_value.host_reg, cause_value.size, offsetof(Core, m_cop0_regs.cause.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); EmitBranchIfBitClear(sr_value.host_reg, sr_value.size, 0, &no_interrupt);
EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value); EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value);
EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00)); 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 // shift mode bits right two, preserving upper bits
static constexpr u32 mode_bits_mask = UINT32_C(0b1111); static constexpr u32 mode_bits_mask = UINT32_C(0b1111);
Value sr = m_register_cache.AllocateScratch(RegSize_32); 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); 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)); 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); 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); InstructionEpilogue(cbi);
return true; return true;
@ -1717,13 +1718,13 @@ Value CodeGenerator::DoGTERegisterRead(u32 index)
case 28: // IRGB case 28: // IRGB
case 29: // ORGB case 29: // ORGB
{ {
EmitFunctionCall(&value, &Thunks::ReadGTERegister, m_register_cache.GetCPUPtr(), Value::FromConstantU32(index)); EmitFunctionCall(&value, &GTE::ReadRegister, Value::FromConstantU32(index));
} }
break; break;
default: 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; break;
} }
@ -1752,7 +1753,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
{ {
// sign-extend z component of vector registers // sign-extend z component of vector registers
Value temp = ConvertValueSize(value.ViewAsSize(RegSize_16), RegSize_32, true); 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; return;
} }
break; break;
@ -1765,7 +1766,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
{ {
// zero-extend unsigned values // zero-extend unsigned values
Value temp = ConvertValueSize(value.ViewAsSize(RegSize_16), RegSize_32, false); 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; return;
} }
break; break;
@ -1776,15 +1777,15 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
Value temp = m_register_cache.AllocateScratch(RegSize_32); Value temp = m_register_cache.AllocateScratch(RegSize_32);
// SXY0 <- SXY1 // SXY0 <- SXY1
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(Core, m_cop2.m_regs.r32[13])); EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(State, gte_regs.r32[13]));
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[12]), temp); EmitStoreCPUStructField(offsetof(State, gte_regs.r32[12]), temp);
// SXY1 <- SXY2 // SXY1 <- SXY2
EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(Core, m_cop2.m_regs.r32[14])); EmitLoadCPUStructField(temp.host_reg, RegSize_32, offsetof(State, gte_regs.r32[14]));
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[13]), temp); EmitStoreCPUStructField(offsetof(State, gte_regs.r32[13]), temp);
// SXY2 <- SXYP // SXY2 <- SXYP
EmitStoreCPUStructField(offsetof(Core, m_cop2.m_regs.r32[14]), value); EmitStoreCPUStructField(offsetof(State, gte_regs.r32[14]), value);
return; return;
} }
break; break;
@ -1793,8 +1794,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
case 30: // LZCS case 30: // LZCS
case 63: // FLAG case 63: // FLAG
{ {
EmitFunctionCall(nullptr, &Thunks::WriteGTERegister, m_register_cache.GetCPUPtr(), Value::FromConstantU32(index), EmitFunctionCall(nullptr, &GTE::WriteRegister, Value::FromConstantU32(index), value);
value);
return; return;
} }
@ -1808,7 +1808,7 @@ void CodeGenerator::DoGTERegisterWrite(u32 index, const Value& value)
default: default:
{ {
// written as-is, 2x16 or 1x32 bits // 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; return;
} }
} }
@ -1878,7 +1878,7 @@ bool CodeGenerator::Compile_cop2(const CodeBlockInstruction& cbi)
InstructionPrologue(cbi, 1); InstructionPrologue(cbi, 1);
Value instruction_bits = Value::FromConstantU32(cbi.instruction.bits & GTE::Instruction::REQUIRED_BITS_MASK); 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); InstructionEpilogue(cbi);
return true; return true;

View file

@ -16,7 +16,7 @@ namespace CPU::Recompiler {
class CodeGenerator class CodeGenerator
{ {
public: public:
CodeGenerator(Core* cpu, JitCodeBuffer* code_buffer, const ASMFunctions& asm_functions); CodeGenerator(JitCodeBuffer* code_buffer);
~CodeGenerator(); ~CodeGenerator();
static u32 CalculateRegisterOffset(Reg reg); static u32 CalculateRegisterOffset(Reg reg);
@ -62,6 +62,8 @@ public:
void EmitLoadCPUStructField(HostReg host_reg, RegSize size, u32 offset); void EmitLoadCPUStructField(HostReg host_reg, RegSize size, u32 offset);
void EmitStoreCPUStructField(u32 offset, const Value& value); void EmitStoreCPUStructField(u32 offset, const Value& value);
void EmitAddCPUStructField(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. // Automatically generates an exception handler.
Value EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const Value& address, RegSize size); Value EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const Value& address, RegSize size);
@ -188,9 +190,7 @@ private:
bool Compile_cop0(const CodeBlockInstruction& cbi); bool Compile_cop0(const CodeBlockInstruction& cbi);
bool Compile_cop2(const CodeBlockInstruction& cbi); bool Compile_cop2(const CodeBlockInstruction& cbi);
Core* m_cpu;
JitCodeBuffer* m_code_buffer; JitCodeBuffer* m_code_buffer;
const ASMFunctions& m_asm_functions;
const CodeBlock* m_block = nullptr; const CodeBlock* m_block = nullptr;
const CodeBlockInstruction* m_block_start = nullptr; const CodeBlockInstruction* m_block_start = nullptr;
const CodeBlockInstruction* m_block_end = nullptr; const CodeBlockInstruction* m_block_end = nullptr;

View file

@ -1,7 +1,9 @@
#include "common/align.h"
#include "common/assert.h"
#include "common/log.h" #include "common/log.h"
#include "cpu_core.h"
#include "cpu_recompiler_code_generator.h" #include "cpu_recompiler_code_generator.h"
#include "cpu_recompiler_thunks.h" #include "cpu_recompiler_thunks.h"
#include "cpu_core.h"
Log_SetChannel(CPU::Recompiler); Log_SetChannel(CPU::Recompiler);
namespace a64 = vixl::aarch64; namespace a64 = vixl::aarch64;
@ -21,10 +23,7 @@ constexpr u64 FUNCTION_CALLER_SAVED_SPACE_RESERVE = 144; // 18 registers -> 224
constexpr u64 FUNCTION_STACK_SIZE = constexpr u64 FUNCTION_STACK_SIZE =
FUNCTION_CALLEE_SAVED_SPACE_RESERVE + FUNCTION_CALLER_SAVED_SPACE_RESERVE + FUNCTION_CALL_SHADOW_SPACE; FUNCTION_CALLEE_SAVED_SPACE_RESERVE + FUNCTION_CALLER_SAVED_SPACE_RESERVE + FUNCTION_CALL_SHADOW_SPACE;
static const a64::WRegister GetHostReg8(HostReg reg) static const a64::WRegister GetHostReg8(HostReg reg) { return a64::WRegister(reg); }
{
return a64::WRegister(reg);
}
static const a64::WRegister GetHostReg8(const Value& value) 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); return a64::WRegister(value.host_reg);
} }
static const a64::WRegister GetHostReg16(HostReg reg) static const a64::WRegister GetHostReg16(HostReg reg) { return a64::WRegister(reg); }
{
return a64::WRegister(reg);
}
static const a64::WRegister GetHostReg16(const Value& value) 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); return a64::WRegister(value.host_reg);
} }
static const a64::WRegister GetHostReg32(HostReg reg) static const a64::WRegister GetHostReg32(HostReg reg) { return a64::WRegister(reg); }
{
return a64::WRegister(reg);
}
static const a64::WRegister GetHostReg32(const Value& value) 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); return a64::WRegister(value.host_reg);
} }
static const a64::XRegister GetHostReg64(HostReg reg) static const a64::XRegister GetHostReg64(HostReg reg) { return a64::XRegister(reg); }
{
return a64::XRegister(reg);
}
static const a64::XRegister GetHostReg64(const Value& value) 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); return a64::XRegister(value.host_reg);
} }
static const a64::XRegister GetCPUPtrReg() static const a64::XRegister GetCPUPtrReg() { return GetHostReg64(RCPUPTR); }
{
return GetHostReg64(RCPUPTR);
}
CodeGenerator::CodeGenerator(Core* cpu, JitCodeBuffer* code_buffer, const ASMFunctions& asm_functions) CodeGenerator::CodeGenerator(JitCodeBuffer* code_buffer)
: m_cpu(cpu), m_code_buffer(code_buffer), m_asm_functions(asm_functions), m_register_cache(*this), : m_code_buffer(code_buffer), m_register_cache(*this),
m_near_emitter(static_cast<vixl::byte*>(code_buffer->GetFreeCodePointer()), code_buffer->GetFreeCodeSpace(), m_near_emitter(static_cast<vixl::byte*>(code_buffer->GetFreeCodePointer()), code_buffer->GetFreeCodeSpace(),
a64::PositionDependentCode), a64::PositionDependentCode),
m_far_emitter(static_cast<vixl::byte*>(code_buffer->GetFreeFarCodePointer()), code_buffer->GetFreeFarCodeSpace(), 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) void CodeGenerator::AlignCodeBuffer(JitCodeBuffer* code_buffer) { code_buffer->Align(16, 0x90); }
{
code_buffer->Align(16, 0x90);
}
void CodeGenerator::InitHostRegs() void CodeGenerator::InitHostRegs()
{ {
@ -127,15 +111,9 @@ void CodeGenerator::InitHostRegs()
m_register_cache.SetCPUPtrHostReg(RCPUPTR); m_register_cache.SetCPUPtrHostReg(RCPUPTR);
} }
void CodeGenerator::SwitchToFarCode() void CodeGenerator::SwitchToFarCode() { m_emit = &m_far_emitter; }
{
m_emit = &m_far_emitter;
}
void CodeGenerator::SwitchToNearCode() void CodeGenerator::SwitchToNearCode() { m_emit = &m_near_emitter; }
{
m_emit = &m_near_emitter;
}
void* CodeGenerator::GetCurrentNearCodePointer() const void* CodeGenerator::GetCurrentNearCodePointer() const
{ {
@ -168,10 +146,10 @@ void CodeGenerator::EmitBeginBlock()
const bool link_reg_allocated = m_register_cache.AllocateHostReg(30); const bool link_reg_allocated = m_register_cache.AllocateHostReg(30);
DebugAssert(link_reg_allocated); 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); const bool cpu_reg_allocated = m_register_cache.AllocateHostReg(RCPUPTR);
DebugAssert(cpu_reg_allocated); DebugAssert(cpu_reg_allocated);
m_emit->Mov(GetCPUPtrReg(), GetHostReg64(RARG1)); m_emit->Mov(GetCPUPtrReg(), reinterpret_cast<size_t>(&g_state));
} }
void CodeGenerator::EmitEndBlock() void CodeGenerator::EmitEndBlock()
@ -904,9 +882,13 @@ u32 CodeGenerator::PrepareStackForCall()
return 0; 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) 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) if (return_value)
return_value->Discard(); return_value->Discard();
// must be allocated before the stack push
Value temp = m_register_cache.AllocateScratch(RegSize_64);
// shadow space allocate // shadow space allocate
const u32 adjust_size = PrepareStackForCall(); const u32 adjust_size = PrepareStackForCall();
// actually call the function // actually call the function
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr)); const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
m_emit->Blr(GetHostReg64(temp)); 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 // shadow space release
RestoreStackAfterCall(adjust_size); RestoreStackAfterCall(adjust_size);
// must happen after the stack push
temp.ReleaseAndClear();
// copy out return value if requested // copy out return value if requested
if (return_value) if (return_value)
{ {
@ -943,9 +928,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
if (return_value) if (return_value)
return_value->Discard(); return_value->Discard();
// must be allocated before the stack push
Value temp = m_register_cache.AllocateScratch(RegSize_64);
// shadow space allocate // shadow space allocate
const u32 adjust_size = PrepareStackForCall(); const u32 adjust_size = PrepareStackForCall();
@ -953,15 +935,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
EmitCopyValue(RARG1, arg1); EmitCopyValue(RARG1, arg1);
// actually call the function // actually call the function
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr)); const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
m_emit->Blr(GetHostReg64(temp)); 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 // shadow space release
RestoreStackAfterCall(adjust_size); RestoreStackAfterCall(adjust_size);
// must happen after the stack push
temp.ReleaseAndClear();
// copy out return value if requested // copy out return value if requested
if (return_value) if (return_value)
{ {
@ -975,9 +963,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
if (return_value) if (return_value)
return_value->Discard(); return_value->Discard();
// must be allocated before the stack push
Value temp = m_register_cache.AllocateScratch(RegSize_64);
// shadow space allocate // shadow space allocate
const u32 adjust_size = PrepareStackForCall(); const u32 adjust_size = PrepareStackForCall();
@ -986,15 +971,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
EmitCopyValue(RARG2, arg2); EmitCopyValue(RARG2, arg2);
// actually call the function // actually call the function
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr)); const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
m_emit->Blr(GetHostReg64(temp)); 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 // shadow space release
RestoreStackAfterCall(adjust_size); RestoreStackAfterCall(adjust_size);
// must happen after the stack push
temp.ReleaseAndClear();
// copy out return value if requested // copy out return value if requested
if (return_value) if (return_value)
{ {
@ -1009,9 +1000,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
if (return_value) if (return_value)
m_register_cache.DiscardHostReg(return_value->GetHostRegister()); 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 // shadow space allocate
const u32 adjust_size = PrepareStackForCall(); const u32 adjust_size = PrepareStackForCall();
@ -1021,15 +1009,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
EmitCopyValue(RARG3, arg3); EmitCopyValue(RARG3, arg3);
// actually call the function // actually call the function
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr)); const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
m_emit->Blr(GetHostReg64(temp)); 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 // shadow space release
RestoreStackAfterCall(adjust_size); RestoreStackAfterCall(adjust_size);
// must happen after the stack push
temp.ReleaseAndClear();
// copy out return value if requested // copy out return value if requested
if (return_value) if (return_value)
{ {
@ -1044,8 +1038,6 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
if (return_value) if (return_value)
return_value->Discard(); return_value->Discard();
// must be allocated before the stack push
Value temp = m_register_cache.AllocateScratch(RegSize_64);
// shadow space allocate // shadow space allocate
const u32 adjust_size = PrepareStackForCall(); const u32 adjust_size = PrepareStackForCall();
@ -1057,15 +1049,21 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr, co
EmitCopyValue(RARG4, arg4); EmitCopyValue(RARG4, arg4);
// actually call the function // actually call the function
m_emit->Mov(GetHostReg64(temp), reinterpret_cast<uintptr_t>(ptr)); const s64 displacement = GetBranchDisplacement(GetCurrentCodePointer(), ptr);
m_emit->Blr(GetHostReg64(temp)); 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 // shadow space release
RestoreStackAfterCall(adjust_size); RestoreStackAfterCall(adjust_size);
// must happen after the stack push
temp.ReleaseAndClear();
// copy out return value if requested // copy out return value if requested
if (return_value) if (return_value)
{ {
@ -1222,15 +1220,15 @@ Value CodeGenerator::EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const
switch (size) switch (size)
{ {
case RegSize_8: case RegSize_8:
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), pc, address); EmitFunctionCall(&result, &Thunks::ReadMemoryByte, pc, address);
break; break;
case RegSize_16: case RegSize_16:
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address); EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, pc, address);
break; break;
case RegSize_32: case RegSize_32:
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), pc, address); EmitFunctionCall(&result, &Thunks::ReadMemoryWord, pc, address);
break; break;
default: default:
@ -1285,15 +1283,15 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
switch (value.size) switch (value.size)
{ {
case RegSize_8: case RegSize_8:
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), pc, address, value); EmitFunctionCall(&result, &Thunks::WriteMemoryByte, pc, address, value);
break; break;
case RegSize_16: case RegSize_16:
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address, value); EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, pc, address, value);
break; break;
case RegSize_32: case RegSize_32:
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), pc, address, value); EmitFunctionCall(&result, &Thunks::WriteMemoryWord, pc, address, value);
break; break;
default: default:
@ -1316,14 +1314,18 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
m_register_cache.PopState(); 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() void CodeGenerator::EmitFlushInterpreterLoadDelay()
{ {
Value reg = m_register_cache.AllocateScratch(RegSize_32); Value reg = m_register_cache.AllocateScratch(RegSize_32);
Value value = 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_reg(GetCPUPtrReg(), offsetof(State, load_delay_reg));
const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(Core, m_load_delay_value)); const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(State, load_delay_value));
const a64::MemOperand regs_base(GetCPUPtrReg(), offsetof(Core, m_regs.r[0])); const a64::MemOperand regs_base(GetCPUPtrReg(), offsetof(State, regs.r[0]));
a64::Label skip_flush; a64::Label skip_flush;
@ -1339,7 +1341,7 @@ void CodeGenerator::EmitFlushInterpreterLoadDelay()
// reg = offset(r[0] + reg << 2) // reg = offset(r[0] + reg << 2)
m_emit->Lsl(GetHostReg32(reg), GetHostReg32(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 // r[reg] = value
m_emit->Str(GetHostReg32(value), a64::MemOperand(GetCPUPtrReg(), GetHostReg32(reg))); 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 reg = m_register_cache.AllocateScratch(RegSize_32);
Value value = 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_reg(GetCPUPtrReg(), offsetof(State, load_delay_reg));
const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(Core, m_load_delay_value)); const a64::MemOperand load_delay_value(GetCPUPtrReg(), offsetof(State, load_delay_value));
const a64::MemOperand next_load_delay_reg(GetCPUPtrReg(), offsetof(Core, m_next_load_delay_reg)); const a64::MemOperand next_load_delay_reg(GetCPUPtrReg(), offsetof(State, next_load_delay_reg));
const a64::MemOperand next_load_delay_value(GetCPUPtrReg(), offsetof(Core, m_next_load_delay_value)); 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->Ldrb(GetHostReg32(reg), next_load_delay_reg);
m_emit->Ldr(GetHostReg32(value), next_load_delay_value); m_emit->Ldr(GetHostReg32(value), next_load_delay_value);
@ -1374,7 +1376,7 @@ void CodeGenerator::EmitCancelInterpreterLoadDelayForReg(Reg reg)
if (!m_load_delay_dirty) if (!m_load_delay_dirty)
return; 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); Value temp = m_register_cache.AllocateScratch(RegSize_8);
a64::Label skip_cancel; a64::Label skip_cancel;
@ -1632,11 +1634,6 @@ void CodeGenerator::EmitBranchIfBitClear(HostReg reg, RegSize size, u8 bit, Labe
} }
} }
void CodeGenerator::EmitBindLabel(LabelType* label) void CodeGenerator::EmitBindLabel(LabelType* label) { m_emit->Bind(label); }
{
m_emit->Bind(label);
}
void ASMFunctions::Generate(JitCodeBuffer* code_buffer) {}
} // namespace CPU::Recompiler } // namespace CPU::Recompiler

View file

@ -17,8 +17,8 @@ void CodeGenerator::EmitStoreGuestRegister(Reg guest_reg, const Value& value)
void CodeGenerator::EmitStoreInterpreterLoadDelay(Reg reg, const Value& value) void CodeGenerator::EmitStoreInterpreterLoadDelay(Reg reg, const Value& value)
{ {
DebugAssert(value.size == RegSize_32 && value.IsInHostRegister()); DebugAssert(value.size == RegSize_32 && value.IsInHostRegister());
EmitStoreCPUStructField(offsetof(Core, m_load_delay_reg), Value::FromConstantU8(static_cast<u8>(reg))); EmitStoreCPUStructField(offsetof(State, load_delay_reg), Value::FromConstantU8(static_cast<u8>(reg)));
EmitStoreCPUStructField(offsetof(Core, m_load_delay_value), value); EmitStoreCPUStructField(offsetof(State, load_delay_value), value);
m_load_delay_dirty = true; m_load_delay_dirty = true;
} }

View file

@ -1,6 +1,7 @@
#include "cpu_core.h" #include "cpu_core.h"
#include "cpu_recompiler_code_generator.h" #include "cpu_recompiler_code_generator.h"
#include "cpu_recompiler_thunks.h" #include "cpu_recompiler_thunks.h"
#include "common/align.h"
namespace CPU::Recompiler { namespace CPU::Recompiler {
@ -73,8 +74,8 @@ static const Xbyak::Reg64 GetCPUPtrReg()
return GetHostReg64(RCPUPTR); return GetHostReg64(RCPUPTR);
} }
CodeGenerator::CodeGenerator(Core* cpu, JitCodeBuffer* code_buffer, const ASMFunctions& asm_functions) CodeGenerator::CodeGenerator(JitCodeBuffer* code_buffer)
: m_cpu(cpu), m_code_buffer(code_buffer), m_asm_functions(asm_functions), m_register_cache(*this), : m_code_buffer(code_buffer), m_register_cache(*this),
m_near_emitter(code_buffer->GetFreeCodeSpace(), code_buffer->GetFreeCodePointer()), m_near_emitter(code_buffer->GetFreeCodeSpace(), code_buffer->GetFreeCodePointer()),
m_far_emitter(code_buffer->GetFreeFarCodeSpace(), code_buffer->GetFreeFarCodePointer()), m_emit(&m_near_emitter) 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. // Store the CPU struct pointer.
const bool cpu_reg_allocated = m_register_cache.AllocateHostReg(RCPUPTR); const bool cpu_reg_allocated = m_register_cache.AllocateHostReg(RCPUPTR);
DebugAssert(cpu_reg_allocated); DebugAssert(cpu_reg_allocated);
m_emit->mov(GetCPUPtrReg(), GetHostReg64(RARG1)); m_emit->mov(GetCPUPtrReg(), reinterpret_cast<size_t>(&g_state));
} }
void CodeGenerator::EmitEndBlock() void CodeGenerator::EmitEndBlock()
@ -1305,8 +1306,15 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr)
const u32 adjust_size = PrepareStackForCall(); const u32 adjust_size = PrepareStackForCall();
// actually call the function // actually call the function
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->mov(GetHostReg64(RRETURN), reinterpret_cast<size_t>(ptr));
m_emit->call(GetHostReg64(RRETURN)); m_emit->call(GetHostReg64(RRETURN));
}
// shadow space release // shadow space release
RestoreStackAfterCall(adjust_size); RestoreStackAfterCall(adjust_size);
@ -1645,15 +1653,15 @@ Value CodeGenerator::EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const
switch (size) switch (size)
{ {
case RegSize_8: case RegSize_8:
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), pc, address); EmitFunctionCall(&result, &Thunks::ReadMemoryByte, pc, address);
break; break;
case RegSize_16: case RegSize_16:
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address); EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, pc, address);
break; break;
case RegSize_32: case RegSize_32:
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), pc, address); EmitFunctionCall(&result, &Thunks::ReadMemoryWord, pc, address);
break; break;
default: default:
@ -1706,15 +1714,15 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
switch (value.size) switch (value.size)
{ {
case RegSize_8: case RegSize_8:
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), pc, address, value); EmitFunctionCall(&result, &Thunks::WriteMemoryByte, pc, address, value);
break; break;
case RegSize_16: case RegSize_16:
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address, value); EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, pc, address, value);
break; break;
case RegSize_32: case RegSize_32:
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), pc, address, value); EmitFunctionCall(&result, &Thunks::WriteMemoryWord, pc, address, value);
break; break;
default: default:
@ -1735,14 +1743,208 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
m_register_cache.PopState(); 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() void CodeGenerator::EmitFlushInterpreterLoadDelay()
{ {
Value reg = m_register_cache.AllocateScratch(RegSize_8); Value reg = m_register_cache.AllocateScratch(RegSize_8);
Value value = m_register_cache.AllocateScratch(RegSize_32); 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_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_value)]; auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, load_delay_value)];
auto reg_ptr = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_regs.r[0]) + GetHostReg64(reg.host_reg) * 4]; auto reg_ptr = m_emit->dword[GetCPUPtrReg() + offsetof(State, regs.r[0]) + GetHostReg64(reg.host_reg) * 4];
Xbyak::Label skip_flush; Xbyak::Label skip_flush;
@ -1768,10 +1970,10 @@ void CodeGenerator::EmitMoveNextInterpreterLoadDelay()
Value reg = m_register_cache.AllocateScratch(RegSize_8); Value reg = m_register_cache.AllocateScratch(RegSize_8);
Value value = m_register_cache.AllocateScratch(RegSize_32); 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_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_value)]; auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, load_delay_value)];
auto next_load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_reg)]; 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(Core, m_next_load_delay_value)]; 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(GetHostReg32(value), next_load_delay_value);
m_emit->mov(GetHostReg8(reg), next_load_delay_reg); m_emit->mov(GetHostReg8(reg), next_load_delay_reg);
@ -1785,7 +1987,7 @@ void CodeGenerator::EmitCancelInterpreterLoadDelayForReg(Reg reg)
if (!m_load_delay_dirty) if (!m_load_delay_dirty)
return; 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; Xbyak::Label skip_cancel;
@ -2047,6 +2249,4 @@ void CodeGenerator::EmitBindLabel(LabelType* label)
m_emit->L(*label); m_emit->L(*label);
} }
void ASMFunctions::Generate(JitCodeBuffer* code_buffer) {}
} // namespace CPU::Recompiler } // namespace CPU::Recompiler

View file

@ -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

View file

@ -1,19 +1,12 @@
#pragma once #pragma once
#include "cpu_code_cache.h"
#include "cpu_types.h" #include "cpu_types.h"
class JitCodeBuffer;
namespace CPU { namespace CPU {
struct CodeBlockInstruction; struct CodeBlockInstruction;
class Core; namespace Recompiler::Thunks {
namespace Recompiler {
class Thunks
{
public:
union RaiseExceptionInfo union RaiseExceptionInfo
{ {
u32 bits; u32 bits;
@ -27,41 +20,32 @@ public:
}; };
}; };
static u32 MakeRaiseExceptionInfo(Exception excode, const CodeBlockInstruction& cbi); ALWAYS_INLINE u32 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;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Trampolines for calling back from the JIT // Trampolines for calling back from the JIT
// Needed because we can't cast member functions to void*... // Needed because we can't cast member functions to void*...
// TODO: Abuse carry flag or something else for exception // TODO: Abuse carry flag or something else for exception
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
static u64 ReadMemoryByte(Core* cpu, u32 pc, u32 address); bool InterpretInstruction();
static u64 ReadMemoryHalfWord(Core* cpu, u32 pc, u32 address); void RaiseException(u32 epc, u32 ri_bits);
static u64 ReadMemoryWord(Core* cpu, u32 pc, u32 address); void RaiseAddressException(u32 address, bool store, bool branch);
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 // Memory access functions for the JIT - MSB is set on exception.
{ u64 ReadMemoryByte(u32 pc, u32 address);
public: u64 ReadMemoryHalfWord(u32 pc, u32 address);
bool (*read_memory_byte)(u32 address, u8* value); u64 ReadMemoryWord(u32 pc, u32 address);
bool (*read_memory_word)(u32 address, u16* value); bool WriteMemoryByte(u32 pc, u32 address, u8 value);
bool (*read_memory_dword)(u32 address, u32* value); bool WriteMemoryHalfWord(u32 pc, u32 address, u16 value);
void (*write_memory_byte)(u32 address, u8 value); bool WriteMemoryWord(u32 pc, u32 address, u32 value);
void (*write_memory_word)(u32 address, u16 value);
void (*write_memory_dword)(u32 address, u32 value);
void Generate(JitCodeBuffer* code_buffer); } // namespace Recompiler::Thunks
};
} // namespace Recompiler
} // namespace CPU } // namespace CPU

View file

@ -21,9 +21,6 @@
namespace CPU { namespace CPU {
class Core;
class CodeCache;
namespace Recompiler { namespace Recompiler {
class CodeGenerator; class CodeGenerator;
@ -77,6 +74,9 @@ constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
// Are shifts implicitly masked to 0..31? // Are shifts implicitly masked to 0..31?
constexpr bool SHIFTS_ARE_IMPLICITLY_MASKED = true; constexpr bool SHIFTS_ARE_IMPLICITLY_MASKED = true;
// Alignment of code stoarge.
constexpr u32 CODE_STORAGE_ALIGNMENT = 4096;
// ABI selection // ABI selection
#if defined(WIN32) #if defined(WIN32)
#define ABI_WIN64 1 #define ABI_WIN64 1
@ -105,6 +105,9 @@ constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
// Are shifts implicitly masked to 0..31? // Are shifts implicitly masked to 0..31?
constexpr bool SHIFTS_ARE_IMPLICITLY_MASKED = true; constexpr bool SHIFTS_ARE_IMPLICITLY_MASKED = true;
// Alignment of code stoarge.
constexpr u32 CODE_STORAGE_ALIGNMENT = 4096;
#else #else
using HostReg = int; using HostReg = int;

View file

@ -4,8 +4,6 @@
namespace CPU { namespace CPU {
class Core;
// Memory address mask used for fetching as well as loadstores (removes cached/uncached/user/kernel bits). // Memory address mask used for fetching as well as loadstores (removes cached/uncached/user/kernel bits).
enum : PhysicalMemoryAddress enum : PhysicalMemoryAddress
{ {

View file

@ -4,6 +4,7 @@
#include "common/log.h" #include "common/log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "cpu_core.h"
#include "gpu.h" #include "gpu.h"
#include "interrupt_controller.h" #include "interrupt_controller.h"
#include "mdec.h" #include "mdec.h"
@ -11,27 +12,27 @@
#include "system.h" #include "system.h"
Log_SetChannel(DMA); Log_SetChannel(DMA);
DMA g_dma;
DMA::DMA() = default; DMA::DMA() = default;
DMA::~DMA() = default; DMA::~DMA() = default;
void DMA::Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, void DMA::Initialize()
SPU* spu, MDEC* mdec)
{ {
m_system = system; m_max_slice_ticks = g_settings.dma_max_slice_ticks;
m_bus = bus; m_halt_ticks = g_settings.dma_halt_ticks;
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_transfer_buffer.resize(32); m_transfer_buffer.resize(32);
m_unhalt_event = system->CreateTimingEvent("DMA Transfer Unhalt", 1, m_max_slice_ticks, m_unhalt_event = TimingEvents::CreateTimingEvent("DMA Transfer Unhalt", 1, m_max_slice_ticks,
std::bind(&DMA::UnhaltTransfer, this, std::placeholders::_1), false); std::bind(&DMA::UnhaltTransfer, this, std::placeholders::_1), false);
Reset();
}
void DMA::Shutdown()
{
m_unhalt_event.reset();
} }
void DMA::Reset() void DMA::Reset()
@ -238,7 +239,7 @@ void DMA::UpdateIRQ()
if (m_DICR.master_flag) if (m_DICR.master_flag)
{ {
Log_TracePrintf("Firing DMA master interrupt"); 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 else
used_ticks = TransferDeviceToMemory(channel, current_address & ADDRESS_MASK, increment, word_count); used_ticks = TransferDeviceToMemory(channel, current_address & ADDRESS_MASK, increment, word_count);
m_system->StallCPU(used_ticks); CPU::AddPendingTicks(used_ticks);
} }
break; 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), Log_DebugPrintf("DMA%u: Copying linked list starting at 0x%08X to device", static_cast<u32>(channel),
current_address & ADDRESS_MASK); current_address & ADDRESS_MASK);
u8* ram_pointer = m_bus->GetRAM(); u8* ram_pointer = Bus::g_ram;
bool halt_transfer = false; bool halt_transfer = false;
while (cs.request) while (cs.request)
{ {
@ -319,7 +320,7 @@ bool DMA::TransferChannel(Channel channel)
} }
cs.base_address = current_address; cs.base_address = current_address;
m_system->StallCPU(used_ticks); CPU::AddPendingTicks(used_ticks);
if (current_address & UINT32_C(0x800000)) if (current_address & UINT32_C(0x800000))
break; break;
@ -370,7 +371,7 @@ bool DMA::TransferChannel(Channel channel)
cs.base_address = current_address & BASE_ADDRESS_MASK; cs.base_address = current_address & BASE_ADDRESS_MASK;
cs.block_control.request.block_count = blocks_remaining; 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 // finish transfer later if the request was cleared
if (blocks_remaining > 0) 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) 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) if (static_cast<s32>(increment) < 0 || ((address + (increment * word_count)) & ADDRESS_MASK) <= address)
{ {
// Use temp buffer if it's wrapping around // Use temp buffer if it's wrapping around
if (m_transfer_buffer.size() < word_count) if (m_transfer_buffer.size() < word_count)
m_transfer_buffer.resize(word_count); m_transfer_buffer.resize(word_count);
src_pointer = m_transfer_buffer.data(); 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) switch (channel)
{ {
case Channel::GPU: case Channel::GPU:
m_gpu->DMAWrite(src_pointer, word_count); g_gpu->DMAWrite(src_pointer, word_count);
break; break;
case Channel::SPU: case Channel::SPU:
m_spu->DMAWrite(src_pointer, word_count); g_spu.DMAWrite(src_pointer, word_count);
break; break;
case Channel::MDECin: case Channel::MDECin:
m_mdec->DMAWrite(src_pointer, word_count); g_mdec.DMAWrite(src_pointer, word_count);
break; break;
case Channel::CDROM: case Channel::CDROM:
@ -459,7 +466,7 @@ TickCount DMA::TransferMemoryToDevice(Channel channel, u32 address, u32 incremen
break; break;
} }
return m_bus->GetDMARAMTickCount(word_count); return Bus::GetDMARAMTickCount(word_count);
} }
TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 increment, u32 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) if (channel == Channel::OTC)
{ {
// clear ordering table // clear ordering table
u8* ram_pointer = m_bus->GetRAM(); u8* ram_pointer = Bus::g_ram;
const u32 word_count_less_1 = word_count - 1; const u32 word_count_less_1 = word_count - 1;
for (u32 i = 0; i < word_count_less_1; i++) 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); const u32 terminator = UINT32_C(0xFFFFFF);
std::memcpy(&ram_pointer[address], &terminator, sizeof(terminator)); std::memcpy(&ram_pointer[address], &terminator, sizeof(terminator));
m_bus->InvalidateCodePages(address, word_count); Bus::InvalidateCodePages(address, word_count);
return m_bus->GetDMARAMTickCount(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) if (static_cast<s32>(increment) < 0 || ((address + (increment * word_count)) & ADDRESS_MASK) <= address)
{ {
// Use temp buffer if it's wrapping around // Use temp buffer if it's wrapping around
@ -495,19 +502,19 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
switch (channel) switch (channel)
{ {
case Channel::GPU: case Channel::GPU:
m_gpu->DMARead(dest_pointer, word_count); g_gpu->DMARead(dest_pointer, word_count);
break; break;
case Channel::CDROM: case Channel::CDROM:
m_cdrom->DMARead(dest_pointer, word_count); g_cdrom.DMARead(dest_pointer, word_count);
break; break;
case Channel::SPU: case Channel::SPU:
m_spu->DMARead(dest_pointer, word_count); g_spu.DMARead(dest_pointer, word_count);
break; break;
case Channel::MDECout: case Channel::MDECout:
m_mdec->DMARead(dest_pointer, word_count); g_mdec.DMARead(dest_pointer, word_count);
break; break;
default: default:
@ -518,7 +525,7 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
if (dest_pointer == m_transfer_buffer.data()) 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++) for (u32 i = 0; i < word_count; i++)
{ {
std::memcpy(&ram_pointer[address], &m_transfer_buffer[i], sizeof(u32)); 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); Bus::InvalidateCodePages(address, word_count);
return m_bus->GetDMARAMTickCount(word_count); return Bus::GetDMARAMTickCount(word_count);
} }

View file

@ -7,14 +7,7 @@
class StateWrapper; class StateWrapper;
class System;
class TimingEvent; class TimingEvent;
class Bus;
class InterruptController;
class GPU;
class CDROM;
class SPU;
class MDEC;
class DMA class DMA
{ {
@ -38,8 +31,8 @@ public:
DMA(); DMA();
~DMA(); ~DMA();
void Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, SPU* spu, void Initialize();
MDEC* mdec); void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -49,7 +42,6 @@ public:
void SetRequest(Channel channel, bool request); void SetRequest(Channel channel, bool request);
// changing interfaces // changing interfaces
void SetGPU(GPU* gpu) { m_gpu = gpu; }
void SetMaxSliceTicks(TickCount ticks) { m_max_slice_ticks = ticks; } void SetMaxSliceTicks(TickCount ticks) { m_max_slice_ticks = ticks; }
void SetHaltTicks(TickCount ticks) { m_halt_ticks = ticks; } void SetHaltTicks(TickCount ticks) { m_halt_ticks = ticks; }
@ -82,14 +74,6 @@ private:
// from memory -> device // from memory -> device
TickCount TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count); 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 // configuration
TickCount m_max_slice_ticks = 1000; TickCount m_max_slice_ticks = 1000;
TickCount m_halt_ticks = 100; TickCount m_halt_ticks = 100;
@ -212,3 +196,5 @@ private:
} }
} m_DICR = {}; } m_DICR = {};
}; };
extern DMA g_dma;

View file

@ -13,51 +13,45 @@
#include <imgui.h> #include <imgui.h>
Log_SetChannel(GPU); Log_SetChannel(GPU);
std::unique_ptr<GPU> g_gpu;
const GPU::GP0CommandHandlerTable GPU::s_GP0_command_handler_table = GPU::GenerateGP0CommandHandlerTable(); const GPU::GP0CommandHandlerTable GPU::s_GP0_command_handler_table = GPU::GenerateGP0CommandHandlerTable();
GPU::GPU() = default; GPU::GPU() = default;
GPU::~GPU() = default; GPU::~GPU() = default;
bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, bool GPU::Initialize(HostDisplay* host_display)
Timers* timers)
{ {
m_host_display = host_display; m_host_display = host_display;
m_system = system; m_force_progressive_scan = g_settings.gpu_disable_interlacing;
m_dma = dma; m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings;
m_interrupt_controller = interrupt_controller; m_crtc_state.display_aspect_ratio = Settings::GetDisplayAspectRatioValue(g_settings.display_aspect_ratio);
m_timers = timers; m_crtc_tick_event = TimingEvents::CreateTimingEvent(
m_force_progressive_scan = m_system->GetSettings().gpu_disable_interlacing; "GPU CRTC Tick", 1, 1, std::bind(&GPU::CRTCTickEvent, this, std::placeholders::_1), true);
m_force_ntsc_timings = m_system->GetSettings().gpu_force_ntsc_timings; m_command_tick_event = TimingEvents::CreateTimingEvent(
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(
"GPU Command Tick", 1, 1, std::bind(&GPU::CommandTickEvent, this, std::placeholders::_1), true); "GPU Command Tick", 1, 1, std::bind(&GPU::CommandTickEvent, this, std::placeholders::_1), true);
m_fifo_size = system->GetSettings().gpu_fifo_size; m_fifo_size = g_settings.gpu_fifo_size;
m_max_run_ahead = system->GetSettings().gpu_max_run_ahead; m_max_run_ahead = g_settings.gpu_max_run_ahead;
m_console_is_pal = system->IsPALRegion(); m_console_is_pal = System::IsPALRegion();
UpdateCRTCConfig(); UpdateCRTCConfig();
return true; return true;
} }
void GPU::UpdateSettings() 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; if (m_force_ntsc_timings != g_settings.gpu_force_ntsc_timings || m_console_is_pal != System::IsPALRegion())
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())
{ {
m_force_ntsc_timings = settings.gpu_force_ntsc_timings; m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings;
m_console_is_pal = m_system->IsPALRegion(); m_console_is_pal = System::IsPALRegion();
UpdateCRTCConfig(); 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 // Crop mode calls this, so recalculate the display area
UpdateCRTCDisplayParameters(); UpdateCRTCDisplayParameters();
@ -75,7 +69,7 @@ void GPU::SoftReset()
FlushRender(); FlushRender();
m_GPUSTAT.bits = 0x14802000; 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.Set(0, 0, 0, 0);
m_drawing_area_changed = true; m_drawing_area_changed = true;
m_drawing_offset = {}; m_drawing_offset = {};
@ -276,7 +270,7 @@ void GPU::UpdateDMARequest()
break; break;
} }
m_GPUSTAT.dma_data_request = dma_request; 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() void GPU::UpdateGPUIdle()
@ -498,7 +492,7 @@ void GPU::UpdateCRTCConfig()
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE; cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
} }
m_system->SetThrottleFrequency(ComputeVerticalFrequency()); System::SetThrottleFrequency(ComputeVerticalFrequency());
UpdateCRTCDisplayParameters(); UpdateCRTCDisplayParameters();
UpdateCRTCTickEvent(); UpdateCRTCTickEvent();
@ -507,7 +501,7 @@ void GPU::UpdateCRTCConfig()
void GPU::UpdateCRTCDisplayParameters() void GPU::UpdateCRTCDisplayParameters()
{ {
CRTCState& cs = m_crtc_state; 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 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; 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.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_total - m_crtc_state.current_scanline + m_crtc_state.vertical_display_end) :
(m_crtc_state.vertical_display_end - m_crtc_state.current_scanline)); (m_crtc_state.vertical_display_end - m_crtc_state.current_scanline));
const TickCount lines_until_event = m_timers->IsExternalIRQEnabled(HBLANK_TIMER_INDEX) ? const TickCount lines_until_event = g_timers.IsExternalIRQEnabled(HBLANK_TIMER_INDEX) ?
std::min(m_timers->GetTicksUntilIRQ(HBLANK_TIMER_INDEX), lines_until_vblank) : std::min(g_timers.GetTicksUntilIRQ(HBLANK_TIMER_INDEX), lines_until_vblank) :
lines_until_vblank; lines_until_vblank;
const TickCount ticks_until_event = const TickCount ticks_until_event =
lines_until_event * m_crtc_state.horizontal_total - m_crtc_state.current_tick_in_scanline; 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 old_hblank = m_crtc_state.in_hblank;
const bool new_hblank = m_crtc_state.current_tick_in_scanline < m_crtc_state.horizontal_display_start || 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.current_tick_in_scanline >= m_crtc_state.horizontal_display_end;
if (!old_hblank && new_hblank && m_timers->IsUsingExternalClock(HBLANK_TIMER_INDEX)) if (!old_hblank && new_hblank && g_timers.IsUsingExternalClock(HBLANK_TIMER_INDEX))
m_timers->AddTicks(HBLANK_TIMER_INDEX, 1); g_timers.AddTicks(HBLANK_TIMER_INDEX, 1);
UpdateCRTCTickEvent(); UpdateCRTCTickEvent();
return; 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 || 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.current_tick_in_scanline >= m_crtc_state.horizontal_display_end;
m_crtc_state.in_hblank = new_hblank; 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); 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) while (lines_to_draw > 0)
@ -734,7 +728,7 @@ void GPU::CRTCTickEvent(TickCount ticks)
if (prev_scanline < m_crtc_state.vertical_display_start && if (prev_scanline < m_crtc_state.vertical_display_start &&
m_crtc_state.current_scanline >= m_crtc_state.vertical_display_end) 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; m_crtc_state.in_vblank = false;
} }
@ -745,12 +739,12 @@ void GPU::CRTCTickEvent(TickCount ticks)
if (new_vblank) if (new_vblank)
{ {
Log_DebugPrintf("Now in v-blank"); 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 // flush any pending draws and "scan out" the image
FlushRender(); FlushRender();
UpdateDisplay(); UpdateDisplay();
m_system->IncrementFrameNumber(); System::FrameDone();
// switch fields early. this is needed so we draw to the correct one. // switch fields early. this is needed so we draw to the correct one.
if (m_GPUSTAT.vertical_interlace) if (m_GPUSTAT.vertical_interlace)
@ -759,7 +753,7 @@ void GPU::CRTCTickEvent(TickCount ticks)
m_crtc_state.interlaced_field = 0; 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; 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; 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); Log_DebugPrintf("Display address start <- 0x%08X", m_crtc_state.regs.display_address_start);
m_system->IncrementInternalFrameNumber(); System::IncrementInternalFrameNumber();
UpdateCRTCDisplayParameters(); UpdateCRTCDisplayParameters();
} }
break; break;
@ -1339,7 +1333,7 @@ void GPU::DrawDebugStateWindow()
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x; const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
ImGui::SetNextWindowSize(ImVec2(450.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver); 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(); ImGui::End();
return; return;

View file

@ -15,10 +15,7 @@ class StateWrapper;
class HostDisplay; class HostDisplay;
class System;
class TimingEvent; class TimingEvent;
class DMA;
class InterruptController;
class Timers; class Timers;
class GPU class GPU
@ -122,8 +119,7 @@ public:
virtual bool IsHardwareRenderer() const = 0; virtual bool IsHardwareRenderer() const = 0;
virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma, virtual bool Initialize(HostDisplay* host_display);
InterruptController* interrupt_controller, Timers* timers);
virtual void Reset(); virtual void Reset();
virtual bool DoState(StateWrapper& sw); virtual bool DoState(StateWrapper& sw);
@ -452,10 +448,6 @@ protected:
} }
HostDisplay* m_host_display = nullptr; 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_crtc_tick_event;
std::unique_ptr<TimingEvent> m_command_tick_event; std::unique_ptr<TimingEvent> m_command_tick_event;
@ -757,3 +749,5 @@ private:
}; };
IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(GPU::TextureMode); IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(GPU::TextureMode);
extern std::unique_ptr<GPU> g_gpu;

View file

@ -205,7 +205,7 @@ bool GPU::HandleInterruptRequestCommand()
if (!m_GPUSTAT.interrupt_request) if (!m_GPUSTAT.interrupt_request)
{ {
m_GPUSTAT.interrupt_request = true; m_GPUSTAT.interrupt_request = true;
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::GPU); g_interrupt_controller.InterruptRequest(InterruptController::IRQ::GPU);
} }
m_fifo.RemoveOne(); m_fifo.RemoveOne();
@ -496,7 +496,7 @@ bool GPU::HandleCopyRectangleCPUToVRAMCommand()
void GPU::FinishVRAMWrite() 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(), 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, 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 // ensure VRAM shadow is up to date
ReadVRAM(m_vram_transfer.x, m_vram_transfer.y, m_vram_transfer.width, m_vram_transfer.height); 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(), 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, m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * VRAM_WIDTH,

View file

@ -8,33 +8,25 @@
#include <sstream> #include <sstream>
Log_SetChannel(GPU_HW); Log_SetChannel(GPU_HW);
GPU_HW::GPU_HW() : GPU() GPU_HW::GPU_HW() : GPU() { m_vram_ptr = m_vram_shadow.data(); }
{
m_vram_ptr = m_vram_shadow.data();
}
GPU_HW::~GPU_HW() = default; GPU_HW::~GPU_HW() = default;
bool GPU_HW::IsHardwareRenderer() const bool GPU_HW::IsHardwareRenderer() const { return true; }
{
return true;
}
bool GPU_HW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, bool GPU_HW::Initialize(HostDisplay* host_display)
Timers* timers)
{ {
if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers)) if (!GPU::Initialize(host_display))
return false; return false;
const Settings& settings = m_system->GetSettings(); m_resolution_scale = g_settings.gpu_resolution_scale;
m_resolution_scale = settings.gpu_resolution_scale;
m_render_api = host_display->GetRenderAPI(); m_render_api = host_display->GetRenderAPI();
m_true_color = settings.gpu_true_color; m_true_color = g_settings.gpu_true_color;
m_scaled_dithering = settings.gpu_scaled_dithering; m_scaled_dithering = g_settings.gpu_scaled_dithering;
m_texture_filtering = settings.gpu_texture_filtering; m_texture_filtering = g_settings.gpu_texture_filtering;
if (m_resolution_scale < 1 || m_resolution_scale > m_max_resolution_scale) 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.", g_host_interface->AddFormattedOSDMessage(5.0f, "Invalid resolution scale %ux specified. Maximum is %u.",
m_resolution_scale, m_max_resolution_scale); m_resolution_scale, m_max_resolution_scale);
m_resolution_scale = std::clamp<u32>(m_resolution_scale, 1u, 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(); GPU::UpdateSettings();
const Settings& settings = m_system->GetSettings(); m_resolution_scale = std::clamp<u32>(g_settings.gpu_resolution_scale, 1, m_max_resolution_scale);
m_resolution_scale = std::clamp<u32>(settings.gpu_resolution_scale, 1, m_max_resolution_scale); m_true_color = g_settings.gpu_true_color;
m_true_color = settings.gpu_true_color; m_scaled_dithering = g_settings.gpu_scaled_dithering;
m_scaled_dithering = settings.gpu_scaled_dithering; m_texture_filtering = g_settings.gpu_texture_filtering;
m_texture_filtering = settings.gpu_texture_filtering;
PrintSettingsToLog(); PrintSettingsToLog();
} }

View file

@ -37,8 +37,7 @@ public:
virtual bool IsHardwareRenderer() const override; virtual bool IsHardwareRenderer() const override;
virtual bool Initialize(HostDisplay* host_display, System* system, DMA* dma, virtual bool Initialize(HostDisplay* host_display) override;
InterruptController* interrupt_controller, Timers* timers) override;
virtual void Reset() override; virtual void Reset() override;
virtual bool DoState(StateWrapper& sw) override; virtual bool DoState(StateWrapper& sw) override;
virtual void UpdateSettings() override; virtual void UpdateSettings() override;

View file

@ -19,8 +19,7 @@ GPU_HW_D3D11::~GPU_HW_D3D11()
} }
} }
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dma, bool GPU_HW_D3D11::Initialize(HostDisplay* host_display)
InterruptController* interrupt_controller, Timers* timers)
{ {
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11) if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
{ {
@ -30,7 +29,7 @@ bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dm
SetCapabilities(); SetCapabilities();
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers)) if (!GPU_HW::Initialize(host_display))
return false; return false;
m_device = static_cast<ID3D11Device*>(host_display->GetRenderDevice()); 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) if (!m_device || !m_context)
return false; return false;
m_shader_cache.Open(system->GetHostInterface()->GetShaderCacheBasePath(), m_device->GetFeatureLevel(), m_shader_cache.Open(g_host_interface->GetShaderCacheBasePath(), m_device->GetFeatureLevel(),
system->GetSettings().gpu_use_debug_device); g_settings.gpu_use_debug_device);
if (!CreateFramebuffer()) 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, 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_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_screen_quad_vertex_shader =
m_shader_cache.GetVertexShader(m_device.Get(), shadergen.GenerateScreenQuadVertexShader()); m_shader_cache.GetVertexShader(m_device.Get(), shadergen.GenerateScreenQuadVertexShader());
@ -572,7 +571,7 @@ void GPU_HW_D3D11::UpdateDisplay()
{ {
GPU_HW::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(), 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()); 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
@ -789,7 +788,4 @@ void GPU_HW_D3D11::UpdateDepthBufferFromMaskBit()
RestoreGraphicsAPIState(); RestoreGraphicsAPIState();
} }
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer() std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer() { return std::make_unique<GPU_HW_D3D11>(); }
{
return std::make_unique<GPU_HW_D3D11>();
}

View file

@ -19,8 +19,7 @@ public:
GPU_HW_D3D11(); GPU_HW_D3D11();
~GPU_HW_D3D11() override; ~GPU_HW_D3D11() override;
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, bool Initialize(HostDisplay* host_display) override;
Timers* timers) override;
void Reset() override; void Reset() override;
void ResetGraphicsAPIState() override; void ResetGraphicsAPIState() override;

View file

@ -27,8 +27,7 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
} }
} }
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* dma, bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display)
InterruptController* interrupt_controller, Timers* timers)
{ {
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL && if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL &&
host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGLES) 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); 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; return false;
if (!CreateFramebuffer()) 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, 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_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++) for (u32 render_mode = 0; render_mode < 4; render_mode++)
{ {
@ -580,7 +579,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
{ {
GPU_HW::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_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, m_vram_texture.GetWidth(), static_cast<s32>(m_vram_texture.GetHeight()), 0,

View file

@ -15,8 +15,7 @@ public:
GPU_HW_OpenGL(); GPU_HW_OpenGL();
~GPU_HW_OpenGL() override; ~GPU_HW_OpenGL() override;
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, bool Initialize(HostDisplay* host_display) override;
Timers* timers) override;
void Reset() override; void Reset() override;
void ResetGraphicsAPIState() override; void ResetGraphicsAPIState() override;

View file

@ -25,8 +25,7 @@ GPU_HW_Vulkan::~GPU_HW_Vulkan()
DestroyResources(); DestroyResources();
} }
bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display, System* system, DMA* dma, bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
InterruptController* interrupt_controller, Timers* timers)
{ {
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::Vulkan) 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); Assert(g_vulkan_shader_cache);
SetCapabilities(); SetCapabilities();
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers)) if (!GPU_HW::Initialize(host_display))
return false; return false;
if (!CreatePipelineLayouts()) if (!CreatePipelineLayouts())
@ -570,7 +569,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
{VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST}}; {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}}; 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(); VkDevice device = g_vulkan_context->GetDevice();
VkPipelineCache pipeline_cache = g_vulkan_shader_cache->GetPipelineCache(); VkPipelineCache pipeline_cache = g_vulkan_shader_cache->GetPipelineCache();
@ -941,7 +940,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
{ {
GPU_HW::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_host_display->SetDisplayTexture(&m_vram_texture, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 0, 0,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight()); m_vram_texture.GetWidth(), m_vram_texture.GetHeight());

View file

@ -14,8 +14,7 @@ public:
GPU_HW_Vulkan(); GPU_HW_Vulkan();
~GPU_HW_Vulkan() override; ~GPU_HW_Vulkan() override;
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, bool Initialize(HostDisplay* host_display) override;
Timers* timers) override;
void Reset() override; void Reset() override;
void ResetGraphicsAPIState() override; void ResetGraphicsAPIState() override;

View file

@ -23,10 +23,9 @@ bool GPU_SW::IsHardwareRenderer() const
return false; return false;
} }
bool GPU_SW::Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, bool GPU_SW::Initialize(HostDisplay* host_display)
Timers* timers)
{ {
if (!GPU::Initialize(host_display, system, dma, interrupt_controller, timers)) if (!GPU::Initialize(host_display))
return false; return false;
m_display_texture = host_display->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, nullptr, 0, true); m_display_texture = host_display->CreateTexture(VRAM_WIDTH, VRAM_HEIGHT, nullptr, 0, true);
@ -148,7 +147,7 @@ void GPU_SW::UpdateDisplay()
// fill display texture // fill display texture
m_display_texture_buffer.resize(VRAM_WIDTH * VRAM_HEIGHT); m_display_texture_buffer.resize(VRAM_WIDTH * VRAM_HEIGHT);
if (!m_system->GetSettings().debugging.show_vram) if (!g_settings.debugging.show_vram)
{ {
if (IsDisplayDisabled()) if (IsDisplayDisabled())
{ {

View file

@ -14,8 +14,7 @@ public:
bool IsHardwareRenderer() const override; bool IsHardwareRenderer() const override;
bool Initialize(HostDisplay* host_display, System* system, DMA* dma, InterruptController* interrupt_controller, bool Initialize(HostDisplay* host_display) override;
Timers* timers) override;
void Reset() override; void Reset() override;
u16 GetPixel(u32 x, u32 y) const { return m_vram[VRAM_WIDTH * y + x]; } u16 GetPixel(u32 x, u32 y) const { return m_vram[VRAM_WIDTH * y + x]; }

File diff suppressed because it is too large Load diff

View file

@ -1,119 +1,24 @@
#pragma once #pragma once
#include "common/state_wrapper.h"
#include "gte_types.h" #include "gte_types.h"
namespace CPU { class StateWrapper;
class Core;
namespace Recompiler {
class CodeGenerator;
}
} // namespace CPU
namespace GTE { namespace GTE {
class Core
{
public:
friend CPU::Core;
friend CPU::Recompiler::CodeGenerator;
Core();
~Core();
ALWAYS_INLINE void SetWidescreenHack(bool enabled) { m_widescreen_hack = enabled; }
void Initialize(); void Initialize();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
// control registers are offset by +32 // control registers are offset by +32
u32 ReadRegister(u32 index) const; u32 ReadRegister(u32 index);
void WriteRegister(u32 index, u32 value); void WriteRegister(u32 index, u32 value);
void ExecuteInstruction(Instruction inst); // use with care, direct register access
u32* GetRegisterPtr(u32 index);
private: void ExecuteInstruction(u32 inst_bits);
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. using InstructionImpl = void (*)(Instruction);
template<u32 index> InstructionImpl GetInstructionImpl(u32 inst_bits);
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"
} // namespace GTE } // namespace GTE

View file

@ -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);
}

View file

@ -3,9 +3,13 @@
#include "types.h" #include "types.h"
namespace GTE { namespace GTE {
static constexpr u32 NUM_DATA_REGS = 32;
static constexpr u32 NUM_CONTROL_REGS = 32; enum : u32
static constexpr u32 NUM_REGS = NUM_DATA_REGS + NUM_CONTROL_REGS; {
NUM_DATA_REGS = 32,
NUM_CONTROL_REGS = 32,
NUM_REGS = NUM_DATA_REGS + NUM_CONTROL_REGS
};
union FLAGS union FLAGS
{ {

View file

@ -9,8 +9,10 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "controller.h" #include "controller.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "cpu_code_cache.h"
#include "dma.h" #include "dma.h"
#include "gpu.h" #include "gpu.h"
#include "gte.h"
#include "host_display.h" #include "host_display.h"
#include "save_state_version.h" #include "save_state_version.h"
#include "system.h" #include "system.h"
@ -21,8 +23,13 @@
#include <stdlib.h> #include <stdlib.h>
Log_SetChannel(HostInterface); Log_SetChannel(HostInterface);
HostInterface* g_host_interface;
HostInterface::HostInterface() HostInterface::HostInterface()
{ {
Assert(!g_host_interface);
g_host_interface = this;
// we can get the program directory at construction time // we can get the program directory at construction time
const std::string program_path = FileSystem::GetProgramPath(); const std::string program_path = FileSystem::GetProgramPath();
m_program_directory = FileSystem::GetPathDirectory(program_path.c_str()); m_program_directory = FileSystem::GetPathDirectory(program_path.c_str());
@ -31,7 +38,9 @@ HostInterface::HostInterface()
HostInterface::~HostInterface() HostInterface::~HostInterface()
{ {
// system should be shut down prior to the destructor // 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() bool HostInterface::Initialize()
@ -44,20 +53,20 @@ void HostInterface::Shutdown() {}
void HostInterface::CreateAudioStream() void HostInterface::CreateAudioStream()
{ {
Log_InfoPrintf("Creating '%s' audio stream, sample rate = %u, channels = %u, buffer size = %u", 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, Settings::GetAudioBackendName(g_settings.audio_backend), AUDIO_SAMPLE_RATE, AUDIO_CHANNELS,
m_settings.audio_buffer_size); 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."); ReportFormattedError("Failed to create or configure audio stream, falling back to null output.");
m_audio_stream.reset(); m_audio_stream.reset();
m_audio_stream = AudioStream::CreateNullAudioStream(); 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) bool HostInterface::BootSystem(const SystemBootParameters& parameters)
@ -78,14 +87,13 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters)
} }
// set host display settings // set host display settings
m_display->SetDisplayLinearFiltering(m_settings.display_linear_filtering); m_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering);
m_display->SetDisplayIntegerScaling(m_settings.display_integer_scaling); m_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
// create the audio stream. this will never fail, since we'll just fall back to null // create the audio stream. this will never fail, since we'll just fall back to null
CreateAudioStream(); CreateAudioStream();
m_system = System::Create(this); if (!System::Boot(parameters))
if (!m_system->Boot(parameters))
{ {
ReportFormattedError("System failed to boot. The log may contain more information."); ReportFormattedError("System failed to boot. The log may contain more information.");
DestroySystem(); DestroySystem();
@ -101,25 +109,22 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters)
void HostInterface::ResetSystem() void HostInterface::ResetSystem()
{ {
m_system->Reset(); System::Reset();
m_system->ResetPerformanceCounters(); System::ResetPerformanceCounters();
AddOSDMessage("System reset."); AddOSDMessage("System reset.");
} }
void HostInterface::PowerOffSystem() void HostInterface::PowerOffSystem()
{ {
if (!m_system)
return;
DestroySystem(); DestroySystem();
} }
void HostInterface::DestroySystem() void HostInterface::DestroySystem()
{ {
if (!m_system) if (System::IsShutdown())
return; return;
m_system.reset(); System::Shutdown();
m_audio_stream.reset(); m_audio_stream.reset();
UpdateSoftwareCursor(); UpdateSoftwareCursor();
ReleaseHostDisplay(); ReleaseHostDisplay();
@ -210,50 +215,50 @@ std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
} while (0) } while (0)
// Try the configured image. // 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. // Try searching in the same folder for other region's images.
switch (region) switch (region)
{ {
case ConsoleRegion::NTSC_J: case ConsoleRegion::NTSC_J:
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph3000.bin", false, false)); TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(), "ps-40j.bin", false, false));
break; break;
case ConsoleRegion::NTSC_U: case ConsoleRegion::NTSC_U:
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph1001.bin", false, false)); TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(), "ps-41a.bin", false, false));
break; break;
case ConsoleRegion::PAL: case ConsoleRegion::PAL:
TRY_FILENAME(FileSystem::BuildPathRelativeToFile(m_settings.bios_path.c_str(), "scph1002.bin", false, false)); TRY_FILENAME(FileSystem::BuildPathRelativeToFile(g_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(g_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(g_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(g_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(g_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(g_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(g_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(g_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(), "ps-41e.bin", false, false));
break; break;
default: default:
@ -266,8 +271,8 @@ std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
// Fall back to the default image. // Fall back to the default image.
Log_WarningPrintf("No suitable BIOS image for region %s could be located, using configured image '%s'. This may " Log_WarningPrintf("No suitable BIOS image for region %s could be located, using configured image '%s'. This may "
"result in instability.", "result in instability.",
Settings::GetConsoleRegionName(region), m_settings.bios_path.c_str()); Settings::GetConsoleRegionName(region), g_settings.bios_path.c_str());
return BIOS::LoadImageFromFile(m_settings.bios_path); return BIOS::LoadImageFromFile(g_settings.bios_path);
} }
bool HostInterface::LoadState(const char* filename) bool HostInterface::LoadState(const char* filename)
@ -278,12 +283,12 @@ bool HostInterface::LoadState(const char* filename)
AddFormattedOSDMessage(2.0f, "Loading state from '%s'...", 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); ReportFormattedError("Loading state from '%s' failed. Resetting.", filename);
m_system->Reset(); ResetSystem();
return false; return false;
} }
} }
@ -295,7 +300,7 @@ bool HostInterface::LoadState(const char* filename)
return false; return false;
} }
m_system->ResetPerformanceCounters(); System::ResetPerformanceCounters();
return true; return true;
} }
@ -307,7 +312,7 @@ bool HostInterface::SaveState(const char* filename)
if (!stream) if (!stream)
return false; return false;
const bool result = m_system->SaveState(stream.get()); const bool result = System::SaveState(stream.get());
if (!result) if (!result)
{ {
ReportFormattedError("Saving state to '%s' failed.", filename); ReportFormattedError("Saving state to '%s' failed.", filename);
@ -422,89 +427,86 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
void HostInterface::LoadSettings(SettingsInterface& si) void HostInterface::LoadSettings(SettingsInterface& si)
{ {
m_settings.Load(si); g_settings.Load(si);
} }
void HostInterface::SaveSettings(SettingsInterface& si) void HostInterface::SaveSettings(SettingsInterface& si)
{ {
m_settings.Save(si); g_settings.Save(si);
} }
void HostInterface::CheckForSettingsChanges(const Settings& old_settings) void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
{ {
if (m_system) if (!System::IsShutdown())
{ {
if (m_settings.gpu_renderer != old_settings.gpu_renderer || if (g_settings.gpu_renderer != old_settings.gpu_renderer ||
m_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device) 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), ReportFormattedMessage("Switching to %s%s GPU renderer.", Settings::GetRendererName(g_settings.gpu_renderer),
m_settings.gpu_use_debug_device ? " (debug)" : ""); g_settings.gpu_use_debug_device ? " (debug)" : "");
RecreateSystem(); RecreateSystem();
} }
if (m_settings.audio_backend != old_settings.audio_backend || if (g_settings.audio_backend != old_settings.audio_backend ||
m_settings.audio_buffer_size != old_settings.audio_buffer_size) 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.", ReportFormattedMessage("Switching to %s audio backend.",
Settings::GetAudioBackendName(m_settings.audio_backend)); Settings::GetAudioBackendName(g_settings.audio_backend));
DebugAssert(m_audio_stream); DebugAssert(m_audio_stream);
m_audio_stream.reset(); m_audio_stream.reset();
CreateAudioStream(); CreateAudioStream();
m_audio_stream->PauseOutput(false); m_audio_stream->PauseOutput(System::IsPaused());
} }
if (m_settings.emulation_speed != old_settings.emulation_speed) if (g_settings.emulation_speed != old_settings.emulation_speed)
m_system->UpdateThrottlePeriod(); 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.", ReportFormattedMessage("Switching to %s CPU execution mode.",
Settings::GetCPUExecutionModeName(m_settings.cpu_execution_mode)); Settings::GetCPUExecutionModeName(g_settings.cpu_execution_mode));
m_system->SetCPUExecutionMode(m_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 || if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale ||
m_settings.gpu_fifo_size != old_settings.gpu_fifo_size || g_settings.gpu_fifo_size != old_settings.gpu_fifo_size ||
m_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead || g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead ||
m_settings.gpu_true_color != old_settings.gpu_true_color || g_settings.gpu_true_color != old_settings.gpu_true_color ||
m_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering || g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
m_settings.gpu_texture_filtering != old_settings.gpu_texture_filtering || g_settings.gpu_texture_filtering != old_settings.gpu_texture_filtering ||
m_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing || g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
m_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings || g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
m_settings.display_crop_mode != old_settings.display_crop_mode || g_settings.display_crop_mode != old_settings.display_crop_mode ||
m_settings.display_aspect_ratio != old_settings.display_aspect_ratio) 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) if (g_settings.cdrom_read_thread != old_settings.cdrom_read_thread)
m_system->GetCDROM()->SetUseReadThread(m_settings.cdrom_read_thread); g_cdrom.SetUseReadThread(g_settings.cdrom_read_thread);
if (m_settings.memory_card_types != old_settings.memory_card_types || if (g_settings.memory_card_types != old_settings.memory_card_types ||
m_settings.memory_card_paths != old_settings.memory_card_paths) 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); g_dma.SetMaxSliceTicks(g_settings.dma_max_slice_ticks);
m_system->GetDMA()->SetHaltTicks(m_settings.dma_halt_ticks); g_dma.SetHaltTicks(g_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);
} }
bool controllers_updated = false; bool controllers_updated = false;
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) 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(); System::UpdateControllers();
m_system->ResetControllers(); System::ResetControllers();
UpdateSoftwareCursor(); UpdateSoftwareCursor();
controllers_updated = true; controllers_updated = true;
} }
@ -512,18 +514,18 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
OnControllerTypeChanged(i); OnControllerTypeChanged(i);
} }
if (m_system && !controllers_updated) if (!System::IsShutdown() && !controllers_updated)
{ {
m_system->UpdateControllerSettings(); System::UpdateControllerSettings();
UpdateSoftwareCursor(); UpdateSoftwareCursor();
} }
} }
if (m_display && m_settings.display_linear_filtering != old_settings.display_linear_filtering) if (m_display && g_settings.display_linear_filtering != old_settings.display_linear_filtering)
m_display->SetDisplayLinearFiltering(m_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) if (m_display && g_settings.display_integer_scaling != old_settings.display_integer_scaling)
m_display->SetDisplayIntegerScaling(m_settings.display_integer_scaling); m_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
} }
void HostInterface::SetUserDirectoryToProgramDirectory() void HostInterface::SetUserDirectoryToProgramDirectory()
@ -620,35 +622,35 @@ float HostInterface::GetFloatSettingValue(const char* section, const char* key,
void HostInterface::ToggleSoftwareRendering() void HostInterface::ToggleSoftwareRendering()
{ {
if (!m_system || m_settings.gpu_renderer == GPURenderer::Software) if (System::IsShutdown() || g_settings.gpu_renderer == GPURenderer::Software)
return; return;
const GPURenderer new_renderer = 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)); 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) void HostInterface::ModifyResolutionScale(s32 increment)
{ {
const u32 new_resolution_scale = std::clamp<u32>( 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); static_cast<u32>(static_cast<s32>(g_settings.gpu_resolution_scale) + increment), 1, GPU::MAX_RESOLUTION_SCALE);
if (new_resolution_scale == m_settings.gpu_resolution_scale) if (new_resolution_scale == g_settings.gpu_resolution_scale)
return; return;
m_settings.gpu_resolution_scale = new_resolution_scale; g_settings.gpu_resolution_scale = new_resolution_scale;
AddFormattedOSDMessage(2.0f, "Resolution scale set to %ux (%ux%u)", m_settings.gpu_resolution_scale, AddFormattedOSDMessage(2.0f, "Resolution scale set to %ux (%ux%u)", g_settings.gpu_resolution_scale,
GPU::VRAM_WIDTH * m_settings.gpu_resolution_scale, GPU::VRAM_WIDTH * g_settings.gpu_resolution_scale,
GPU::VRAM_HEIGHT * m_settings.gpu_resolution_scale); GPU::VRAM_HEIGHT * g_settings.gpu_resolution_scale);
if (m_system) if (!System::IsShutdown())
m_system->GetGPU()->UpdateSettings(); g_gpu->UpdateSettings();
} }
void HostInterface::UpdateSoftwareCursor() void HostInterface::UpdateSoftwareCursor()
{ {
if (!m_system) if (System::IsShutdown())
{ {
m_display->ClearSoftwareCursor(); m_display->ClearSoftwareCursor();
return; return;
@ -659,7 +661,7 @@ void HostInterface::UpdateSoftwareCursor()
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) 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)) if (controller && controller->GetSoftwareCursor(&image, &image_scale))
break; break;
} }
@ -677,8 +679,10 @@ void HostInterface::UpdateSoftwareCursor()
void HostInterface::RecreateSystem() void HostInterface::RecreateSystem()
{ {
Assert(!System::IsShutdown());
std::unique_ptr<ByteStream> stream = ByteStream_CreateGrowableMemoryStream(nullptr, 8 * 1024); 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."); ReportError("Failed to save state before system recreation. Shutting down.");
DestroySystem(); DestroySystem();
@ -695,7 +699,7 @@ void HostInterface::RecreateSystem()
return; return;
} }
m_system->ResetPerformanceCounters(); System::ResetPerformanceCounters();
} }
void HostInterface::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/, void HostInterface::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, int progress_max /*= -1*/,

View file

@ -20,13 +20,10 @@ class CDImage;
class HostDisplay; class HostDisplay;
class GameList; class GameList;
class System;
struct SystemBootParameters; struct SystemBootParameters;
class HostInterface class HostInterface
{ {
friend System;
public: public:
enum : u32 enum : u32
{ {
@ -44,12 +41,6 @@ public:
/// Access to host audio stream. /// Access to host audio stream.
ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); } 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. /// Initializes the emulator frontend.
virtual bool Initialize(); virtual bool Initialize();
@ -118,6 +109,12 @@ public:
/// Returns a float setting from the configuration. /// Returns a float setting from the configuration.
virtual float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f); 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: protected:
virtual bool AcquireHostDisplay() = 0; virtual bool AcquireHostDisplay() = 0;
virtual void ReleaseHostDisplay() = 0; virtual void ReleaseHostDisplay() = 0;
@ -125,9 +122,7 @@ protected:
virtual void OnSystemCreated(); virtual void OnSystemCreated();
virtual void OnSystemDestroyed(); virtual void OnSystemDestroyed();
virtual void OnSystemPerformanceCountersUpdated();
virtual void OnSystemStateSaved(bool global, s32 slot); virtual void OnSystemStateSaved(bool global, s32 slot);
virtual void OnRunningGameChanged();
virtual void OnControllerTypeChanged(u32 slot); virtual void OnControllerTypeChanged(u32 slot);
/// Restores all settings to defaults. /// Restores all settings to defaults.
@ -148,9 +143,6 @@ protected:
/// Sets the user directory to the program directory, i.e. "portable mode". /// Sets the user directory to the program directory, i.e. "portable mode".
void SetUserDirectoryToProgramDirectory(); 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. /// Quick switch between software and hardware rendering.
void ToggleSoftwareRendering(); void ToggleSoftwareRendering();
@ -165,8 +157,8 @@ protected:
std::unique_ptr<HostDisplay> m_display; std::unique_ptr<HostDisplay> m_display;
std::unique_ptr<AudioStream> m_audio_stream; std::unique_ptr<AudioStream> m_audio_stream;
std::unique_ptr<System> m_system;
Settings m_settings;
std::string m_program_directory; std::string m_program_directory;
std::string m_user_directory; std::string m_user_directory;
}; };
extern HostInterface* g_host_interface;

View file

@ -2,15 +2,9 @@
#include "common/log.h" #include "common/log.h"
Log_SetChannel(HostInterfaceProgressCallback); Log_SetChannel(HostInterfaceProgressCallback);
HostInterfaceProgressCallback::HostInterfaceProgressCallback(HostInterface* intf) HostInterfaceProgressCallback::HostInterfaceProgressCallback() : BaseProgressCallback() {}
: BaseProgressCallback(), m_host_interface(intf)
{
}
void HostInterfaceProgressCallback::PushState() void HostInterfaceProgressCallback::PushState() { BaseProgressCallback::PushState(); }
{
BaseProgressCallback::PushState();
}
void HostInterfaceProgressCallback::PopState() void HostInterfaceProgressCallback::PopState()
{ {
@ -58,38 +52,23 @@ void HostInterfaceProgressCallback::Redraw(bool force)
return; return;
m_last_progress_percent = percent; 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)); static_cast<int>(m_progress_value));
} }
void HostInterfaceProgressCallback::DisplayError(const char* message) void HostInterfaceProgressCallback::DisplayError(const char* message) { Log_ErrorPrint(message); }
{
Log_ErrorPrint(message);
}
void HostInterfaceProgressCallback::DisplayWarning(const char* message) void HostInterfaceProgressCallback::DisplayWarning(const char* message) { Log_WarningPrint(message); }
{
Log_WarningPrint(message);
}
void HostInterfaceProgressCallback::DisplayInformation(const char* message) void HostInterfaceProgressCallback::DisplayInformation(const char* message) { Log_InfoPrint(message); }
{
Log_InfoPrint(message);
}
void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message) void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message) { Log_DevPrint(message); }
{
Log_DevPrint(message);
}
void HostInterfaceProgressCallback::ModalError(const char* message) void HostInterfaceProgressCallback::ModalError(const char* message) { g_host_interface->ReportError(message); }
{
m_host_interface->ReportError(message);
}
bool HostInterfaceProgressCallback::ModalConfirmation(const char* 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, ...) u32 HostInterfaceProgressCallback::ModalPrompt(const char* message, u32 num_options, ...)

View file

@ -5,7 +5,7 @@
class HostInterfaceProgressCallback : public BaseProgressCallback class HostInterfaceProgressCallback : public BaseProgressCallback
{ {
public: public:
HostInterfaceProgressCallback(HostInterface* intf); HostInterfaceProgressCallback();
void PushState() override; void PushState() override;
void PopState() override; void PopState() override;
@ -27,6 +27,5 @@ public:
private: private:
void Redraw(bool force); void Redraw(bool force);
HostInterface* m_host_interface;
int m_last_progress_percent = -1; int m_last_progress_percent = -1;
}; };

View file

@ -4,15 +4,19 @@
#include "cpu_core.h" #include "cpu_core.h"
Log_SetChannel(InterruptController); Log_SetChannel(InterruptController);
InterruptController g_interrupt_controller;
InterruptController::InterruptController() = default; InterruptController::InterruptController() = default;
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() void InterruptController::Reset()
{ {
m_interrupt_status_register = 0; m_interrupt_status_register = 0;
@ -82,7 +86,7 @@ void InterruptController::UpdateCPUInterruptRequest()
{ {
// external interrupts set bit 10 only? // external interrupts set bit 10 only?
if ((m_interrupt_status_register & m_interrupt_mask_register) != 0) if ((m_interrupt_status_register & m_interrupt_mask_register) != 0)
m_cpu->SetExternalInterrupt(2); CPU::SetExternalInterrupt(2);
else else
m_cpu->ClearExternalInterrupt(2); CPU::ClearExternalInterrupt(2);
} }

View file

@ -3,12 +3,7 @@
class StateWrapper; class StateWrapper;
namespace CPU class InterruptController final
{
class Core;
}
class InterruptController
{ {
public: public:
static constexpr u32 NUM_IRQS = 11; static constexpr u32 NUM_IRQS = 11;
@ -32,12 +27,13 @@ public:
InterruptController(); InterruptController();
~InterruptController(); ~InterruptController();
void Initialize(CPU::Core* cpu); void Initialize();
void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
// Should mirror CPU state. // 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. // Interupts are edge-triggered, so if it is masked when TriggerInterrupt() is called, it will be lost.
void InterruptRequest(IRQ irq); void InterruptRequest(IRQ irq);
@ -52,9 +48,8 @@ private:
void UpdateCPUInterruptRequest(); void UpdateCPUInterruptRequest();
CPU::Core* m_cpu;
u32 m_interrupt_status_register = 0; u32 m_interrupt_status_register = 0;
u32 m_interrupt_mask_register = DEFAULT_INTERRUPT_MASK; u32 m_interrupt_mask_register = DEFAULT_INTERRUPT_MASK;
}; };
extern InterruptController g_interrupt_controller;

View file

@ -1,26 +1,35 @@
#include "mdec.h" #include "mdec.h"
#include "common/log.h" #include "common/log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "cpu_core.h"
#include "dma.h" #include "dma.h"
#include "interrupt_controller.h" #include "interrupt_controller.h"
#include "system.h" #include "system.h"
#include <imgui.h> #include <imgui.h>
Log_SetChannel(MDEC); Log_SetChannel(MDEC);
MDEC g_mdec;
MDEC::MDEC() = default; MDEC::MDEC() = default;
MDEC::~MDEC() = default; MDEC::~MDEC() = default;
void MDEC::Initialize(System* system, DMA* dma) void MDEC::Initialize()
{ {
m_system = system; m_block_copy_out_event = TimingEvents::CreateTimingEvent("MDEC Block Copy Out", TICKS_PER_BLOCK, TICKS_PER_BLOCK,
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); std::bind(&MDEC::CopyOutBlock, this), false);
m_total_blocks_decoded = 0;
Reset();
}
void MDEC::Shutdown()
{
m_block_copy_out_event.reset();
} }
void MDEC::Reset() void MDEC::Reset()
{ {
m_block_copy_out_event->Deactivate();
SoftReset(); SoftReset();
} }
@ -176,12 +185,12 @@ void MDEC::UpdateStatus()
// we always want data in if it's enabled // 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); 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_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 // 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(); const bool data_out_request = m_enable_dma_out && !m_data_out_fifo.IsEmpty();
m_status.data_out_request = data_out_request; 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() u32 MDEC::ReadDataRegister()
@ -192,7 +201,7 @@ u32 MDEC::ReadDataRegister()
if (HasPendingBlockCopyOut()) if (HasPendingBlockCopyOut())
{ {
Log_DevPrint("MDEC data out FIFO empty on read - stalling CPU"); 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 else
{ {
@ -695,7 +704,7 @@ void MDEC::DrawDebugStateWindow()
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x; const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
ImGui::SetNextWindowSize(ImVec2(300.0f * framebuffer_scale, 350.0f * framebuffer_scale), ImGuiCond_FirstUseEver); 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(); ImGui::End();
return; return;

View file

@ -7,9 +7,7 @@
class StateWrapper; class StateWrapper;
class System;
class TimingEvent; class TimingEvent;
class DMA;
class MDEC class MDEC
{ {
@ -17,7 +15,8 @@ public:
MDEC(); MDEC();
~MDEC(); ~MDEC();
void Initialize(System* system, DMA* dma); void Initialize();
void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -122,9 +121,6 @@ private:
const std::array<s16, 64>& Yblk); const std::array<s16, 64>& Yblk);
void y_to_mono(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 = {}; StatusRegister m_status = {};
bool m_enable_dma_in = false; bool m_enable_dma_in = false;
bool m_enable_dma_out = false; bool m_enable_dma_out = false;
@ -151,3 +147,5 @@ private:
u32 m_total_blocks_decoded = 0; u32 m_total_blocks_decoded = 0;
}; };
extern MDEC g_mdec;

View file

@ -1,20 +1,20 @@
#include "memory_card.h" #include "memory_card.h"
#include "common/byte_stream.h" #include "common/byte_stream.h"
#include "common/file_system.h" #include "common/file_system.h"
#include "common/string_util.h"
#include "common/log.h" #include "common/log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "common/string_util.h"
#include "host_interface.h" #include "host_interface.h"
#include "system.h" #include "system.h"
#include <cstdio> #include <cstdio>
Log_SetChannel(MemoryCard); Log_SetChannel(MemoryCard);
MemoryCard::MemoryCard(System* system) : m_system(system) MemoryCard::MemoryCard()
{ {
m_FLAG.no_write_yet = true; m_FLAG.no_write_yet = true;
m_save_event = m_save_event =
system->CreateTimingEvent("Memory Card Host Flush", SAVE_DELAY_IN_SYSCLK_TICKS, SAVE_DELAY_IN_SYSCLK_TICKS, TimingEvents::CreateTimingEvent("Memory Card Host Flush", SAVE_DELAY_IN_SYSCLK_TICKS, SAVE_DELAY_IN_SYSCLK_TICKS,
std::bind(&MemoryCard::SaveIfChanged, this, true), false); std::bind(&MemoryCard::SaveIfChanged, this, true), false);
} }
@ -237,16 +237,16 @@ bool MemoryCard::Transfer(const u8 data_in, u8* data_out)
return ack; 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(); mc->Format();
return mc; 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; mc->m_filename = filename;
if (!mc->LoadFromFile()) 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(filename.data(), static_cast<u32>(filename.length()));
message.AppendString("' could not be read, formatting."); message.AppendString("' could not be read, formatting.");
Log_ErrorPrint(message); Log_ErrorPrint(message);
system->GetHostInterface()->AddOSDMessage(message.GetCharArray(), 5.0f); g_host_interface->AddOSDMessage(message.GetCharArray(), 5.0f);
mc->Format(); mc->Format();
} }
@ -385,8 +385,7 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str()); Log_InfoPrintf("Saved memory card to '%s'", m_filename.c_str());
if (display_osd_message) if (display_osd_message)
{ {
m_system->GetHostInterface()->AddOSDMessage( g_host_interface->AddOSDMessage(StringUtil::StdStringFromFormat("Saved memory card to '%s'", m_filename.c_str()));
StringUtil::StdStringFromFormat("Saved memory card to '%s'", m_filename.c_str()));
} }
return true; return true;

View file

@ -6,7 +6,6 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
class System;
class TimingEvent; class TimingEvent;
class MemoryCard final class MemoryCard final
@ -19,11 +18,11 @@ public:
NUM_SECTORS = DATA_SIZE / SECTOR_SIZE NUM_SECTORS = DATA_SIZE / SECTOR_SIZE
}; };
MemoryCard(System* system); MemoryCard();
~MemoryCard(); ~MemoryCard();
static std::unique_ptr<MemoryCard> Create(System* system); static std::unique_ptr<MemoryCard> Create();
static std::unique_ptr<MemoryCard> Open(System* system, std::string_view filename); static std::unique_ptr<MemoryCard> Open(std::string_view filename);
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -85,7 +84,6 @@ private:
bool SaveIfChanged(bool display_osd_message); bool SaveIfChanged(bool display_osd_message);
void QueueFileSave(); void QueueFileSave();
System* m_system;
std::unique_ptr<TimingEvent> m_save_event; std::unique_ptr<TimingEvent> m_save_event;
State m_state = State::Idle; State m_state = State::Idle;

View file

@ -10,7 +10,7 @@
#include <array> #include <array>
Log_SetChannel(NamcoGunCon); Log_SetChannel(NamcoGunCon);
NamcoGunCon::NamcoGunCon(System* system) : m_system(system) {} NamcoGunCon::NamcoGunCon() = default;
NamcoGunCon::~NamcoGunCon() = default; NamcoGunCon::~NamcoGunCon() = default;
@ -153,14 +153,13 @@ bool NamcoGunCon::Transfer(const u8 data_in, u8* data_out)
void NamcoGunCon::UpdatePosition() void NamcoGunCon::UpdatePosition()
{ {
// get screen coordinates // 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_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY(); const s32 mouse_y = display->GetMousePositionY();
// are we within the active display area? // are we within the active display area?
u32 tick, line; u32 tick, line;
if (mouse_x < 0 || mouse_y < 0 || if (mouse_x < 0 || mouse_y < 0 || !g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, &tick, &line))
!m_system->GetGPU()->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, &tick, &line))
{ {
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y); Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
m_position_x = 0x01; m_position_x = 0x01;
@ -169,16 +168,16 @@ void NamcoGunCon::UpdatePosition()
} }
// 8MHz units for X = 44100*768*11/7 = 53222400 / 8000000 = 6.6528 // 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_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line); 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, Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
m_position_x); 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) std::optional<s32> NamcoGunCon::StaticGetAxisCodeByName(std::string_view button_name)
@ -234,11 +233,11 @@ Controller::SettingList NamcoGunCon::StaticGetSettings()
return SettingList(settings.begin(), settings.end()); 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) if (path != m_crosshair_image_path)
{ {
m_crosshair_image_path = std::move(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()); 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) bool NamcoGunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale)

View file

@ -15,10 +15,10 @@ public:
Count Count
}; };
NamcoGunCon(System* system); NamcoGunCon();
~NamcoGunCon() override; ~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> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name); static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames(); static AxisList StaticGetAxisNames();
@ -32,7 +32,7 @@ public:
void Reset() override; void Reset() override;
bool DoState(StateWrapper& sw) 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; bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale) override;
void SetAxisState(s32 axis_code, float value) override; void SetAxisState(s32 axis_code, float value) override;
@ -58,7 +58,6 @@ private:
YMSB YMSB
}; };
System* m_system;
Common::RGBA8Image m_crosshair_image; Common::RGBA8Image m_crosshair_image;
std::string m_crosshair_image_path; std::string m_crosshair_image_path;
float m_crosshair_image_scale = 1.0f; float m_crosshair_image_scale = 1.0f;

View file

@ -8,16 +8,28 @@
#include "system.h" #include "system.h"
Log_SetChannel(Pad); Log_SetChannel(Pad);
Pad g_pad;
Pad::Pad() = default; Pad::Pad() = default;
Pad::~Pad() = default; Pad::~Pad() = default;
void Pad::Initialize(System* system, InterruptController* interrupt_controller) void Pad::Initialize()
{ {
m_system = system; m_transfer_event = TimingEvents::CreateTimingEvent(
m_interrupt_controller = interrupt_controller; "Pad Serial Transfer", 1, 1, std::bind(&Pad::TransferEvent, this, std::placeholders::_2), false);
m_transfer_event = system->CreateTimingEvent("Pad Serial Transfer", 1, 1, Reset();
std::bind(&Pad::TransferEvent, this, std::placeholders::_2), false); }
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() void Pad::Reset()
@ -44,27 +56,26 @@ bool Pad::DoState(StateWrapper& sw)
if (controller_type != state_controller_type) 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.", 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(state_controller_type), i + 1u,
Settings::GetControllerTypeName(controller_type)); Settings::GetControllerTypeName(controller_type));
m_controllers[i].reset(); m_controllers[i].reset();
if (state_controller_type != ControllerType::None) 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 else
{ {
m_system->GetHostInterface()->AddFormattedOSDMessage(2.0f, "Ignoring mismatched controller type %s in port %u.", g_host_interface->AddFormattedOSDMessage(2.0f, "Ignoring mismatched controller type %s in port %u.",
Settings::GetControllerTypeName(state_controller_type), Settings::GetControllerTypeName(state_controller_type), i + 1u);
i + 1u);
// we still need to read the save state controller state // we still need to read the save state controller state
if (state_controller_type != ControllerType::None) 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 (dummy_controller)
{ {
if (!sw.DoMarker("Controller") || !dummy_controller->DoState(sw)) 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]); bool card_present = static_cast<bool>(m_memory_cards[i]);
sw.Do(&card_present); 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); 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)) if (!sw.DoMarker("MemoryCard") || !dummy_card.DoState(sw))
return false; return false;
@ -102,13 +113,13 @@ bool Pad::DoState(StateWrapper& sw)
if (card_present && !m_memory_cards[i]) 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); 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]) 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); 2.0f, "Memory card %u present in system but not in save state. Removing card.", i + 1u);
m_memory_cards[i].reset(); m_memory_cards[i].reset();
} }
@ -411,7 +422,7 @@ void Pad::DoACK()
{ {
Log_DebugPrintf("Triggering ACK interrupt"); Log_DebugPrintf("Triggering ACK interrupt");
m_JOY_STAT.INTR = true; m_JOY_STAT.INTR = true;
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::IRQ7); g_interrupt_controller.InterruptRequest(InterruptController::IRQ::IRQ7);
} }
EndTransfer(); EndTransfer();

View file

@ -7,19 +7,18 @@
class StateWrapper; class StateWrapper;
class System;
class TimingEvent; class TimingEvent;
class InterruptController;
class Controller; class Controller;
class MemoryCard; class MemoryCard;
class Pad class Pad final
{ {
public: public:
Pad(); Pad();
~Pad(); ~Pad();
void Initialize(System* system, InterruptController* interrupt_controller); void Initialize();
void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -108,9 +107,6 @@ private:
void EndTransfer(); void EndTransfer();
void ResetDeviceTransferState(); 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<Controller>, NUM_SLOTS> m_controllers;
std::array<std::unique_ptr<MemoryCard>, NUM_SLOTS> m_memory_cards; std::array<std::unique_ptr<MemoryCard>, NUM_SLOTS> m_memory_cards;
@ -129,3 +125,5 @@ private:
bool m_receive_buffer_full = false; bool m_receive_buffer_full = false;
bool m_transmit_buffer_full = false; bool m_transmit_buffer_full = false;
}; };
extern Pad g_pad;

View file

@ -9,10 +9,10 @@
#include <array> #include <array>
Log_SetChannel(PlayStationMouse); 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_x = g_host_interface->GetDisplay()->GetMousePositionX();
m_last_host_position_y = system->GetHostInterface()->GetDisplay()->GetMousePositionY(); m_last_host_position_y = g_host_interface->GetDisplay()->GetMousePositionY();
} }
PlayStationMouse::~PlayStationMouse() = default; PlayStationMouse::~PlayStationMouse() = default;
@ -142,7 +142,7 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out)
void PlayStationMouse::UpdatePosition() void PlayStationMouse::UpdatePosition()
{ {
// get screen coordinates // 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_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY(); const s32 mouse_y = display->GetMousePositionY();
const s32 delta_x = mouse_x - m_last_host_position_x; 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())); 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) std::optional<s32> PlayStationMouse::StaticGetAxisCodeByName(std::string_view button_name)

View file

@ -14,10 +14,10 @@ public:
Count Count
}; };
PlayStationMouse(System* system); PlayStationMouse();
~PlayStationMouse() override; ~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> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name); static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames(); static AxisList StaticGetAxisNames();
@ -52,8 +52,6 @@ private:
DeltaY DeltaY
}; };
System* m_system;
s32 m_last_host_position_x = 0; s32 m_last_host_position_x = 0;
s32 m_last_host_position_y = 0; s32 m_last_host_position_y = 0;

View file

@ -4,6 +4,8 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
Settings g_settings;
const char* SettingInfo::StringDefaultValue() const const char* SettingInfo::StringDefaultValue() const
{ {
return default_value ? default_value : ""; return default_value ? default_value : "";

View file

@ -146,6 +146,9 @@ struct Settings
bool log_to_window = false; bool log_to_window = false;
bool log_to_file = 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; bool HasAnyPerGameMemoryCards() const;
enum : u32 enum : u32
@ -216,3 +219,5 @@ struct Settings
static constexpr MemoryCardType DEFAULT_MEMORY_CARD_2_TYPE = MemoryCardType::None; static constexpr MemoryCardType DEFAULT_MEMORY_CARD_2_TYPE = MemoryCardType::None;
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO; static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
}; };
extern Settings g_settings;

View file

@ -1,23 +1,25 @@
#include "sio.h" #include "sio.h"
#include "common/log.h" #include "common/log.h"
#include "common/state_wrapper.h" #include "common/state_wrapper.h"
#include "controller.h"
#include "host_interface.h" #include "host_interface.h"
#include "interrupt_controller.h" #include "interrupt_controller.h"
#include "memory_card.h" #include "memory_card.h"
#include "controller.h"
#include "system.h"
Log_SetChannel(SIO); Log_SetChannel(SIO);
SIO g_sio;
SIO::SIO() = default; SIO::SIO() = default;
SIO::~SIO() = default; SIO::~SIO() = default;
void SIO::Initialize(System* system, InterruptController* interrupt_controller) void SIO::Initialize()
{ {
m_system = system; Reset();
m_interrupt_controller = interrupt_controller;
} }
void SIO::Shutdown() {}
void SIO::Reset() void SIO::Reset()
{ {
SoftReset(); SoftReset();

View file

@ -7,8 +7,6 @@
class StateWrapper; class StateWrapper;
class System;
class InterruptController;
class Controller; class Controller;
class MemoryCard; class MemoryCard;
@ -18,7 +16,8 @@ public:
SIO(); SIO();
~SIO(); ~SIO();
void Initialize(System* system, InterruptController* interrupt_controller); void Initialize();
void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -73,11 +72,10 @@ private:
void SoftReset(); void SoftReset();
System* m_system = nullptr;
InterruptController* m_interrupt_controller = nullptr;
SIO_CTRL m_SIO_CTRL = {}; SIO_CTRL m_SIO_CTRL = {};
SIO_STAT m_SIO_STAT = {}; SIO_STAT m_SIO_STAT = {};
SIO_MODE m_SIO_MODE = {}; SIO_MODE m_SIO_MODE = {};
u16 m_SIO_BAUD = 0; u16 m_SIO_BAUD = 0;
}; };
extern SIO g_sio;

View file

@ -11,21 +11,28 @@
#include <imgui.h> #include <imgui.h>
Log_SetChannel(SPU); Log_SetChannel(SPU);
SPU g_spu;
SPU::SPU() = default; SPU::SPU() = default;
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_tick_event = TimingEvents::CreateTimingEvent("SPU Sample", SYSCLK_TICKS_PER_SPU_TICK, SYSCLK_TICKS_PER_SPU_TICK,
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); std::bind(&SPU::Execute, this, std::placeholders::_1), false);
m_transfer_event = m_transfer_event =
m_system->CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, TimingEvents::CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
std::bind(&SPU::ExecuteTransfer, this, std::placeholders::_1), false); 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() void SPU::Reset()
@ -81,6 +88,7 @@ void SPU::Reset()
} }
m_transfer_fifo.Clear(); m_transfer_fifo.Clear();
m_transfer_event->Deactivate();
m_ram.fill(0); m_ram.fill(0);
UpdateEventInterval(); UpdateEventInterval();
} }
@ -147,7 +155,7 @@ bool SPU::DoState(StateWrapper& sw)
if (sw.IsReading()) if (sw.IsReading())
{ {
m_system->GetHostInterface()->GetAudioStream()->EmptyBuffers(); g_host_interface->GetAudioStream()->EmptyBuffers();
UpdateEventInterval(); UpdateEventInterval();
UpdateTransferEvent(); UpdateTransferEvent();
} }
@ -649,7 +657,7 @@ void SPU::CheckRAMIRQ(u32 address)
{ {
Log_DebugPrintf("SPU IRQ at address 0x%08X", address); Log_DebugPrintf("SPU IRQ at address 0x%08X", address);
m_SPUSTAT.irq9_flag = true; 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) while (remaining_frames > 0)
{ {
AudioStream* const output_stream = m_system->GetHostInterface()->GetAudioStream(); AudioStream* const output_stream = g_host_interface->GetAudioStream();
s16* output_frame_start; s16* output_frame_start;
u32 output_frame_space = remaining_frames; u32 output_frame_space = remaining_frames;
output_stream->BeginWrite(&output_frame_start, &output_frame_space); output_stream->BeginWrite(&output_frame_start, &output_frame_space);
@ -730,7 +738,7 @@ void SPU::Execute(TickCount ticks)
UpdateNoise(); UpdateNoise();
// Mix in CD audio. // 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) if (m_SPUCNT.cd_audio_enable)
{ {
const s32 cd_audio_volume_left = ApplyVolume(s32(cd_audio_left), m_cd_audio_volume_left); 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 // 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 // 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. // 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... // 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; 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. // 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) void SPU::DMARead(u32* words, u32 word_count)
@ -1744,7 +1752,7 @@ void SPU::DrawDebugStateWindow()
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x; const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 800.0f * framebuffer_scale), ImGuiCond_FirstUseEver); 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(); ImGui::End();
return; return;

View file

@ -11,11 +11,7 @@ namespace Common {
class WAVWriter; class WAVWriter;
} }
class System;
class TimingEvent; class TimingEvent;
class DMA;
class CDROM;
class InterruptController;
class SPU class SPU
{ {
@ -23,7 +19,8 @@ public:
SPU(); SPU();
~SPU(); ~SPU();
void Initialize(System* system, DMA* dma, CDROM* cdrom, InterruptController* interrupt_controller); void Initialize();
void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -369,10 +366,6 @@ private:
void UpdateTransferEvent(); void UpdateTransferEvent();
void UpdateDMARequest(); 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_tick_event;
std::unique_ptr<TimingEvent> m_transfer_event; std::unique_ptr<TimingEvent> m_transfer_event;
std::unique_ptr<Common::WAVWriter> m_dump_writer; std::unique_ptr<Common::WAVWriter> m_dump_writer;
@ -422,3 +415,5 @@ private:
std::array<u8, RAM_SIZE> m_ram{}; std::array<u8, RAM_SIZE> m_ram{};
}; };
extern SPU g_spu;

File diff suppressed because it is too large Load diff

View file

@ -11,22 +11,7 @@ class ByteStream;
class CDImage; class CDImage;
class StateWrapper; 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 Controller;
class Timers;
class SPU;
class MDEC;
class SIO;
struct SystemBootParameters struct SystemBootParameters
{ {
@ -44,65 +29,53 @@ struct SystemBootParameters
bool force_software_renderer = false; bool force_software_renderer = false;
}; };
class System namespace System {
{
public:
enum : u32 enum : u32
{ {
// 5 megabytes is sufficient for now, at the moment they're around 4.2MB. // 5 megabytes is sufficient for now, at the moment they're around 4.2MB.
MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024 MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024
}; };
friend TimingEvent; enum class State
{
~System(); Shutdown,
Starting,
Running,
Paused
};
/// Returns the preferred console type for a disc. /// Returns the preferred console type for a disc.
static ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region); ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
/// Creates a new System. State GetState();
static std::unique_ptr<System> Create(HostInterface* host_interface); void SetState(State new_state);
bool IsRunning();
bool IsPaused();
bool IsShutdown();
bool IsValid();
// Accessing components. ConsoleRegion GetRegion();
HostInterface* GetHostInterface() const { return m_host_interface; } bool IsPALRegion();
CPU::Core* GetCPU() const { return m_cpu.get(); } u32 GetFrameNumber();
Bus* GetBus() const { return m_bus.get(); } u32 GetInternalFrameNumber();
DMA* GetDMA() const { return m_dma.get(); } void FrameDone();
InterruptController* GetInterruptController() const { return m_interrupt_controller.get(); } void IncrementInternalFrameNumber();
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; } const std::string& GetRunningPath();
bool IsPALRegion() const { return m_region == ConsoleRegion::PAL; } const std::string& GetRunningCode();
u32 GetFrameNumber() const { return m_frame_number; } const std::string& GetRunningTitle();
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(); } float GetFPS();
float GetVPS();
const std::string& GetRunningPath() const { return m_running_game_path; } float GetEmulationSpeed();
const std::string& GetRunningCode() const { return m_running_game_code; } float GetAverageFrameTime();
const std::string& GetRunningTitle() const { return m_running_game_title; } float GetWorstFrameTime();
float GetThrottleFrequency();
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); bool Boot(const SystemBootParameters& params);
void Reset(); void Reset();
void Shutdown();
bool LoadState(ByteStream* state); bool LoadState(ByteStream* state);
bool SaveState(ByteStream* state, u32 screenshot_size = 128); bool SaveState(ByteStream* state, u32 screenshot_size = 128);
@ -110,12 +83,6 @@ public:
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes. /// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
bool RecreateGPU(GPURenderer renderer); 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(); void RunFrame();
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second. /// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
@ -130,37 +97,25 @@ public:
void UpdatePerformanceCounters(); void UpdatePerformanceCounters();
void ResetPerformanceCounters(); 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. // Access controllers for simulating input.
Controller* GetController(u32 slot) const; Controller* GetController(u32 slot);
void UpdateControllers(); void UpdateControllers();
void UpdateControllerSettings(); void UpdateControllerSettings();
void ResetControllers(); void ResetControllers();
void UpdateMemoryCards(); void UpdateMemoryCards();
bool HasMedia() const; bool HasMedia();
bool InsertMedia(const char* path); bool InsertMedia(const char* path);
void RemoveMedia(); 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. /// Returns the number of entries in the media/disc playlist.
ALWAYS_INLINE u32 GetMediaPlaylistCount() const { return static_cast<u32>(m_media_playlist.size()); } u32 GetMediaPlaylistCount();
/// Returns the current image from the media/disc playlist. /// Returns the current image from the media/disc playlist.
u32 GetMediaPlaylistIndex() const; u32 GetMediaPlaylistIndex();
/// Returns the path to the specified playlist index. /// Returns the path to the specified playlist index.
const std::string& GetMediaPlaylistPath(u32 index) const { return m_media_playlist[index]; } const std::string& GetMediaPlaylistPath(u32 index);
/// Adds a new path to the media playlist. /// Adds a new path to the media playlist.
bool AddMediaPathToPlaylist(const std::string_view& path); bool AddMediaPathToPlaylist(const std::string_view& path);
@ -175,96 +130,4 @@ public:
/// Switches to the specified media/disc playlist index. /// Switches to the specified media/disc playlist index.
bool SwitchMediaFromPlaylist(u32 index); bool SwitchMediaFromPlaylist(u32 index);
private: } // namespace System
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;
};

View file

@ -7,17 +7,22 @@
#include <imgui.h> #include <imgui.h>
Log_SetChannel(Timers); Log_SetChannel(Timers);
Timers g_timers;
Timers::Timers() = default; Timers::Timers() = default;
Timers::~Timers() = default; Timers::~Timers() = default;
void Timers::Initialize(System* system, InterruptController* interrupt_controller, GPU* gpu) void Timers::Initialize()
{ {
m_system = system; m_sysclk_event = TimingEvents::CreateTimingEvent(
m_interrupt_controller = interrupt_controller; "Timer SysClk Interrupt", 1, 1, std::bind(&Timers::AddSysClkTicks, this, std::placeholders::_1), false);
m_gpu = gpu; Reset();
m_sysclk_event = system->CreateTimingEvent("Timer SysClk Interrupt", 1, 1, }
std::bind(&Timers::AddSysClkTicks, this, std::placeholders::_1), false);
void Timers::Shutdown()
{
m_sysclk_event.reset();
} }
void Timers::Reset() void Timers::Reset()
@ -182,8 +187,8 @@ u32 Timers::ReadRegister(u32 offset)
if (timer_index < 2 && cs.external_counting_enabled) if (timer_index < 2 && cs.external_counting_enabled)
{ {
// timers 0/1 depend on the GPU // timers 0/1 depend on the GPU
if (timer_index == 0 || m_gpu->IsCRTCScanlinePending()) if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
m_gpu->SynchronizeCRTC(); g_gpu->SynchronizeCRTC();
} }
m_sysclk_event->InvokeEarly(); m_sysclk_event->InvokeEarly();
@ -196,8 +201,8 @@ u32 Timers::ReadRegister(u32 offset)
if (timer_index < 2 && cs.external_counting_enabled) if (timer_index < 2 && cs.external_counting_enabled)
{ {
// timers 0/1 depend on the GPU // timers 0/1 depend on the GPU
if (timer_index == 0 || m_gpu->IsCRTCScanlinePending()) if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
m_gpu->SynchronizeCRTC(); g_gpu->SynchronizeCRTC();
} }
m_sysclk_event->InvokeEarly(); m_sysclk_event->InvokeEarly();
@ -227,8 +232,8 @@ void Timers::WriteRegister(u32 offset, u32 value)
if (timer_index < 2 && cs.external_counting_enabled) if (timer_index < 2 && cs.external_counting_enabled)
{ {
// timers 0/1 depend on the GPU // timers 0/1 depend on the GPU
if (timer_index == 0 || m_gpu->IsCRTCScanlinePending()) if (timer_index == 0 || g_gpu->IsCRTCScanlinePending())
m_gpu->SynchronizeCRTC(); g_gpu->SynchronizeCRTC();
} }
m_sysclk_event->InvokeEarly(); m_sysclk_event->InvokeEarly();
@ -311,7 +316,7 @@ void Timers::UpdateIRQ(u32 index)
Log_DebugPrintf("Raising timer %u IRQ", index); Log_DebugPrintf("Raising timer %u IRQ", index);
cs.irq_done = true; cs.irq_done = true;
m_interrupt_controller->InterruptRequest( g_interrupt_controller.InterruptRequest(
static_cast<InterruptController::IRQ>(static_cast<u32>(InterruptController::IRQ::TMR0) + index)); 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; const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 100.0f * framebuffer_scale), ImGuiCond_FirstUseEver); 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(); ImGui::End();
return; return;

View file

@ -6,18 +6,17 @@
class StateWrapper; class StateWrapper;
class System;
class TimingEvent; class TimingEvent;
class InterruptController;
class GPU; class GPU;
class Timers class Timers final
{ {
public: public:
Timers(); Timers();
~Timers(); ~Timers();
void Initialize(System* system, InterruptController* interrupt_controller, GPU* gpu); void Initialize();
void Shutdown();
void Reset(); void Reset();
bool DoState(StateWrapper& sw); bool DoState(StateWrapper& sw);
@ -26,10 +25,10 @@ public:
void DrawDebugStateWindow(); void DrawDebugStateWindow();
// dot clock/hblank/sysclk div 8 // 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 // queries for GPU
bool IsExternalIRQEnabled(u32 timer) const ALWAYS_INLINE bool IsExternalIRQEnabled(u32 timer) const
{ {
const CounterState& cs = m_states[timer]; const CounterState& cs = m_states[timer];
return (cs.external_counting_enabled && (cs.mode.bits & ((1u << 4) | (1u << 5))) != 0); return (cs.external_counting_enabled && (cs.mode.bits & ((1u << 4) | (1u << 5))) != 0);
@ -42,9 +41,6 @@ public:
u32 ReadRegister(u32 offset); u32 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u32 value); void WriteRegister(u32 offset, u32 value);
// changing interfaces
void SetGPU(GPU* gpu) { m_gpu = gpu; }
private: private:
static constexpr u32 NUM_TIMERS = 3; static constexpr u32 NUM_TIMERS = 3;
@ -93,11 +89,10 @@ private:
TickCount GetTicksUntilNextInterrupt() const; TickCount GetTicksUntilNextInterrupt() const;
void UpdateSysClkEvent(); void UpdateSysClkEvent();
System* m_system = nullptr;
InterruptController* m_interrupt_controller = nullptr;
GPU* m_gpu = nullptr;
std::unique_ptr<TimingEvent> m_sysclk_event; std::unique_ptr<TimingEvent> m_sysclk_event;
std::array<CounterState, NUM_TIMERS> m_states{}; std::array<CounterState, NUM_TIMERS> m_states{};
u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8 u32 m_sysclk_div_8_carry = 0; // partial ticks for timer 3 with sysclk/8
}; };
extern Timers g_timers;

View file

@ -1,34 +1,268 @@
#include "timing_event.h" #include "timing_event.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/log.h"
#include "common/state_wrapper.h"
#include "cpu_core.h" #include "cpu_core.h"
#include "system.h" #include "system.h"
Log_SetChannel(TimingEvents);
TimingEvent::TimingEvent(System* system, std::string name, TickCount period, TickCount interval, namespace TimingEvents {
TimingEventCallback callback)
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_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() TimingEvent::~TimingEvent()
{ {
if (m_active) if (m_active)
m_system->RemoveActiveEvent(this); TimingEvents::RemoveActiveEvent(this);
} }
TickCount TimingEvent::GetTicksSinceLastExecution() const 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 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) 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; m_downcount = pending_ticks + ticks;
if (!m_active) 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. // Event is going active, so we want it to only execute ticks from the current timestamp.
m_time_since_last_run = -pending_ticks; m_time_since_last_run = -pending_ticks;
m_active = true; m_active = true;
m_system->AddActiveEvent(this); TimingEvents::AddActiveEvent(this);
} }
else else
{ {
// Event is already active, so we leave the time since last run alone, and just modify the downcount. // 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. // 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_downcount = m_interval;
m_time_since_last_run = 0; m_time_since_last_run = 0;
m_system->SortEvents(); TimingEvents::SortEvents();
} }
void TimingEvent::InvokeEarly(bool force /* = false */) void TimingEvent::InvokeEarly(bool force /* = false */)
@ -74,7 +308,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
if (!m_active) if (!m_active)
return; 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; const TickCount ticks_to_execute = m_time_since_last_run + pending_ticks;
if (!force && ticks_to_execute < m_period) if (!force && ticks_to_execute < m_period)
return; return;
@ -84,7 +318,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
m_callback(ticks_to_execute, 0); m_callback(ticks_to_execute, 0);
// Since we've changed the downcount, we need to re-sort the events. // Since we've changed the downcount, we need to re-sort the events.
m_system->SortEvents(); TimingEvents::SortEvents();
} }
void TimingEvent::Activate() void TimingEvent::Activate()
@ -93,12 +327,12 @@ void TimingEvent::Activate()
return; return;
// leave the downcount intact // leave the downcount intact
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks(); const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount += pending_ticks; m_downcount += pending_ticks;
m_time_since_last_run -= pending_ticks; m_time_since_last_run -= pending_ticks;
m_active = true; m_active = true;
m_system->AddActiveEvent(this); TimingEvents::AddActiveEvent(this);
} }
void TimingEvent::Deactivate() void TimingEvent::Deactivate()
@ -106,10 +340,10 @@ void TimingEvent::Deactivate()
if (!m_active) if (!m_active)
return; return;
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks(); const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount -= pending_ticks; m_downcount -= pending_ticks;
m_time_since_last_run += pending_ticks; m_time_since_last_run += pending_ticks;
m_active = false; m_active = false;
m_system->RemoveActiveEvent(this); TimingEvents::RemoveActiveEvent(this);
} }

View file

@ -6,29 +6,24 @@
#include "types.h" #include "types.h"
class System; class StateWrapper;
class TimingEvent;
// Event callback type. Second parameter is the number of cycles the event was executed "late". // 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)>; using TimingEventCallback = std::function<void(TickCount ticks, TickCount ticks_late)>;
class TimingEvent class TimingEvent
{ {
friend System;
public: public:
TimingEvent(System* system, std::string name, TickCount period, TickCount interval, TimingEventCallback callback); TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback);
~TimingEvent(); ~TimingEvent();
System* GetSystem() const { return m_system; }
const std::string& GetName() const { return m_name; } const std::string& GetName() const { return m_name; }
bool IsActive() const { return m_active; } bool IsActive() const { return m_active; }
// Returns the number of ticks between each event. // Returns the number of ticks between each event.
TickCount GetPeriod() const { return m_period; } ALWAYS_INLINE TickCount GetPeriod() const { return m_period; }
TickCount GetInterval() const { return m_interval; } ALWAYS_INLINE TickCount GetInterval() const { return m_interval; }
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
TickCount GetDowncount() const { return m_downcount; }
// Includes pending time. // Includes pending time.
TickCount GetTicksSinceLastExecution() const; TickCount GetTicksSinceLastExecution() const;
@ -61,14 +56,35 @@ public:
void SetInterval(TickCount interval) { m_interval = interval; } void SetInterval(TickCount interval) { m_interval = interval; }
void SetPeriod(TickCount period) { m_period = period; } void SetPeriod(TickCount period) { m_period = period; }
private:
TickCount m_downcount; TickCount m_downcount;
TickCount m_time_since_last_run; TickCount m_time_since_last_run;
TickCount m_period; TickCount m_period;
TickCount m_interval; TickCount m_interval;
TimingEventCallback m_callback; TimingEventCallback m_callback;
System* m_system;
std::string m_name; std::string m_name;
bool m_active; 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

View file

@ -162,13 +162,13 @@ void LibretroHostInterface::AddOSDMessage(std::string message, float duration /*
{ {
retro_message msg = {}; retro_message msg = {};
msg.msg = message.c_str(); 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); g_retro_environment_callback(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
} }
void LibretroHostInterface::retro_get_system_av_info(struct retro_system_av_info* info) 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); GetSystemAVInfo(info, use_resolution_scale);
Log_InfoPrintf("base = %ux%u, max = %ux%u, aspect ratio = %.2f, fps = %.2f", info->geometry.base_width, 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) 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; const u32 resolution_scale = use_resolution_scale ? g_settings.gpu_resolution_scale : 1u;
Assert(m_system); Assert(System::IsValid());
std::memset(info, 0, sizeof(*info)); 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_width = 320;
info->geometry.base_height = 240; info->geometry.base_height = 240;
info->geometry.max_width = GPU::VRAM_WIDTH * resolution_scale; info->geometry.max_width = GPU::VRAM_WIDTH * resolution_scale;
info->geometry.max_height = GPU::VRAM_HEIGHT * 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); info->timing.sample_rate = static_cast<double>(AUDIO_SAMPLE_RATE);
} }
@ -209,7 +209,7 @@ void LibretroHostInterface::UpdateSystemAVInfo(bool use_resolution_scale)
void LibretroHostInterface::UpdateGeometry() void LibretroHostInterface::UpdateGeometry()
{ {
struct retro_system_av_info avi; 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); GetSystemAVInfo(&avi, use_resolution_scale);
Log_InfoPrintf("base = %ux%u, max = %ux%u, aspect ratio = %.2f", avi.geometry.base_width, avi.geometry.base_height, 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() void LibretroHostInterface::UpdateLogging()
{ {
Log::SetFilterLevel(m_settings.log_level); Log::SetFilterLevel(g_settings.log_level);
if (s_libretro_log_callback_valid) if (s_libretro_log_callback_valid)
Log::SetConsoleOutputParams(false); Log::SetConsoleOutputParams(false);
else 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) 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)) if (!BootSystem(bp))
return false; return false;
if (m_settings.gpu_renderer != GPURenderer::Software) if (g_settings.gpu_renderer != GPURenderer::Software)
{ {
if (!m_hw_render_callback_valid) if (!m_hw_render_callback_valid)
RequestHardwareRendererContext(); RequestHardwareRendererContext();
@ -252,25 +252,25 @@ bool LibretroHostInterface::retro_load_game(const struct retro_game_info* game)
void LibretroHostInterface::retro_run_frame() void LibretroHostInterface::retro_run_frame()
{ {
Assert(m_system); Assert(!System::IsShutdown());
if (HasCoreVariablesChanged()) if (HasCoreVariablesChanged())
UpdateSettings(); UpdateSettings();
UpdateControllers(); UpdateControllers();
m_system->GetGPU()->RestoreGraphicsAPIState(); g_gpu->RestoreGraphicsAPIState();
m_system->RunFrame(); System::RunFrame();
m_system->GetGPU()->ResetGraphicsAPIState(); g_gpu->ResetGraphicsAPIState();
m_display->Render(); m_display->Render();
} }
unsigned LibretroHostInterface::retro_get_region() 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() 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) bool LibretroHostInterface::retro_serialize(void* data, size_t size)
{ {
std::unique_ptr<ByteStream> stream = ByteStream_CreateMemoryStream(data, static_cast<u32>(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"); Log_ErrorPrintf("Failed to save state to memory stream");
return false; 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) bool LibretroHostInterface::retro_unserialize(const void* data, size_t size)
{ {
std::unique_ptr<ByteStream> stream = ByteStream_CreateReadOnlyMemoryStream(data, static_cast<u32>(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"); Log_ErrorPrintf("Failed to load save state from memory stream");
return false; return false;
@ -307,7 +307,7 @@ void* LibretroHostInterface::retro_get_memory_data(unsigned id)
switch (id) switch (id)
{ {
case RETRO_MEMORY_SYSTEM_RAM: case RETRO_MEMORY_SYSTEM_RAM:
return m_system ? m_system->GetBus()->GetRAM() : nullptr; return System::IsShutdown() ? nullptr : Bus::g_ram;
default: default:
return nullptr; return nullptr;
@ -552,27 +552,27 @@ bool LibretroHostInterface::HasCoreVariablesChanged()
void LibretroHostInterface::LoadSettings() void LibretroHostInterface::LoadSettings()
{ {
LibretroSettingsInterface si; LibretroSettingsInterface si;
m_settings.Load(si); g_settings.Load(si);
// Assume BIOS files are located in system directory. // Assume BIOS files are located in system directory.
const char* system_directory = nullptr; const char* system_directory = nullptr;
if (!g_retro_environment_callback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_directory) || !system_directory) if (!g_retro_environment_callback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_directory) || !system_directory)
system_directory = "bios"; system_directory = "bios";
m_settings.bios_path = g_settings.bios_path =
StringUtil::StdStringFromFormat("%s%cscph1001.bin", system_directory, FS_OSPATH_SEPERATOR_CHARACTER); StringUtil::StdStringFromFormat("%s%cscph1001.bin", system_directory, FS_OSPATH_SEPERATOR_CHARACTER);
// Ensure we don't use the standalone memcard directory in shared mode. // Ensure we don't use the standalone memcard directory in shared mode.
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) 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() void LibretroHostInterface::UpdateSettings()
{ {
Settings old_settings(std::move(m_settings)); Settings old_settings(std::move(g_settings));
LoadSettings(); LoadSettings();
if (m_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale && if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale &&
m_settings.gpu_renderer != GPURenderer::Software) g_settings.gpu_renderer != GPURenderer::Software)
{ {
ReportMessage("Resolution changed, updating system AV info..."); ReportMessage("Resolution changed, updating system AV info...");
@ -588,14 +588,14 @@ void LibretroHostInterface::UpdateSettings()
SwitchToHardwareRenderer(); SwitchToHardwareRenderer();
// Don't let the base class mess with the GPU. // 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.", ReportFormattedMessage("Switch to %s renderer pending, please restart the core to apply.",
Settings::GetRendererDisplayName(m_settings.gpu_renderer)); Settings::GetRendererDisplayName(g_settings.gpu_renderer));
m_settings.gpu_renderer = old_settings.gpu_renderer; g_settings.gpu_renderer = old_settings.gpu_renderer;
} }
CheckForSettingsChanges(old_settings); CheckForSettingsChanges(old_settings);
@ -605,10 +605,10 @@ void LibretroHostInterface::CheckForSettingsChanges(const Settings& old_settings
{ {
HostInterface::CheckForSettingsChanges(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(); UpdateGeometry();
if (m_settings.log_level != old_settings.log_level) if (g_settings.log_level != old_settings.log_level)
UpdateLogging(); UpdateLogging();
} }
@ -618,7 +618,7 @@ void LibretroHostInterface::UpdateControllers()
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) 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: case ControllerType::None:
break; break;
@ -633,7 +633,7 @@ void LibretroHostInterface::UpdateControllers()
default: default:
ReportFormattedError("Unhandled controller type '%s'.", ReportFormattedError("Unhandled controller type '%s'.",
Settings::GetControllerTypeDisplayName(m_settings.controller_types[i])); Settings::GetControllerTypeDisplayName(g_settings.controller_types[i]));
break; break;
} }
} }
@ -641,7 +641,7 @@ void LibretroHostInterface::UpdateControllers()
void LibretroHostInterface::UpdateControllersDigitalController(u32 index) void LibretroHostInterface::UpdateControllersDigitalController(u32 index)
{ {
DigitalController* controller = static_cast<DigitalController*>(m_system->GetController(index)); DigitalController* controller = static_cast<DigitalController*>(System::GetController(index));
DebugAssert(controller); DebugAssert(controller);
static constexpr std::array<std::pair<DigitalController::Button, u32>, 14> mapping = { 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) void LibretroHostInterface::UpdateControllersAnalogController(u32 index)
{ {
AnalogController* controller = static_cast<AnalogController*>(m_system->GetController(index)); AnalogController* controller = static_cast<AnalogController*>(System::GetController(index));
DebugAssert(controller); DebugAssert(controller);
static constexpr std::array<std::pair<AnalogController::Button, u32>, 16> button_mapping = { 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_width = avi.geometry.base_width;
wi.surface_height = avi.geometry.base_height; wi.surface_height = avi.geometry.base_height;
wi.surface_scale = 1.0f; wi.surface_scale = 1.0f;
if (!display || !display->CreateRenderDevice(wi, {}, g_libretro_host_interface.m_settings.gpu_use_debug_device) || if (!display || !display->CreateRenderDevice(wi, {}, g_settings.gpu_use_debug_device) ||
!display->InitializeRenderDevice({}, m_settings.gpu_use_debug_device)) !display->InitializeRenderDevice({}, g_settings.gpu_use_debug_device))
{ {
Log_ErrorPrintf("Failed to create hardware host display"); Log_ErrorPrintf("Failed to create hardware host display");
return; return;
@ -884,7 +884,7 @@ void LibretroHostInterface::SwitchToHardwareRenderer()
} }
std::swap(display, g_libretro_host_interface.m_display); std::swap(display, g_libretro_host_interface.m_display);
g_libretro_host_interface.m_system->RecreateGPU(renderer.value()); System::RecreateGPU(renderer.value());
display->DestroyRenderDevice(); display->DestroyRenderDevice();
m_using_hardware_renderer = true; m_using_hardware_renderer = true;
} }
@ -917,13 +917,12 @@ void LibretroHostInterface::SwitchToSoftwareRenderer()
} }
m_display = std::make_unique<LibretroHostDisplay>(); m_display = std::make_unique<LibretroHostDisplay>();
m_system->RecreateGPU(GPURenderer::Software); System::RecreateGPU(GPURenderer::Software);
} }
bool LibretroHostInterface::DiskControlSetEjectState(bool ejected) bool LibretroHostInterface::DiskControlSetEjectState(bool ejected)
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown())
if (!system)
{ {
Log_ErrorPrintf("DiskControlSetEjectState() - no system"); Log_ErrorPrintf("DiskControlSetEjectState() - no system");
return false; return false;
@ -933,51 +932,48 @@ bool LibretroHostInterface::DiskControlSetEjectState(bool ejected)
if (ejected) if (ejected)
{ {
if (!system->HasMedia()) if (!System::HasMedia())
return false; return false;
system->RemoveMedia(); System::RemoveMedia();
return true; return true;
} }
else else
{ {
const u32 image_to_insert = P_THIS->m_next_disc_index.value_or(0); const u32 image_to_insert = P_THIS->m_next_disc_index.value_or(0);
Log_DevPrintf("Inserting image %u", image_to_insert); Log_DevPrintf("Inserting image %u", image_to_insert);
return system->SwitchMediaFromPlaylist(image_to_insert); return System::SwitchMediaFromPlaylist(image_to_insert);
} }
} }
bool LibretroHostInterface::DiskControlGetEjectState() bool LibretroHostInterface::DiskControlGetEjectState()
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown())
if (!system)
{ {
Log_ErrorPrintf("DiskControlGetEjectState() - no system"); Log_ErrorPrintf("DiskControlGetEjectState() - no system");
return false; return false;
} }
Log_DevPrintf("DiskControlGetEjectState() -> %u", static_cast<unsigned>(system->HasMedia())); Log_DevPrintf("DiskControlGetEjectState() -> %u", static_cast<unsigned>(System::HasMedia()));
return system->HasMedia(); return System::HasMedia();
} }
unsigned LibretroHostInterface::DiskControlGetImageIndex() unsigned LibretroHostInterface::DiskControlGetImageIndex()
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown())
if (!system)
{ {
Log_ErrorPrintf("DiskControlGetImageIndex() - no system"); Log_ErrorPrintf("DiskControlGetImageIndex() - no system");
return false; 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); Log_DevPrintf("DiskControlGetImageIndex() -> %u", index);
return index; return index;
} }
bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index) bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index)
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown())
if (!system)
{ {
Log_ErrorPrintf("DiskControlSetImageIndex() - no system"); Log_ErrorPrintf("DiskControlSetImageIndex() - no system");
return false; return false;
@ -985,7 +981,7 @@ bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index)
Log_DevPrintf("DiskControlSetImageIndex(%u)", index); Log_DevPrintf("DiskControlSetImageIndex(%u)", index);
if (index >= system->GetMediaPlaylistCount()) if (index >= System::GetMediaPlaylistCount())
return false; return false;
P_THIS->m_next_disc_index = index; P_THIS->m_next_disc_index = index;
@ -994,21 +990,19 @@ bool LibretroHostInterface::DiskControlSetImageIndex(unsigned index)
unsigned LibretroHostInterface::DiskControlGetNumImages() unsigned LibretroHostInterface::DiskControlGetNumImages()
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown())
if (!system)
{ {
Log_ErrorPrintf("DiskControlGetNumImages() - no system"); Log_ErrorPrintf("DiskControlGetNumImages() - no system");
return false; return false;
} }
Log_DevPrintf("DiskControlGetNumImages() -> %u", system->GetMediaPlaylistCount()); Log_DevPrintf("DiskControlGetNumImages() -> %u", System::GetMediaPlaylistCount());
return static_cast<unsigned>(system->GetMediaPlaylistCount()); return static_cast<unsigned>(System::GetMediaPlaylistCount());
} }
bool LibretroHostInterface::DiskControlReplaceImageIndex(unsigned index, const retro_game_info* info) bool LibretroHostInterface::DiskControlReplaceImageIndex(unsigned index, const retro_game_info* info)
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown())
if (!system)
{ {
Log_ErrorPrintf("DiskControlReplaceImageIndex() - no system"); Log_ErrorPrintf("DiskControlReplaceImageIndex() - no system");
return false; return false;
@ -1016,22 +1010,21 @@ bool LibretroHostInterface::DiskControlReplaceImageIndex(unsigned index, const r
Log_DevPrintf("DiskControlReplaceImageIndex(%u, %s)", index, info ? info->path : "null"); Log_DevPrintf("DiskControlReplaceImageIndex(%u, %s)", index, info ? info->path : "null");
if (info && info->path) if (info && info->path)
return system->ReplaceMediaPathFromPlaylist(index, info->path); return System::ReplaceMediaPathFromPlaylist(index, info->path);
else else
return system->RemoveMediaPathFromPlaylist(index); return System::RemoveMediaPathFromPlaylist(index);
} }
bool LibretroHostInterface::DiskControlAddImageIndex() bool LibretroHostInterface::DiskControlAddImageIndex()
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown())
if (!system)
{ {
Log_ErrorPrintf("DiskControlAddImageIndex() - no system"); Log_ErrorPrintf("DiskControlAddImageIndex() - no system");
return false; return false;
} }
Log_DevPrintf("DiskControlAddImageIndex() -> %zu", system->GetMediaPlaylistCount()); Log_DevPrintf("DiskControlAddImageIndex() -> %zu", System::GetMediaPlaylistCount());
system->AddMediaPathToPlaylist({}); System::AddMediaPathToPlaylist({});
return true; return true;
} }
@ -1044,11 +1037,10 @@ bool LibretroHostInterface::DiskControlSetInitialImage(unsigned index, const cha
bool LibretroHostInterface::DiskControlGetImagePath(unsigned index, char* path, size_t len) bool LibretroHostInterface::DiskControlGetImagePath(unsigned index, char* path, size_t len)
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown() || index >= System::GetMediaPlaylistCount())
if (!system || index >= system->GetMediaPlaylistCount())
return false; 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()); Log_DevPrintf("DiskControlGetImagePath(%u) -> %s", index, image_path.c_str());
if (image_path.empty()) if (image_path.empty())
return false; return false;
@ -1059,11 +1051,10 @@ bool LibretroHostInterface::DiskControlGetImagePath(unsigned index, char* path,
bool LibretroHostInterface::DiskControlGetImageLabel(unsigned index, char* label, size_t len) bool LibretroHostInterface::DiskControlGetImageLabel(unsigned index, char* label, size_t len)
{ {
System* system = P_THIS->GetSystem(); if (System::IsShutdown() || index >= System::GetMediaPlaylistCount())
if (!system || index >= system->GetMediaPlaylistCount())
return false; return false;
const std::string& image_path = system->GetMediaPlaylistPath(index); const std::string& image_path = System::GetMediaPlaylistPath(index);
if (image_path.empty()) if (image_path.empty())
return false; return false;

View file

@ -16,7 +16,7 @@ public:
static bool HasCoreVariablesChanged(); static bool HasCoreVariablesChanged();
static void InitDiskControlInterface(); 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; bool Initialize() override;
void Shutdown() override; void Shutdown() override;

View file

@ -269,7 +269,7 @@ void QtHostInterface::setDefaultSettings()
return; 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); std::lock_guard<std::recursive_mutex> guard(m_settings_mutex);
SetDefaultSettings(*m_settings_interface.get()); SetDefaultSettings(*m_settings_interface.get());
@ -289,7 +289,7 @@ void QtHostInterface::applySettings()
return; 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); std::lock_guard<std::recursive_mutex> guard(m_settings_mutex);
CommonHostInterface::LoadSettings(*m_settings_interface.get()); CommonHostInterface::LoadSettings(*m_settings_interface.get());
@ -298,7 +298,7 @@ void QtHostInterface::applySettings()
CheckForSettingsChanges(old_settings); CheckForSettingsChanges(old_settings);
// detect when render-to-main flag changes // 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); 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) 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); m_display->ResizeRenderWindow(width, height);
// re-render the display, since otherwise it will be out of date and stretched if paused // re-render the display, since otherwise it will be out of date and stretched if paused
if (m_system) if (!System::IsShutdown())
renderDisplay(); renderDisplay();
} }
@ -434,7 +434,7 @@ void QtHostInterface::redrawDisplayWindow()
return; return;
} }
if (!m_display || !m_system) if (!m_display || System::IsShutdown())
return; return;
renderDisplay(); renderDisplay();
@ -458,8 +458,8 @@ bool QtHostInterface::AcquireHostDisplay()
m_is_rendering_to_main = m_settings_interface->GetBoolValue("Main", "RenderToMainWindow", true); m_is_rendering_to_main = m_settings_interface->GetBoolValue("Main", "RenderToMainWindow", true);
QtDisplayWidget* display_widget = QtDisplayWidget* display_widget =
createDisplayRequested(m_worker_thread, QString::fromStdString(m_settings.gpu_adapter), createDisplayRequested(m_worker_thread, QString::fromStdString(g_settings.gpu_adapter),
m_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main); g_settings.gpu_use_debug_device, m_is_fullscreen, m_is_rendering_to_main);
if (!display_widget || !m_display->HasRenderDevice()) if (!display_widget || !m_display->HasRenderDevice())
{ {
emit destroyDisplayRequested(); emit destroyDisplayRequested();
@ -470,7 +470,7 @@ bool QtHostInterface::AcquireHostDisplay()
createImGuiContext(display_widget->devicePixelRatioFromScreen()); createImGuiContext(display_widget->devicePixelRatioFromScreen());
if (!m_display->MakeRenderContextCurrent() || 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(); destroyImGuiContext();
m_display->DestroyRenderDevice(); m_display->DestroyRenderDevice();
@ -486,7 +486,7 @@ bool QtHostInterface::AcquireHostDisplay()
HostDisplay* QtHostInterface::createHostDisplay() HostDisplay* QtHostInterface::createHostDisplay()
{ {
switch (m_settings.gpu_renderer) switch (g_settings.gpu_renderer)
{ {
case GPURenderer::HardwareVulkan: case GPURenderer::HardwareVulkan:
m_display = std::make_unique<FrontendCommon::VulkanHostDisplay>(); m_display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
@ -630,20 +630,19 @@ void QtHostInterface::OnSystemPerformanceCountersUpdated()
{ {
HostInterface::OnSystemPerformanceCountersUpdated(); HostInterface::OnSystemPerformanceCountersUpdated();
DebugAssert(m_system); emit systemPerformanceCountersUpdated(System::GetEmulationSpeed(), System::GetFPS(), System::GetVPS(),
emit systemPerformanceCountersUpdated(m_system->GetEmulationSpeed(), m_system->GetFPS(), m_system->GetVPS(), System::GetAverageFrameTime(), System::GetWorstFrameTime());
m_system->GetAverageFrameTime(), m_system->GetWorstFrameTime());
} }
void QtHostInterface::OnRunningGameChanged() void QtHostInterface::OnRunningGameChanged()
{ {
CommonHostInterface::OnRunningGameChanged(); CommonHostInterface::OnRunningGameChanged();
if (m_system) if (!System::IsShutdown())
{ {
emit runningGameChanged(QString::fromStdString(m_system->GetRunningPath()), emit runningGameChanged(QString::fromStdString(System::GetRunningPath()),
QString::fromStdString(m_system->GetRunningCode()), QString::fromStdString(System::GetRunningCode()),
QString::fromStdString(m_system->GetRunningTitle())); QString::fromStdString(System::GetRunningTitle()));
} }
else else
{ {
@ -653,7 +652,7 @@ void QtHostInterface::OnRunningGameChanged()
void QtHostInterface::OnSystemStateSaved(bool global, s32 slot) 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() void QtHostInterface::LoadSettings()
@ -731,10 +730,10 @@ void QtHostInterface::powerOffSystem()
return; return;
} }
if (!m_system) if (System::IsShutdown())
return; return;
if (m_settings.save_state_on_exit) if (g_settings.save_state_on_exit)
SaveResumeSaveState(); SaveResumeSaveState();
DestroySystem(); DestroySystem();
@ -756,7 +755,7 @@ void QtHostInterface::resetSystem()
return; return;
} }
if (!m_system) if (System::IsShutdown())
{ {
Log_ErrorPrintf("resetSystem() called without system"); Log_ErrorPrintf("resetSystem() called without system");
return; return;
@ -784,13 +783,13 @@ void QtHostInterface::changeDisc(const QString& new_disc_filename)
return; return;
} }
if (!m_system) if (System::IsShutdown())
return; return;
if (!new_disc_filename.isEmpty()) if (!new_disc_filename.isEmpty())
m_system->InsertMedia(new_disc_filename.toStdString().c_str()); System::InsertMedia(new_disc_filename.toStdString().c_str());
else else
m_system->RemoveMedia(); System::RemoveMedia();
} }
static QString FormatTimestampForSaveStateMenu(u64 timestamp) static QString FormatTimestampForSaveStateMenu(u64 timestamp)
@ -925,7 +924,7 @@ void QtHostInterface::saveState(bool global, qint32 slot, bool block_until_done
return; return;
} }
if (m_system) if (!System::IsShutdown())
SaveState(global, slot); SaveState(global, slot);
} }
@ -940,7 +939,7 @@ void QtHostInterface::setAudioOutputVolume(int value)
if (m_audio_stream) if (m_audio_stream)
m_audio_stream->SetOutputVolume(value); m_audio_stream->SetOutputVolume(value);
m_settings.audio_output_volume = value; g_settings.audio_output_volume = value;
} }
void QtHostInterface::setAudioOutputMuted(bool muted) void QtHostInterface::setAudioOutputMuted(bool muted)
@ -952,9 +951,9 @@ void QtHostInterface::setAudioOutputMuted(bool muted)
} }
if (m_audio_stream) 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() void QtHostInterface::startDumpingAudio()
@ -1058,14 +1057,14 @@ void QtHostInterface::threadEntryPoint()
// TODO: Event which flags the thread as ready // TODO: Event which flags the thread as ready
while (!m_shutdown_flag.load()) while (!m_shutdown_flag.load())
{ {
if (!m_system || m_paused) if (!System::IsRunning())
{ {
// wait until we have a system before running // wait until we have a system before running
m_worker_thread_event_loop->exec(); m_worker_thread_event_loop->exec();
continue; continue;
} }
m_system->RunFrame(); System::RunFrame();
UpdateControllerRumble(); UpdateControllerRumble();
if (m_frame_step_request) if (m_frame_step_request)
{ {
@ -1075,10 +1074,10 @@ void QtHostInterface::threadEntryPoint()
renderDisplay(); renderDisplay();
m_system->UpdatePerformanceCounters(); System::UpdatePerformanceCounters();
if (m_speed_limiter_enabled) if (m_speed_limiter_enabled)
m_system->Throttle(); System::Throttle();
m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents); m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents);
PollAndUpdate(); PollAndUpdate();
@ -1095,14 +1094,14 @@ void QtHostInterface::threadEntryPoint()
void QtHostInterface::renderDisplay() void QtHostInterface::renderDisplay()
{ {
m_system->GetGPU()->ResetGraphicsAPIState(); g_gpu->ResetGraphicsAPIState();
DrawImGuiWindows(); DrawImGuiWindows();
m_display->Render(); m_display->Render();
ImGui::NewFrame(); ImGui::NewFrame();
m_system->GetGPU()->RestoreGraphicsAPIState(); g_gpu->RestoreGraphicsAPIState();
} }
void QtHostInterface::wakeThread() void QtHostInterface::wakeThread()

View file

@ -108,7 +108,7 @@ bool SDLHostInterface::CreateDisplay()
} }
std::unique_ptr<HostDisplay> display; std::unique_ptr<HostDisplay> display;
switch (m_settings.gpu_renderer) switch (g_settings.gpu_renderer)
{ {
case GPURenderer::HardwareVulkan: case GPURenderer::HardwareVulkan:
display = std::make_unique<FrontendCommon::VulkanHostDisplay>(); display = std::make_unique<FrontendCommon::VulkanHostDisplay>();
@ -130,8 +130,8 @@ bool SDLHostInterface::CreateDisplay()
} }
Assert(display); Assert(display);
if (!display->CreateRenderDevice(wi.value(), m_settings.gpu_adapter, m_settings.gpu_use_debug_device) || if (!display->CreateRenderDevice(wi.value(), g_settings.gpu_adapter, g_settings.gpu_use_debug_device) ||
!display->InitializeRenderDevice(GetShaderCacheBasePath(), m_settings.gpu_use_debug_device)) !display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device))
{ {
ReportError("Failed to create/initialize display render device"); ReportError("Failed to create/initialize display render device");
return false; return false;
@ -209,7 +209,7 @@ bool SDLHostInterface::AcquireHostDisplay()
// Handle renderer switch if required. // Handle renderer switch if required.
const HostDisplay::RenderAPI render_api = m_display->GetRenderAPI(); const HostDisplay::RenderAPI render_api = m_display->GetRenderAPI();
bool needs_switch = false; bool needs_switch = false;
switch (m_settings.gpu_renderer) switch (g_settings.gpu_renderer)
{ {
#ifdef WIN32 #ifdef WIN32
case GPURenderer::HardwareD3D11: case GPURenderer::HardwareD3D11:
@ -321,8 +321,8 @@ void SDLHostInterface::OnRunningGameChanged()
{ {
CommonHostInterface::OnRunningGameChanged(); CommonHostInterface::OnRunningGameChanged();
if (m_system && !m_system->GetRunningTitle().empty()) if (!System::GetRunningTitle().empty())
SDL_SetWindowTitle(m_window, m_system->GetRunningTitle().c_str()); SDL_SetWindowTitle(m_window, System::GetRunningTitle().c_str());
else else
SDL_SetWindowTitle(m_window, GetWindowTitle()); SDL_SetWindowTitle(m_window, GetWindowTitle());
} }
@ -345,7 +345,7 @@ void SDLHostInterface::SaveAndUpdateSettings()
{ {
m_settings_copy.Save(*m_settings_interface.get()); 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()); CommonHostInterface::LoadSettings(*m_settings_interface.get());
CheckForSettingsChanges(old_settings); CheckForSettingsChanges(old_settings);
@ -451,7 +451,7 @@ void SDLHostInterface::LoadSettings()
// Settings need to be loaded prior to creating the window for OpenGL bits. // Settings need to be loaded prior to creating the window for OpenGL bits.
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName()); m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
CommonHostInterface::LoadSettings(*m_settings_interface.get()); CommonHostInterface::LoadSettings(*m_settings_interface.get());
m_settings_copy = m_settings; m_settings_copy = g_settings;
} }
void SDLHostInterface::ReportError(const char* message) void SDLHostInterface::ReportError(const char* message)
@ -604,7 +604,7 @@ void SDLHostInterface::DrawImGuiWindows()
CommonHostInterface::DrawImGuiWindows(); CommonHostInterface::DrawImGuiWindows();
if (!m_system) if (System::IsShutdown())
DrawPoweredOffWindow(); DrawPoweredOffWindow();
if (m_settings_window_open) if (m_settings_window_open)
@ -621,7 +621,7 @@ void SDLHostInterface::DrawMainMenuBar()
if (!ImGui::BeginMainMenuBar()) if (!ImGui::BeginMainMenuBar())
return; return;
const bool system_enabled = static_cast<bool>(m_system); const bool system_enabled = static_cast<bool>(!System::IsShutdown());
if (ImGui::BeginMenu("System")) if (ImGui::BeginMenu("System"))
{ {
@ -653,9 +653,9 @@ void SDLHostInterface::DrawMainMenuBar()
ClearImGuiFocus(); 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(); ClearImGuiFocus();
} }
@ -669,7 +669,7 @@ void SDLHostInterface::DrawMainMenuBar()
if (ImGui::MenuItem("Remove Disc", nullptr, false, system_enabled)) if (ImGui::MenuItem("Remove Disc", nullptr, false, system_enabled))
{ {
RunLater([this]() { m_system->RemoveMedia(); }); RunLater([this]() { System::RemoveMedia(); });
ClearImGuiFocus(); ClearImGuiFocus();
} }
@ -752,21 +752,26 @@ void SDLHostInterface::DrawMainMenuBar()
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (m_system) if (!System::IsShutdown())
{ {
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x; 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::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::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)); 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)); const u32 rounded_speed = static_cast<u32>(std::round(speed));
if (speed < 90.0f) if (speed < 90.0f)
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed); 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::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed);
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (165.0f * framebuffer_scale)); 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::SetCursorPosX(ImGui::GetIO().DisplaySize.x - (80.0f * framebuffer_scale));
ImGui::Text("VPS: %.2f", m_system->GetVPS()); ImGui::Text("VPS: %.2f", 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");
} }
} }
@ -800,7 +800,7 @@ void SDLHostInterface::DrawQuickSettingsMenu()
if (ImGui::BeginMenu("CPU Execution Mode")) 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++) for (u32 i = 0; i < static_cast<u32>(CPUExecutionMode::Count); i++)
{ {
if (ImGui::MenuItem(Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i)), nullptr, if (ImGui::MenuItem(Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i)), nullptr,
@ -868,7 +868,7 @@ void SDLHostInterface::DrawQuickSettingsMenu()
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem("Dump Audio", nullptr, IsDumpingAudio(), HasSystem())) if (ImGui::MenuItem("Dump Audio", nullptr, IsDumpingAudio(), System::IsValid()))
{ {
if (!IsDumpingAudio()) if (!IsDumpingAudio())
StartDumpingAudio(); StartDumpingAudio();
@ -885,7 +885,7 @@ void SDLHostInterface::DrawQuickSettingsMenu()
void SDLHostInterface::DrawDebugMenu() void SDLHostInterface::DrawDebugMenu()
{ {
Settings::DebugSettings& debug_settings = m_settings.debugging; Settings::DebugSettings& debug_settings = g_settings.debugging;
bool settings_changed = false; bool settings_changed = false;
if (ImGui::BeginMenu("Log Level")) if (ImGui::BeginMenu("Log Level"))
@ -893,7 +893,7 @@ void SDLHostInterface::DrawDebugMenu()
for (u32 i = LOGLEVEL_NONE; i < LOGLEVEL_COUNT; i++) for (u32 i = LOGLEVEL_NONE; i < LOGLEVEL_COUNT; i++)
{ {
if (ImGui::MenuItem(Settings::GetLogLevelDisplayName(static_cast<LOGLEVEL>(i)), nullptr, 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); m_settings_copy.log_level = static_cast<LOGLEVEL>(i);
settings_changed = true; settings_changed = true;
@ -1455,7 +1455,7 @@ void SDLHostInterface::ClearImGuiFocus()
void SDLHostInterface::DoStartDisc() void SDLHostInterface::DoStartDisc()
{ {
Assert(!m_system); Assert(System::IsShutdown());
nfdchar_t* path = nullptr; nfdchar_t* path = nullptr;
if (!NFD_OpenDialog("bin,img,cue,chd,exe,psexe,psf", nullptr, &path) || !path || std::strlen(path) == 0) 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() void SDLHostInterface::DoChangeDisc()
{ {
Assert(m_system); Assert(!System::IsShutdown());
nfdchar_t* path = nullptr; nfdchar_t* path = nullptr;
if (!NFD_OpenDialog("bin,img,cue,chd", nullptr, &path) || !path || std::strlen(path) == 0) if (!NFD_OpenDialog("bin,img,cue,chd", nullptr, &path) || !path || std::strlen(path) == 0)
return; return;
if (m_system->InsertMedia(path)) if (System::InsertMedia(path))
AddFormattedOSDMessage(2.0f, "Switched CD to '%s'", path); AddFormattedOSDMessage(2.0f, "Switched CD to '%s'", path);
else else
AddOSDMessage("Failed to switch CD. The log may contain further information."); AddOSDMessage("Failed to switch CD. The log may contain further information.");
m_system->ResetPerformanceCounters(); System::ResetPerformanceCounters();
} }
void SDLHostInterface::Run() void SDLHostInterface::Run()
@ -1490,9 +1490,9 @@ void SDLHostInterface::Run()
{ {
PollAndUpdate(); PollAndUpdate();
if (m_system && !m_paused) if (System::IsRunning())
{ {
m_system->RunFrame(); System::RunFrame();
UpdateControllerRumble(); UpdateControllerRumble();
if (m_frame_step_request) if (m_frame_step_request)
{ {
@ -1505,28 +1505,28 @@ void SDLHostInterface::Run()
{ {
DrawImGuiWindows(); DrawImGuiWindows();
if (m_system) if (System::IsRunning())
m_system->GetGPU()->ResetGraphicsAPIState(); g_gpu->ResetGraphicsAPIState();
m_display->Render(); m_display->Render();
ImGui_ImplSDL2_NewFrame(m_window); ImGui_ImplSDL2_NewFrame(m_window);
ImGui::NewFrame(); ImGui::NewFrame();
if (m_system) if (System::IsRunning())
{ {
m_system->GetGPU()->RestoreGraphicsAPIState(); g_gpu->RestoreGraphicsAPIState();
m_system->UpdatePerformanceCounters(); System::UpdatePerformanceCounters();
if (m_speed_limiter_enabled) if (m_speed_limiter_enabled)
m_system->Throttle(); System::Throttle();
} }
} }
} }
// Save state on exit so it can be resumed // 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(); SaveResumeSaveState();
DestroySystem(); DestroySystem();
} }

View file

@ -12,7 +12,6 @@
#include <mutex> #include <mutex>
#include <string> #include <string>
class System;
class AudioStream; class AudioStream;
class INISettingsInterface; class INISettingsInterface;
@ -61,8 +60,6 @@ protected:
void UpdateInputMap() override; void UpdateInputMap() override;
private: private:
bool HasSystem() const { return static_cast<bool>(m_system); }
bool CreateSDLWindow(); bool CreateSDLWindow();
void DestroySDLWindow(); void DestroySDLWindow();
bool CreateDisplay(); bool CreateDisplay();

View file

@ -61,9 +61,9 @@ bool CommonHostInterface::Initialize()
Log_ErrorPrintf("Failed to set working directory to '%s'", m_user_directory.c_str()); Log_ErrorPrintf("Failed to set working directory to '%s'", m_user_directory.c_str());
LoadSettings(); LoadSettings();
UpdateLogSettings(m_settings.log_level, m_settings.log_filter.empty() ? nullptr : m_settings.log_filter.c_str(), UpdateLogSettings(g_settings.log_level, g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(),
m_settings.log_to_console, m_settings.log_to_debug, m_settings.log_to_window, g_settings.log_to_console, g_settings.log_to_debug, g_settings.log_to_window,
m_settings.log_to_file); g_settings.log_to_file);
m_game_list = std::make_unique<GameList>(); m_game_list = std::make_unique<GameList>();
m_game_list->SetCacheFilename(GetUserDirectoryRelativePath("cache/gamelist.cache")); m_game_list->SetCacheFilename(GetUserDirectoryRelativePath("cache/gamelist.cache"));
@ -102,7 +102,7 @@ void CommonHostInterface::Shutdown()
ShutdownDiscordPresence(); ShutdownDiscordPresence();
#endif #endif
m_system.reset(); System::Shutdown();
m_audio_stream.reset(); m_audio_stream.reset();
if (m_display) if (m_display)
ReleaseHostDisplay(); ReleaseHostDisplay();
@ -155,13 +155,13 @@ bool CommonHostInterface::BootSystem(const SystemBootParameters& parameters)
} }
// enter fullscreen if requested in the parameters // enter fullscreen if requested in the parameters
if (!m_settings.start_paused && ((parameters.override_fullscreen.has_value() && *parameters.override_fullscreen) || if (!g_settings.start_paused && ((parameters.override_fullscreen.has_value() && *parameters.override_fullscreen) ||
(!parameters.override_fullscreen.has_value() && m_settings.start_fullscreen))) (!parameters.override_fullscreen.has_value() && g_settings.start_fullscreen)))
{ {
SetFullscreen(true); SetFullscreen(true);
} }
if (m_settings.audio_dump_on_boot) if (g_settings.audio_dump_on_boot)
StartDumpingAudio(); StartDumpingAudio();
UpdateSpeedLimiterState(); UpdateSpeedLimiterState();
@ -170,30 +170,28 @@ bool CommonHostInterface::BootSystem(const SystemBootParameters& parameters)
void CommonHostInterface::PauseSystem(bool paused) void CommonHostInterface::PauseSystem(bool paused)
{ {
if (paused == m_paused || !m_system) if (paused == System::IsPaused() || System::IsShutdown())
return; return;
m_paused = paused; System::SetState(paused ? System::State::Paused : System::State::Running);
m_audio_stream->PauseOutput(m_paused); m_audio_stream->PauseOutput(paused);
OnSystemPaused(paused); OnSystemPaused(paused);
UpdateSpeedLimiterState(); UpdateSpeedLimiterState();
if (!paused) if (!paused)
m_system->ResetPerformanceCounters(); System::ResetPerformanceCounters();
} }
void CommonHostInterface::DestroySystem() void CommonHostInterface::DestroySystem()
{ {
SetTimerResolutionIncreased(false); SetTimerResolutionIncreased(false);
m_paused = false;
HostInterface::DestroySystem(); HostInterface::DestroySystem();
} }
void CommonHostInterface::PowerOffSystem() void CommonHostInterface::PowerOffSystem()
{ {
if (m_settings.save_state_on_exit) if (g_settings.save_state_on_exit)
SaveResumeSaveState(); SaveResumeSaveState();
HostInterface::PowerOffSystem(); HostInterface::PowerOffSystem();
@ -469,20 +467,20 @@ std::unique_ptr<ControllerInterface> CommonHostInterface::CreateControllerInterf
bool CommonHostInterface::LoadState(bool global, s32 slot) 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."); ReportFormattedError("Can't save per-game state without a running game code.");
return false; return false;
} }
std::string save_path = 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()); return LoadState(save_path.c_str());
} }
bool CommonHostInterface::SaveState(bool global, s32 slot) bool CommonHostInterface::SaveState(bool global, s32 slot)
{ {
const std::string& code = m_system->GetRunningCode(); const std::string& code = System::GetRunningCode();
if (!global && code.empty()) if (!global && code.empty())
{ {
ReportFormattedError("Can't save per-game state without a running game code."); 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)) if (!BootSystem(boot_params))
return false; return false;
const bool global = m_system->GetRunningCode().empty(); const bool global = System::GetRunningCode().empty();
if (global) if (global)
{ {
ReportFormattedError("Cannot resume system with undetectable game code from '%s'.", filename); ReportFormattedError("Cannot resume system with undetectable game code from '%s'.", filename);
@ -516,7 +514,7 @@ bool CommonHostInterface::ResumeSystemFromState(const char* filename, bool boot_
} }
else 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 (FileSystem::FileExists(path.c_str()))
{ {
if (!LoadState(path.c_str()) && !boot_on_failure) 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) else if (!boot_on_failure)
{ {
ReportFormattedError("Resume save state not found for '%s' ('%s').", m_system->GetRunningCode().c_str(), ReportFormattedError("Resume save state not found for '%s' ('%s').", System::GetRunningCode().c_str(),
m_system->GetRunningTitle().c_str()); System::GetRunningTitle().c_str());
DestroySystem(); DestroySystem();
return false; return false;
} }
@ -551,34 +549,36 @@ bool CommonHostInterface::ResumeSystemFromMostRecentState()
void CommonHostInterface::UpdateSpeedLimiterState() void CommonHostInterface::UpdateSpeedLimiterState()
{ {
if (!m_system || !m_audio_stream || !m_display) m_speed_limiter_enabled = g_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled;
return;
m_speed_limiter_enabled = m_settings.speed_limiter_enabled && !m_speed_limiter_temp_disabled; const bool is_non_standard_speed = (std::abs(g_settings.emulation_speed - 1.0f) > 0.05f);
const bool is_non_standard_speed = (std::abs(m_settings.emulation_speed - 1.0f) > 0.05f);
const bool audio_sync_enabled = 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 = 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" : "", Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "",
(audio_sync_enabled && video_sync_enabled) ? " and video" : (video_sync_enabled ? "video" : "")); (audio_sync_enabled && video_sync_enabled) ? " and video" : (video_sync_enabled ? "video" : ""));
if (m_audio_stream)
{
m_audio_stream->SetSync(audio_sync_enabled); m_audio_stream->SetSync(audio_sync_enabled);
if (audio_sync_enabled) if (audio_sync_enabled)
m_audio_stream->EmptyBuffers(); m_audio_stream->EmptyBuffers();
}
if (m_display)
m_display->SetVSync(video_sync_enabled); m_display->SetVSync(video_sync_enabled);
if (m_settings.increase_timer_resolution) if (g_settings.increase_timer_resolution)
SetTimerResolutionIncreased(m_speed_limiter_enabled); SetTimerResolutionIncreased(m_speed_limiter_enabled);
m_system->ResetPerformanceCounters(); if (System::IsValid())
System::ResetPerformanceCounters();
} }
void CommonHostInterface::RecreateSystem() void CommonHostInterface::RecreateSystem()
{ {
const bool was_paused = m_paused; const bool was_paused = System::IsPaused();
HostInterface::RecreateSystem(); HostInterface::RecreateSystem();
if (was_paused) if (was_paused)
PauseSystem(true); PauseSystem(true);
@ -588,12 +588,12 @@ void CommonHostInterface::UpdateLogSettings(LOGLEVEL level, const char* filter,
bool log_to_window, bool log_to_file) bool log_to_window, bool log_to_file)
{ {
Log::SetFilterLevel(level); Log::SetFilterLevel(level);
Log::SetConsoleOutputParams(m_settings.log_to_console, filter, level); Log::SetConsoleOutputParams(g_settings.log_to_console, filter, level);
Log::SetDebugOutputParams(m_settings.log_to_debug, filter, level); Log::SetDebugOutputParams(g_settings.log_to_debug, filter, level);
if (log_to_file) 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); filter, level);
} }
else else
@ -707,7 +707,7 @@ void CommonHostInterface::OnControllerTypeChanged(u32 slot)
void CommonHostInterface::DrawImGuiWindows() void CommonHostInterface::DrawImGuiWindows()
{ {
if (m_system) if (System::IsValid())
{ {
DrawDebugWindows(); DrawDebugWindows();
DrawFPSWindow(); DrawFPSWindow();
@ -721,7 +721,7 @@ void CommonHostInterface::DrawImGuiWindows()
void CommonHostInterface::DrawFPSWindow() 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; return;
const ImVec2 window_size = const ImVec2 window_size =
@ -739,12 +739,12 @@ void CommonHostInterface::DrawFPSWindow()
} }
bool first = true; 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; first = false;
} }
if (m_settings.display_show_vps) if (g_settings.display_show_vps)
{ {
if (first) if (first)
{ {
@ -757,9 +757,9 @@ void CommonHostInterface::DrawFPSWindow()
ImGui::SameLine(); 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) if (first)
{ {
@ -772,7 +772,7 @@ void CommonHostInterface::DrawFPSWindow()
ImGui::SameLine(); ImGui::SameLine();
} }
const float speed = m_system->GetEmulationSpeed(); const float speed = System::GetEmulationSpeed();
const u32 rounded_speed = static_cast<u32>(std::round(speed)); const u32 rounded_speed = static_cast<u32>(std::round(speed));
if (speed < 90.0f) if (speed < 90.0f)
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed); ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
@ -823,7 +823,7 @@ void CommonHostInterface::DrawOSDMessages()
continue; continue;
} }
if (!m_settings.display_show_osd_messages) if (!g_settings.display_show_osd_messages)
continue; continue;
const float opacity = std::min(time_remaining, 1.0f); const float opacity = std::min(time_remaining, 1.0f);
@ -848,27 +848,24 @@ void CommonHostInterface::DrawOSDMessages()
void CommonHostInterface::DrawDebugWindows() void CommonHostInterface::DrawDebugWindows()
{ {
const Settings::DebugSettings& debug_settings = m_system->GetSettings().debugging; if (g_settings.debugging.show_gpu_state)
g_gpu->DrawDebugStateWindow();
if (debug_settings.show_gpu_state) if (g_settings.debugging.show_cdrom_state)
m_system->GetGPU()->DrawDebugStateWindow(); g_cdrom.DrawDebugWindow();
if (debug_settings.show_cdrom_state) if (g_settings.debugging.show_timers_state)
m_system->GetCDROM()->DrawDebugWindow(); g_timers.DrawDebugStateWindow();
if (debug_settings.show_timers_state) if (g_settings.debugging.show_spu_state)
m_system->GetTimers()->DrawDebugStateWindow(); g_spu.DrawDebugStateWindow();
if (debug_settings.show_spu_state) if (g_settings.debugging.show_mdec_state)
m_system->GetSPU()->DrawDebugStateWindow(); g_mdec.DrawDebugStateWindow();
if (debug_settings.show_mdec_state)
m_system->GetMDEC()->DrawDebugStateWindow();
} }
void CommonHostInterface::DoFrameStep() void CommonHostInterface::DoFrameStep()
{ {
if (!m_system) if (System::IsShutdown())
return; return;
m_frame_step_request = true; m_frame_step_request = true;
if (m_paused)
PauseSystem(false); PauseSystem(false);
} }
@ -926,11 +923,9 @@ void CommonHostInterface::AddControllerRumble(u32 controller_index, u32 num_moto
void CommonHostInterface::UpdateControllerRumble() void CommonHostInterface::UpdateControllerRumble()
{ {
DebugAssert(m_system);
for (ControllerRumbleState& rumble : m_controller_vibration_motors) for (ControllerRumbleState& rumble : m_controller_vibration_motors)
{ {
Controller* controller = m_system->GetController(rumble.controller_index); Controller* controller = System::GetController(rumble.controller_index);
if (!controller) if (!controller)
continue; continue;
@ -984,7 +979,7 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
for (u32 controller_index = 0; controller_index < 2; controller_index++) 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) if (ctype == ControllerType::None)
continue; continue;
@ -1004,10 +999,10 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
continue; continue;
AddButtonToInputMap(binding, device, button, [this, controller_index, button_code](bool pressed) { AddButtonToInputMap(binding, device, button, [this, controller_index, button_code](bool pressed) {
if (!m_system) if (System::IsShutdown())
return; return;
Controller* controller = m_system->GetController(controller_index); Controller* controller = System::GetController(controller_index);
if (controller) if (controller)
controller->SetButtonState(button_code, pressed); controller->SetButtonState(button_code, pressed);
}); });
@ -1029,10 +1024,10 @@ void CommonHostInterface::UpdateControllerInputMap(SettingsInterface& si)
continue; continue;
AddAxisToInputMap(binding, device, axis, [this, controller_index, axis_code](float value) { AddAxisToInputMap(binding, device, axis, [this, controller_index, axis_code](float value) {
if (!m_system) if (System::IsShutdown())
return; return;
Controller* controller = m_system->GetController(controller_index); Controller* controller = System::GetController(controller_index);
if (controller) if (controller)
controller->SetAxisState(axis_code, value); controller->SetAxisState(axis_code, value);
}); });
@ -1253,23 +1248,23 @@ void CommonHostInterface::RegisterGeneralHotkeys()
RegisterHotkey(StaticString("General"), StaticString("TogglePause"), StaticString("Toggle Pause"), RegisterHotkey(StaticString("General"), StaticString("TogglePause"), StaticString("Toggle Pause"),
[this](bool pressed) { [this](bool pressed) {
if (!pressed) if (System::IsValid() && !pressed)
PauseSystem(!m_paused); PauseSystem(!System::IsPaused());
}); });
RegisterHotkey(StaticString("General"), StaticString("PowerOff"), StaticString("Power Off System"), RegisterHotkey(StaticString("General"), StaticString("PowerOff"), StaticString("Power Off System"),
[this](bool pressed) { [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?"); 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."); confirmation_message.AppendString("\n\nThe current state will be saved.");
if (!ConfirmMessage(confirmation_message)) if (!ConfirmMessage(confirmation_message))
{ {
m_system->ResetPerformanceCounters(); System::ResetPerformanceCounters();
return; return;
} }
} }
@ -1280,7 +1275,7 @@ void CommonHostInterface::RegisterGeneralHotkeys()
RegisterHotkey(StaticString("General"), StaticString("Screenshot"), StaticString("Save Screenshot"), RegisterHotkey(StaticString("General"), StaticString("Screenshot"), StaticString("Save Screenshot"),
[this](bool pressed) { [this](bool pressed) {
if (!pressed && m_system) if (!pressed && System::IsValid())
SaveScreenshot(); SaveScreenshot();
}); });
@ -1363,33 +1358,33 @@ void CommonHostInterface::RegisterSaveStateHotkeys()
void CommonHostInterface::RegisterAudioHotkeys() void CommonHostInterface::RegisterAudioHotkeys()
{ {
RegisterHotkey(StaticString("Audio"), StaticString("AudioMute"), StaticString("Toggle Mute"), [this](bool pressed) { 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; g_settings.audio_output_muted = !g_settings.audio_output_muted;
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.audio_output_muted) if (g_settings.audio_output_muted)
AddOSDMessage("Volume: Muted", 2.0f); AddOSDMessage("Volume: Muted", 2.0f);
else 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) { 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); g_settings.audio_output_volume = std::min<s32>(g_settings.audio_output_volume + 10, 100);
m_settings.audio_output_muted = false; g_settings.audio_output_muted = false;
m_audio_stream->SetOutputVolume(m_settings.audio_output_volume); m_audio_stream->SetOutputVolume(g_settings.audio_output_volume);
AddFormattedOSDMessage(2.0f, "Volume: %d%%", m_settings.audio_output_volume); AddFormattedOSDMessage(2.0f, "Volume: %d%%", g_settings.audio_output_volume);
} }
}); });
RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeDown"), StaticString("Volume Down"), RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeDown"), StaticString("Volume Down"),
[this](bool pressed) { [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); g_settings.audio_output_volume = std::max<s32>(g_settings.audio_output_volume - 10, 0);
m_settings.audio_output_muted = false; g_settings.audio_output_muted = false;
m_audio_stream->SetOutputVolume(m_settings.audio_output_volume); m_audio_stream->SetOutputVolume(g_settings.audio_output_volume);
AddFormattedOSDMessage(2.0f, "Volume: %d%%", m_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++) 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) if (ctype == ControllerType::None)
continue; continue;
@ -1483,7 +1478,7 @@ void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsIn
return; return;
} }
m_settings.controller_types[controller_index - 1] = *ctype; g_settings.controller_types[controller_index - 1] = *ctype;
HostInterface::OnControllerTypeChanged(controller_index - 1); HostInterface::OnControllerTypeChanged(controller_index - 1);
si.SetStringValue(section_name, "Type", Settings::GetControllerTypeName(*ctype)); si.SetStringValue(section_name, "Type", Settings::GetControllerTypeName(*ctype));
@ -1520,8 +1515,8 @@ void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsIn
} }
} }
if (m_system) if (System::IsValid())
m_system->UpdateControllers(); System::UpdateControllers();
UpdateInputMap(si); 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++) 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) if (ctype == ControllerType::None)
continue; continue;
@ -1802,32 +1797,27 @@ void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings)
{ {
HostInterface::CheckForSettingsChanges(old_settings); HostInterface::CheckForSettingsChanges(old_settings);
if (m_system) if (System::IsValid())
{ {
if (m_settings.audio_backend != old_settings.audio_backend || if (g_settings.audio_backend != old_settings.audio_backend ||
m_settings.audio_buffer_size != old_settings.audio_buffer_size) g_settings.audio_buffer_size != old_settings.audio_buffer_size ||
{ g_settings.video_sync_enabled != old_settings.video_sync_enabled ||
m_audio_stream->PauseOutput(m_paused); g_settings.audio_sync_enabled != old_settings.audio_sync_enabled ||
UpdateSpeedLimiterState(); 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)
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)
{ {
UpdateSpeedLimiterState(); UpdateSpeedLimiterState();
} }
} }
if (m_settings.log_level != old_settings.log_level || m_settings.log_filter != old_settings.log_filter || if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||
m_settings.log_to_console != old_settings.log_to_console || g_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) 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(), UpdateLogSettings(g_settings.log_level, g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(),
m_settings.log_to_console, m_settings.log_to_debug, m_settings.log_to_window, g_settings.log_to_console, g_settings.log_to_debug, g_settings.log_to_window,
m_settings.log_to_file); g_settings.log_to_file);
} }
UpdateInputMap(); UpdateInputMap();
@ -1912,27 +1902,27 @@ void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::str
bool CommonHostInterface::SaveResumeSaveState() bool CommonHostInterface::SaveResumeSaveState()
{ {
if (!m_system) if (System::IsShutdown())
return false; return false;
const bool global = m_system->GetRunningCode().empty(); const bool global = System::GetRunningCode().empty();
return SaveState(global, -1); return SaveState(global, -1);
} }
bool CommonHostInterface::IsDumpingAudio() const bool CommonHostInterface::IsDumpingAudio() const
{ {
return m_system ? m_system->GetSPU()->IsDumpingAudio() : false; return g_spu.IsDumpingAudio();
} }
bool CommonHostInterface::StartDumpingAudio(const char* filename) bool CommonHostInterface::StartDumpingAudio(const char* filename)
{ {
if (!m_system) if (System::IsShutdown())
return false; return false;
std::string auto_filename; std::string auto_filename;
if (!filename) if (!filename)
{ {
const auto& code = m_system->GetRunningCode(); const auto& code = System::GetRunningCode();
if (code.empty()) if (code.empty())
{ {
auto_filename = GetUserDirectoryRelativePath("dump/audio/%s.wav", GetTimestampStringForFileName().GetCharArray()); auto_filename = GetUserDirectoryRelativePath("dump/audio/%s.wav", GetTimestampStringForFileName().GetCharArray());
@ -1946,7 +1936,7 @@ bool CommonHostInterface::StartDumpingAudio(const char* filename)
filename = auto_filename.c_str(); 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); AddFormattedOSDMessage(5.0f, "Started dumping audio to '%s'.", filename);
return true; return true;
@ -1960,7 +1950,7 @@ bool CommonHostInterface::StartDumpingAudio(const char* filename)
void CommonHostInterface::StopDumpingAudio() void CommonHostInterface::StopDumpingAudio()
{ {
if (!m_system || !m_system->GetSPU()->StopDumpingAudio()) if (System::IsShutdown() || !g_spu.StopDumpingAudio())
return; return;
AddOSDMessage("Stopped dumping audio.", 5.0f); 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 CommonHostInterface::SaveScreenshot(const char* filename /* = nullptr */, bool full_resolution /* = true */,
bool apply_aspect_ratio /* = true */) bool apply_aspect_ratio /* = true */)
{ {
if (!m_system) if (System::IsShutdown())
return false; return false;
std::string auto_filename; std::string auto_filename;
if (!filename) if (!filename)
{ {
const auto& code = m_system->GetRunningCode(); const auto& code = System::GetRunningCode();
const char* extension = "png"; const char* extension = "png";
if (code.empty()) if (code.empty())
{ {
@ -1991,9 +1981,9 @@ bool CommonHostInterface::SaveScreenshot(const char* filename /* = nullptr */, b
filename = auto_filename.c_str(); 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); const bool screenshot_saved = m_display->WriteDisplayTextureToFile(filename, full_resolution, apply_aspect_ratio);
m_system->GetGPU()->RestoreGraphicsAPIState(); g_gpu->RestoreGraphicsAPIState();
if (!screenshot_saved) if (!screenshot_saved)
{ {
AddFormattedOSDMessage(10.0f, "Failed to save screenshot to '%s'", filename); AddFormattedOSDMessage(10.0f, "Failed to save screenshot to '%s'", filename);
@ -2052,10 +2042,10 @@ void CommonHostInterface::UpdateDiscordPresence()
rp.startTimestamp = std::time(nullptr); rp.startTimestamp = std::time(nullptr);
SmallString details_string; SmallString details_string;
if (m_system) if (System::IsValid())
{ {
details_string.AppendFormattedString("%s (%s)", m_system->GetRunningTitle().c_str(), details_string.AppendFormattedString("%s (%s)", System::GetRunningTitle().c_str(),
m_system->GetRunningCode().c_str()); System::GetRunningCode().c_str());
} }
else else
{ {

View file

@ -280,7 +280,6 @@ protected:
std::deque<OSDMessage> m_osd_messages; std::deque<OSDMessage> m_osd_messages;
std::mutex m_osd_messages_lock; std::mutex m_osd_messages_lock;
bool m_paused = false;
bool m_frame_step_request = false; bool m_frame_step_request = false;
bool m_speed_limiter_temp_disabled = false; bool m_speed_limiter_temp_disabled = false;
bool m_speed_limiter_enabled = false; bool m_speed_limiter_enabled = false;

View file

@ -21,17 +21,6 @@ void ControllerInterface::Shutdown()
m_host_interface = nullptr; 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) void ControllerInterface::SetHook(Hook::Callback callback)
{ {
std::unique_lock<std::mutex> lock(m_event_intercept_mutex); std::unique_lock<std::mutex> lock(m_event_intercept_mutex);

View file

@ -7,7 +7,6 @@
#include <mutex> #include <mutex>
class HostInterface; class HostInterface;
class System;
class Controller; class Controller;
class ControllerInterface class ControllerInterface
@ -75,8 +74,6 @@ public:
void ClearHook(); void ClearHook();
protected: protected:
System* GetSystem() const;
Controller* GetController(u32 slot) const;
bool DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value); bool DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value);
void OnControllerConnected(int host_id); void OnControllerConnected(int host_id);

View file

@ -42,13 +42,12 @@ void SaveStateSelectorUI::RefreshList()
{ {
ClearList(); ClearList();
const System* system = m_host_interface->GetSystem(); if (!System::GetRunningCode().empty())
if (system && !system->GetRunningCode().empty())
{ {
for (s32 i = 1; i <= CommonHostInterface::GLOBAL_SAVE_STATE_SLOTS; i++) for (s32 i = 1; i <= CommonHostInterface::GLOBAL_SAVE_STATE_SLOTS; i++)
{ {
std::optional<CommonHostInterface::ExtendedSaveStateInfo> ssi = 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; ListEntry li;
if (ssi) if (ssi)