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

View File

@ -10,8 +10,29 @@
#include <sys/mman.h>
#endif
JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_size /* = 0 */)
JitCodeBuffer::JitCodeBuffer() = default;
JitCodeBuffer::JitCodeBuffer(u32 size, u32 far_code_size)
{
if (!Allocate(size, far_code_size))
Panic("Failed to allocate code space");
}
JitCodeBuffer::JitCodeBuffer(void* buffer, u32 size, u32 far_code_size, u32 guard_pages)
{
if (!Initialize(buffer, size, far_code_size))
Panic("Failed to initialize code space");
}
JitCodeBuffer::~JitCodeBuffer()
{
Destroy();
}
bool JitCodeBuffer::Allocate(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_size /* = 0 */)
{
Destroy();
m_total_size = size + far_code_size;
#if defined(WIN32)
@ -22,6 +43,10 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz
#else
m_code_ptr = nullptr;
#endif
if (!m_code_ptr)
return false;
m_free_code_ptr = m_code_ptr;
m_code_size = size;
m_code_used = 0;
@ -31,17 +56,91 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz
m_far_code_size = far_code_size;
m_far_code_used = 0;
if (!m_code_ptr)
Panic("Failed to allocate code space.");
m_old_protection = 0;
m_owns_buffer = true;
return true;
}
JitCodeBuffer::~JitCodeBuffer()
bool JitCodeBuffer::Initialize(void* buffer, u32 size, u32 far_code_size /* = 0 */, u32 guard_size /* = 0 */)
{
Destroy();
if ((far_code_size > 0 && guard_size >= far_code_size) || (far_code_size + (guard_size * 2)) > size)
return false;
#if defined(WIN32)
VirtualFree(m_code_ptr, 0, MEM_RELEASE);
DWORD old_protect = 0;
if (!VirtualProtect(buffer, size, PAGE_EXECUTE_READWRITE, &old_protect))
return false;
if (guard_size > 0)
{
DWORD old_guard_protect = 0;
u8* guard_at_end = (static_cast<u8*>(buffer) + size) - guard_size;
if (!VirtualProtect(buffer, guard_size, PAGE_NOACCESS, &old_guard_protect) ||
!VirtualProtect(guard_at_end, guard_size, PAGE_NOACCESS, &old_guard_protect))
{
return false;
}
}
m_code_ptr = static_cast<u8*>(buffer);
m_old_protection = static_cast<u32>(old_protect);
#elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
munmap(m_code_ptr, m_total_size);
if (mprotect(buffer, size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
return false;
if (guard_size > 0)
{
u8* guard_at_end = (static_cast<u8*>(buffer) + size) - guard_size;
if (mprotect(buffer, guard_size, PROT_NONE) != 0 || mprotect(guard_at_end, guard_size, PROT_NONE) != 0)
return false;
}
// reasonable default?
m_code_ptr = static_cast<u8*>(buffer);
m_old_protection = PROT_READ | PROT_WRITE;
#else
m_code_ptr = nullptr;
#endif
if (!m_code_ptr)
return false;
m_total_size = size;
m_free_code_ptr = m_code_ptr + guard_size;
m_code_size = size - far_code_size - (guard_size * 2);
m_code_used = 0;
m_far_code_ptr = static_cast<u8*>(m_code_ptr) + m_code_size;
m_free_far_code_ptr = m_far_code_ptr;
m_far_code_size = far_code_size - guard_size;
m_far_code_used = 0;
m_guard_size = guard_size;
m_owns_buffer = false;
return true;
}
void JitCodeBuffer::Destroy()
{
if (m_owns_buffer)
{
#if defined(WIN32)
VirtualFree(m_code_ptr, 0, MEM_RELEASE);
#elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__)
munmap(m_code_ptr, m_total_size);
#endif
}
else if (m_code_ptr)
{
#if defined(WIN32)
DWORD old_protect = 0;
VirtualProtect(m_code_ptr, m_total_size, m_old_protection, &old_protect);
#else
mprotect(m_code_ptr, m_total_size, m_old_protection);
#endif
}
}
void JitCodeBuffer::CommitCode(u32 length)
@ -76,18 +175,18 @@ void JitCodeBuffer::CommitFarCode(u32 length)
void JitCodeBuffer::Reset()
{
std::memset(m_code_ptr, 0, m_code_size);
FlushInstructionCache(m_code_ptr, m_code_size);
m_free_code_ptr = m_code_ptr + m_guard_size;
m_code_used = 0;
std::memset(m_free_code_ptr, 0, m_code_size);
FlushInstructionCache(m_free_code_ptr, m_code_size);
if (m_far_code_size > 0)
{
std::memset(m_far_code_ptr, 0, m_far_code_size);
FlushInstructionCache(m_far_code_ptr, m_far_code_size);
m_free_far_code_ptr = m_far_code_ptr;
m_far_code_used = 0;
std::memset(m_free_far_code_ptr, 0, m_far_code_size);
FlushInstructionCache(m_free_far_code_ptr, m_far_code_size);
}
m_free_code_ptr = m_code_ptr;
m_code_used = 0;
m_free_far_code_ptr = m_far_code_ptr;
m_far_code_used = 0;
}
void JitCodeBuffer::Align(u32 alignment, u8 padding_value)

View File

@ -4,9 +4,14 @@
class JitCodeBuffer
{
public:
JitCodeBuffer(u32 size = 64 * 1024 * 1024, u32 far_code_size = 0);
JitCodeBuffer();
JitCodeBuffer(u32 size, u32 far_code_size);
JitCodeBuffer(void* buffer, u32 size, u32 far_code_size, u32 guard_size);
~JitCodeBuffer();
bool Allocate(u32 size = 64 * 1024 * 1024, u32 far_code_size = 0);
bool Initialize(void* buffer, u32 size, u32 far_code_size = 0, u32 guard_size = 0);
void Destroy();
void Reset();
u8* GetFreeCodePointer() const { return m_free_code_ptr; }
@ -25,16 +30,19 @@ public:
static void FlushInstructionCache(void* address, u32 size);
private:
u8* m_code_ptr;
u8* m_free_code_ptr;
u32 m_code_size;
u32 m_code_used;
u8* m_code_ptr = nullptr;
u8* m_free_code_ptr = nullptr;
u32 m_code_size = 0;
u32 m_code_used = 0;
u8* m_far_code_ptr;
u8* m_free_far_code_ptr;
u32 m_far_code_size;
u32 m_far_code_used;
u8* m_far_code_ptr = nullptr;
u8* m_free_far_code_ptr = nullptr;
u32 m_far_code_size = 0;
u32 m_far_code_used = 0;
u32 m_total_size;
u32 m_total_size = 0;
u32 m_guard_size = 0;
u32 m_old_protection = 0;
bool m_owns_buffer = false;
};

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
#pragma once
#include "common/bitfield.h"
#include "cpu_code_cache.h"
#include "types.h"
#include <array>
#include <bitset>
@ -8,288 +9,139 @@
class StateWrapper;
namespace CPU {
class Core;
class CodeCache;
} // namespace CPU
namespace Bus {
class DMA;
class InterruptController;
class GPU;
class CDROM;
class Pad;
class Timers;
class SPU;
class MDEC;
class SIO;
class System;
class Bus
enum : u32
{
friend DMA;
public:
enum : u32
{
RAM_BASE = 0x00000000,
RAM_SIZE = 0x200000,
RAM_MASK = RAM_SIZE - 1,
RAM_MIRROR_END = 0x800000,
EXP1_BASE = 0x1F000000,
EXP1_SIZE = 0x800000,
EXP1_MASK = EXP1_SIZE - 1,
MEMCTRL_BASE = 0x1F801000,
MEMCTRL_SIZE = 0x40,
MEMCTRL_MASK = MEMCTRL_SIZE - 1,
PAD_BASE = 0x1F801040,
PAD_SIZE = 0x10,
PAD_MASK = PAD_SIZE - 1,
SIO_BASE = 0x1F801050,
SIO_SIZE = 0x10,
SIO_MASK = SIO_SIZE - 1,
MEMCTRL2_BASE = 0x1F801060,
MEMCTRL2_SIZE = 0x10,
MEMCTRL2_MASK = MEMCTRL_SIZE - 1,
INTERRUPT_CONTROLLER_BASE = 0x1F801070,
INTERRUPT_CONTROLLER_SIZE = 0x10,
INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1,
DMA_BASE = 0x1F801080,
DMA_SIZE = 0x80,
DMA_MASK = DMA_SIZE - 1,
TIMERS_BASE = 0x1F801100,
TIMERS_SIZE = 0x40,
TIMERS_MASK = TIMERS_SIZE - 1,
CDROM_BASE = 0x1F801800,
CDROM_SIZE = 0x10,
CDROM_MASK = CDROM_SIZE - 1,
GPU_BASE = 0x1F801810,
GPU_SIZE = 0x10,
GPU_MASK = GPU_SIZE - 1,
MDEC_BASE = 0x1F801820,
MDEC_SIZE = 0x10,
MDEC_MASK = MDEC_SIZE - 1,
SPU_BASE = 0x1F801C00,
SPU_SIZE = 0x400,
SPU_MASK = 0x3FF,
EXP2_BASE = 0x1F802000,
EXP2_SIZE = 0x2000,
EXP2_MASK = EXP2_SIZE - 1,
BIOS_BASE = 0x1FC00000,
BIOS_SIZE = 0x80000
};
Bus();
~Bus();
void Initialize(CPU::Core* cpu, CPU::CodeCache* cpu_code_cache, DMA* dma, InterruptController* interrupt_controller,
GPU* gpu, CDROM* cdrom, Pad* pad, Timers* timers, SPU* spu, MDEC* mdec, SIO* sio);
void Reset();
bool DoState(StateWrapper& sw);
bool ReadByte(PhysicalMemoryAddress address, u8* value);
bool ReadHalfWord(PhysicalMemoryAddress address, u16* value);
bool ReadWord(PhysicalMemoryAddress address, u32* value);
bool WriteByte(PhysicalMemoryAddress address, u8 value);
bool WriteHalfWord(PhysicalMemoryAddress address, u16 value);
bool WriteWord(PhysicalMemoryAddress address, u32 value);
template<MemoryAccessType type, MemoryAccessSize size>
TickCount DispatchAccess(PhysicalMemoryAddress address, u32& value);
// Optimized variant for burst/multi-word read/writing.
TickCount ReadWords(PhysicalMemoryAddress address, u32* words, u32 word_count);
TickCount WriteWords(PhysicalMemoryAddress address, const u32* words, u32 word_count);
void SetExpansionROM(std::vector<u8> data);
void SetBIOS(const std::vector<u8>& image);
// changing interfaces
void SetGPU(GPU* gpu) { m_gpu = gpu; }
/// Returns the address which should be used for code caching (i.e. removes mirrors).
ALWAYS_INLINE static PhysicalMemoryAddress UnmirrorAddress(PhysicalMemoryAddress address)
{
// RAM
if (address < 0x800000)
return address & UINT32_C(0x1FFFFF);
else
return address;
}
/// Returns true if the address specified is cacheable (RAM or BIOS).
ALWAYS_INLINE static bool IsCacheableAddress(PhysicalMemoryAddress address)
{
return (address < RAM_MIRROR_END) || (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE));
}
/// Returns true if the address specified is writable (RAM).
ALWAYS_INLINE static bool IsRAMAddress(PhysicalMemoryAddress address) { return address < RAM_MIRROR_END; }
/// Flags a RAM region as code, so we know when to invalidate blocks.
ALWAYS_INLINE void SetRAMCodePage(u32 index) { m_ram_code_bits[index] = true; }
/// Unflags a RAM region as code, the code cache will no longer be notified when writes occur.
ALWAYS_INLINE void ClearRAMCodePage(u32 index) { m_ram_code_bits[index] = false; }
/// Clears all code bits for RAM regions.
ALWAYS_INLINE void ClearRAMCodePageFlags() { m_ram_code_bits.reset(); }
/// Direct access to RAM - used by DMA.
ALWAYS_INLINE u8* GetRAM() { return m_ram; }
private:
enum : u32
{
MEMCTRL_REG_COUNT = 9
};
union MEMDELAY
{
u32 bits;
BitField<u32, u8, 4, 4> access_time; // cycles
BitField<u32, bool, 8, 1> use_com0_time;
BitField<u32, bool, 9, 1> use_com1_time;
BitField<u32, bool, 10, 1> use_com2_time;
BitField<u32, bool, 11, 1> use_com3_time;
BitField<u32, bool, 12, 1> data_bus_16bit;
BitField<u32, u8, 16, 5> memory_window_size;
static constexpr u32 WRITE_MASK = 0b10101111'00011111'11111111'11111111;
};
union COMDELAY
{
u32 bits;
BitField<u32, u8, 0, 4> com0;
BitField<u32, u8, 4, 4> com1;
BitField<u32, u8, 8, 4> com2;
BitField<u32, u8, 12, 4> com3;
BitField<u32, u8, 16, 2> comunk;
static constexpr u32 WRITE_MASK = 0b00000000'00000011'11111111'11111111;
};
union MEMCTRL
{
u32 regs[MEMCTRL_REG_COUNT];
struct
{
u32 exp1_base;
u32 exp2_base;
MEMDELAY exp1_delay_size;
MEMDELAY exp3_delay_size;
MEMDELAY bios_delay_size;
MEMDELAY spu_delay_size;
MEMDELAY cdrom_delay_size;
MEMDELAY exp2_delay_size;
COMDELAY common_delay;
};
};
static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
void RecalculateMemoryTimings();
template<MemoryAccessType type, MemoryAccessSize size>
TickCount DoRAMAccess(u32 offset, u32& value);
template<MemoryAccessType type, MemoryAccessSize size>
TickCount DoBIOSAccess(u32 offset, u32& value);
TickCount DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress address, u32& value);
u32 DoReadEXP1(MemoryAccessSize size, u32 offset);
void DoWriteEXP1(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadEXP2(MemoryAccessSize size, u32 offset);
void DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadMemoryControl(MemoryAccessSize size, u32 offset);
void DoWriteMemoryControl(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadMemoryControl2(MemoryAccessSize size, u32 offset);
void DoWriteMemoryControl2(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadPad(MemoryAccessSize size, u32 offset);
void DoWritePad(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadSIO(MemoryAccessSize size, u32 offset);
void DoWriteSIO(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadCDROM(MemoryAccessSize size, u32 offset);
void DoWriteCDROM(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadGPU(MemoryAccessSize size, u32 offset);
void DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadMDEC(MemoryAccessSize size, u32 offset);
void DoWriteMDEC(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadInterruptController(MemoryAccessSize size, u32 offset);
void DoWriteInterruptController(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadDMA(MemoryAccessSize size, u32 offset);
void DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadTimers(MemoryAccessSize size, u32 offset);
void DoWriteTimers(MemoryAccessSize size, u32 offset, u32 value);
u32 DoReadSPU(MemoryAccessSize size, u32 offset);
void DoWriteSPU(MemoryAccessSize size, u32 offset, u32 value);
void DoInvalidateCodeCache(u32 page_index);
/// Returns the number of cycles stolen by DMA RAM access.
ALWAYS_INLINE static TickCount GetDMARAMTickCount(u32 word_count)
{
// DMA is using DRAM Hyper Page mode, allowing it to access DRAM rows at 1 clock cycle per word (effectively around
// 17 clks per 16 words, due to required row address loading, probably plus some further minimal overload due to
// refresh cycles). This is making DMA much faster than CPU memory accesses (CPU DRAM access takes 1 opcode cycle
// plus 6 waitstates, ie. 7 cycles in total).
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
}
/// Invalidates any code pages which overlap the specified range.
ALWAYS_INLINE void InvalidateCodePages(PhysicalMemoryAddress address, u32 word_count)
{
const u32 start_page = address / CPU_CODE_CACHE_PAGE_SIZE;
const u32 end_page = (address + word_count * sizeof(u32)) / CPU_CODE_CACHE_PAGE_SIZE;
for (u32 page = start_page; page <= end_page; page++)
{
if (m_ram_code_bits[page])
DoInvalidateCodeCache(page);
}
}
CPU::Core* m_cpu = nullptr;
CPU::CodeCache* m_cpu_code_cache = nullptr;
DMA* m_dma = nullptr;
InterruptController* m_interrupt_controller = nullptr;
GPU* m_gpu = nullptr;
CDROM* m_cdrom = nullptr;
Pad* m_pad = nullptr;
Timers* m_timers = nullptr;
SPU* m_spu = nullptr;
MDEC* m_mdec = nullptr;
SIO* m_sio = nullptr;
std::array<TickCount, 3> m_exp1_access_time = {};
std::array<TickCount, 3> m_exp2_access_time = {};
std::array<TickCount, 3> m_bios_access_time = {};
std::array<TickCount, 3> m_cdrom_access_time = {};
std::array<TickCount, 3> m_spu_access_time = {};
std::bitset<CPU_CODE_CACHE_PAGE_COUNT> m_ram_code_bits{};
u8 m_ram[RAM_SIZE]{}; // 2MB RAM
u8 m_bios[BIOS_SIZE]{}; // 512K BIOS ROM
std::vector<u8> m_exp1_rom;
MEMCTRL m_MEMCTRL = {};
u32 m_ram_size_reg = 0;
std::string m_tty_line_buffer;
RAM_BASE = 0x00000000,
RAM_SIZE = 0x200000,
RAM_MASK = RAM_SIZE - 1,
RAM_MIRROR_END = 0x800000,
EXP1_BASE = 0x1F000000,
EXP1_SIZE = 0x800000,
EXP1_MASK = EXP1_SIZE - 1,
MEMCTRL_BASE = 0x1F801000,
MEMCTRL_SIZE = 0x40,
MEMCTRL_MASK = MEMCTRL_SIZE - 1,
PAD_BASE = 0x1F801040,
PAD_SIZE = 0x10,
PAD_MASK = PAD_SIZE - 1,
SIO_BASE = 0x1F801050,
SIO_SIZE = 0x10,
SIO_MASK = SIO_SIZE - 1,
MEMCTRL2_BASE = 0x1F801060,
MEMCTRL2_SIZE = 0x10,
MEMCTRL2_MASK = MEMCTRL2_SIZE - 1,
INTERRUPT_CONTROLLER_BASE = 0x1F801070,
INTERRUPT_CONTROLLER_SIZE = 0x10,
INTERRUPT_CONTROLLER_MASK = INTERRUPT_CONTROLLER_SIZE - 1,
DMA_BASE = 0x1F801080,
DMA_SIZE = 0x80,
DMA_MASK = DMA_SIZE - 1,
TIMERS_BASE = 0x1F801100,
TIMERS_SIZE = 0x40,
TIMERS_MASK = TIMERS_SIZE - 1,
CDROM_BASE = 0x1F801800,
CDROM_SIZE = 0x10,
CDROM_MASK = CDROM_SIZE - 1,
GPU_BASE = 0x1F801810,
GPU_SIZE = 0x10,
GPU_MASK = GPU_SIZE - 1,
MDEC_BASE = 0x1F801820,
MDEC_SIZE = 0x10,
MDEC_MASK = MDEC_SIZE - 1,
SPU_BASE = 0x1F801C00,
SPU_SIZE = 0x400,
SPU_MASK = 0x3FF,
EXP2_BASE = 0x1F802000,
EXP2_SIZE = 0x2000,
EXP2_MASK = EXP2_SIZE - 1,
BIOS_BASE = 0x1FC00000,
BIOS_SIZE = 0x80000,
BIOS_MASK = 0x7FFFF,
};
#include "bus.inl"
enum : u32
{
MEMCTRL_REG_COUNT = 9
};
void Initialize();
void Shutdown();
void Reset();
bool DoState(StateWrapper& sw);
void SetExpansionROM(std::vector<u8> data);
void SetBIOS(const std::vector<u8>& image);
extern std::bitset<CPU_CODE_CACHE_PAGE_COUNT> m_ram_code_bits;
extern u8 g_ram[RAM_SIZE]; // 2MB RAM
extern u8 g_bios[BIOS_SIZE]; // 512K BIOS ROM
/// Returns the address which should be used for code caching (i.e. removes mirrors).
ALWAYS_INLINE PhysicalMemoryAddress UnmirrorAddress(PhysicalMemoryAddress address)
{
// RAM
if (address < 0x800000)
return address & UINT32_C(0x1FFFFF);
else
return address;
}
/// Returns true if the address specified is cacheable (RAM or BIOS).
ALWAYS_INLINE bool IsCacheableAddress(PhysicalMemoryAddress address)
{
return (address < RAM_MIRROR_END) || (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE));
}
/// Reads a cachable address (RAM or BIOS).
ALWAYS_INLINE u32 ReadCacheableAddress(PhysicalMemoryAddress address)
{
u32 value;
if (address < RAM_MIRROR_END)
{
std::memcpy(&value, &g_ram[address & RAM_MASK], sizeof(value));
return value;
}
else
{
std::memcpy(&value, &g_bios[address & BIOS_MASK], sizeof(value));
return value;
}
}
/// Returns true if the address specified is writable (RAM).
ALWAYS_INLINE bool IsRAMAddress(PhysicalMemoryAddress address) { return address < RAM_MIRROR_END; }
/// Flags a RAM region as code, so we know when to invalidate blocks.
ALWAYS_INLINE void SetRAMCodePage(u32 index) { m_ram_code_bits[index] = true; }
/// Unflags a RAM region as code, the code cache will no longer be notified when writes occur.
ALWAYS_INLINE void ClearRAMCodePage(u32 index) { m_ram_code_bits[index] = false; }
/// Clears all code bits for RAM regions.
ALWAYS_INLINE void ClearRAMCodePageFlags() { m_ram_code_bits.reset(); }
/// Returns the number of cycles stolen by DMA RAM access.
ALWAYS_INLINE TickCount GetDMARAMTickCount(u32 word_count)
{
// DMA is using DRAM Hyper Page mode, allowing it to access DRAM rows at 1 clock cycle per word (effectively around
// 17 clks per 16 words, due to required row address loading, probably plus some further minimal overload due to
// refresh cycles). This is making DMA much faster than CPU memory accesses (CPU DRAM access takes 1 opcode cycle
// plus 6 waitstates, ie. 7 cycles in total).
return static_cast<TickCount>(word_count + ((word_count + 15) / 16));
}
/// Invalidates any code pages which overlap the specified range.
ALWAYS_INLINE void InvalidateCodePages(PhysicalMemoryAddress address, u32 word_count)
{
const u32 start_page = address / CPU_CODE_CACHE_PAGE_SIZE;
const u32 end_page = (address + word_count * sizeof(u32)) / CPU_CODE_CACHE_PAGE_SIZE;
for (u32 page = start_page; page <= end_page; page++)
{
if (m_ram_code_bits[page])
CPU::CodeCache::InvalidateBlocksWithPageIndex(page);
}
}
} // namespace Bus

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

View File

@ -12,20 +12,16 @@
#include <vector>
class StateWrapper;
class System;
class TimingEvent;
class DMA;
class InterruptController;
class SPU;
class CDROM
class CDROM final
{
public:
CDROM();
~CDROM();
void Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, SPU* spu);
void Initialize();
void Shutdown();
void Reset();
bool DoState(StateWrapper& sw);
@ -276,10 +272,6 @@ private:
template<bool STEREO, bool SAMPLE_RATE>
void ResampleXAADPCM(const s16* frames_in, u32 num_frames_in);
System* m_system = nullptr;
DMA* m_dma = nullptr;
InterruptController* m_interrupt_controller = nullptr;
SPU* m_spu = nullptr;
std::unique_ptr<TimingEvent> m_command_event;
std::unique_ptr<TimingEvent> m_drive_event;
@ -347,5 +339,7 @@ private:
CDROMAsyncReader m_reader;
// two 16-bit samples packed in 32-bits
InlineFIFOQueue<u32, AUDIO_FIFO_SIZE> m_audio_fifo;
HeapFIFOQueue<u32, AUDIO_FIFO_SIZE> m_audio_fifo;
};
extern CDROM g_cdrom;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,187 +1,229 @@
#include "cpu_code_cache.h"
#include "bus.h"
#include "common/log.h"
#include "cpu_core.h"
#include "cpu_disasm.h"
#include "system.h"
#include "timing_event.h"
Log_SetChannel(CPU::CodeCache);
#ifdef WITH_RECOMPILER
#include "cpu_recompiler_code_generator.h"
#include "cpu_recompiler_thunks.h"
#endif
namespace CPU {
namespace CPU::CodeCache {
constexpr bool USE_BLOCK_LINKING = true;
#ifdef WITH_RECOMPILER
static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 32 * 1024 * 1024;
static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 32 * 1024 * 1024;
static constexpr u32 RECOMPILER_GUARD_SIZE = 4096;
alignas(Recompiler::CODE_STORAGE_ALIGNMENT) static u8
s_code_storage[RECOMPILER_CODE_CACHE_SIZE + RECOMPILER_FAR_CODE_CACHE_SIZE];
static JitCodeBuffer s_code_buffer;
#endif
CodeCache::CodeCache() = default;
using BlockMap = std::unordered_map<u32, CodeBlock*>;
CodeCache::~CodeCache()
void LogCurrentState();
/// Returns the block key for the current execution state.
static CodeBlockKey GetNextBlockKey();
/// Looks up the block in the cache if it's already been compiled.
static CodeBlock* LookupBlock(CodeBlockKey key);
/// Can the current block execute? This will re-validate the block if necessary.
/// The block can also be flushed if recompilation failed, so ignore the pointer if false is returned.
static bool RevalidateBlock(CodeBlock* block);
static bool CompileBlock(CodeBlock* block);
static void FlushBlock(CodeBlock* block);
static void AddBlockToPageMap(CodeBlock* block);
static void RemoveBlockFromPageMap(CodeBlock* block);
/// Link block from to to.
static void LinkBlock(CodeBlock* from, CodeBlock* to);
/// Unlink all blocks which point to this block, and any that this block links to.
static void UnlinkBlock(CodeBlock* block);
static bool s_use_recompiler = false;
static BlockMap s_blocks;
static std::array<std::vector<CodeBlock*>, CPU_CODE_CACHE_PAGE_COUNT> m_ram_block_map;
void Initialize(bool use_recompiler)
{
if (m_system)
Flush();
}
void CodeCache::Initialize(System* system, Core* core, Bus* bus, bool use_recompiler)
{
m_system = system;
m_core = core;
m_bus = bus;
Assert(s_blocks.empty());
#ifdef WITH_RECOMPILER
m_use_recompiler = use_recompiler;
m_code_buffer = std::make_unique<JitCodeBuffer>(RECOMPILER_CODE_CACHE_SIZE, RECOMPILER_FAR_CODE_CACHE_SIZE);
m_asm_functions = std::make_unique<Recompiler::ASMFunctions>();
m_asm_functions->Generate(m_code_buffer.get());
s_use_recompiler = use_recompiler;
if (!s_code_buffer.Initialize(s_code_storage, sizeof(s_code_storage), RECOMPILER_FAR_CODE_CACHE_SIZE,
RECOMPILER_GUARD_SIZE))
{
Panic("Failed to initialize code space");
}
#else
m_use_recompiler = false;
s_use_recompiler = false;
#endif
}
void CodeCache::Execute()
void Shutdown()
{
CodeBlockKey next_block_key = GetNextBlockKey();
Flush();
s_code_buffer.Destroy();
}
while (m_core->m_pending_ticks < m_core->m_downcount)
void Execute()
{
CodeBlockKey next_block_key;
g_state.frame_done = false;
while (!g_state.frame_done)
{
if (m_core->HasPendingInterrupt())
{
// TODO: Fill in m_next_instruction...
m_core->SafeReadMemoryWord(m_core->m_regs.pc, &m_core->m_next_instruction.bits);
m_core->DispatchInterrupt();
next_block_key = GetNextBlockKey();
}
CodeBlock* block = LookupBlock(next_block_key);
if (!block)
{
Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", m_core->GetRegs().pc);
InterpretUncachedBlock();
continue;
}
reexecute_block:
#if 0
const u32 tick = m_system->GetGlobalTickCounter() + m_core->GetPendingTicks();
if (tick == 61033207)
__debugbreak();
#endif
#if 0
LogCurrentState();
#endif
if (m_use_recompiler)
block->host_code(m_core);
else
InterpretCachedBlock(*block);
if (m_core->m_pending_ticks >= m_core->m_downcount)
break;
else if (m_core->HasPendingInterrupt() || !USE_BLOCK_LINKING)
continue;
TimingEvents::UpdateCPUDowncount();
next_block_key = GetNextBlockKey();
if (next_block_key.bits == block->key.bits)
while (g_state.pending_ticks < g_state.downcount)
{
// we can jump straight to it if there's no pending interrupts
// ensure it's not a self-modifying block
if (!block->invalidated || RevalidateBlock(block))
goto reexecute_block;
}
else if (!block->invalidated)
{
// Try to find an already-linked block.
// TODO: Don't need to dereference the block, just store a pointer to the code.
for (CodeBlock* linked_block : block->link_successors)
if (HasPendingInterrupt())
{
if (linked_block->key.bits == next_block_key.bits)
{
if (linked_block->invalidated && !RevalidateBlock(linked_block))
{
// CanExecuteBlock can result in a block flush, so stop iterating here.
break;
}
// TODO: Fill in m_next_instruction...
SafeReadMemoryWord(g_state.regs.pc, &g_state.next_instruction.bits);
DispatchInterrupt();
next_block_key = GetNextBlockKey();
}
// Execute the linked block
block = linked_block;
CodeBlock* block = LookupBlock(next_block_key);
if (!block)
{
Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", g_state.regs.pc);
InterpretUncachedBlock();
continue;
}
reexecute_block:
#if 0
const u32 tick = g_system->GetGlobalTickCounter() + m_core->GetPendingTicks();
if (tick == 61033207)
__debugbreak();
#endif
#if 0
LogCurrentState();
#endif
if (s_use_recompiler)
block->host_code();
else
InterpretCachedBlock(*block);
if (g_state.pending_ticks >= g_state.downcount)
break;
else if (HasPendingInterrupt() || !USE_BLOCK_LINKING)
continue;
next_block_key = GetNextBlockKey();
if (next_block_key.bits == block->key.bits)
{
// we can jump straight to it if there's no pending interrupts
// ensure it's not a self-modifying block
if (!block->invalidated || RevalidateBlock(block))
goto reexecute_block;
}
else if (!block->invalidated)
{
// Try to find an already-linked block.
// TODO: Don't need to dereference the block, just store a pointer to the code.
for (CodeBlock* linked_block : block->link_successors)
{
if (linked_block->key.bits == next_block_key.bits)
{
if (linked_block->invalidated && !RevalidateBlock(linked_block))
{
// CanExecuteBlock can result in a block flush, so stop iterating here.
break;
}
// Execute the linked block
block = linked_block;
goto reexecute_block;
}
}
// No acceptable blocks found in the successor list, try a new one.
CodeBlock* next_block = LookupBlock(next_block_key);
if (next_block)
{
// Link the previous block to this new block if we find a new block.
LinkBlock(block, next_block);
block = next_block;
goto reexecute_block;
}
}
// No acceptable blocks found in the successor list, try a new one.
CodeBlock* next_block = LookupBlock(next_block_key);
if (next_block)
{
// Link the previous block to this new block if we find a new block.
LinkBlock(block, next_block);
block = next_block;
goto reexecute_block;
}
}
TimingEvents::RunEvents();
}
// in case we switch to interpreter...
m_core->m_regs.npc = m_core->m_regs.pc;
g_state.regs.npc = g_state.regs.pc;
}
void CodeCache::SetUseRecompiler(bool enable)
void SetUseRecompiler(bool enable)
{
#ifdef WITH_RECOMPILER
if (m_use_recompiler == enable)
if (s_use_recompiler == enable)
return;
m_use_recompiler = enable;
s_use_recompiler = enable;
Flush();
#endif
}
void CodeCache::Flush()
void Flush()
{
m_bus->ClearRAMCodePageFlags();
Bus::ClearRAMCodePageFlags();
for (auto& it : m_ram_block_map)
it.clear();
for (const auto& it : m_blocks)
for (const auto& it : s_blocks)
delete it.second;
m_blocks.clear();
s_blocks.clear();
#ifdef WITH_RECOMPILER
m_code_buffer->Reset();
s_code_buffer.Reset();
#endif
}
void CodeCache::LogCurrentState()
void LogCurrentState()
{
const auto& regs = m_core->m_regs;
const auto& regs = g_state.regs;
WriteToExecutionLog("tick=%u pc=%08X zero=%08X at=%08X v0=%08X v1=%08X a0=%08X a1=%08X a2=%08X a3=%08X t0=%08X "
"t1=%08X t2=%08X t3=%08X t4=%08X t5=%08X t6=%08X t7=%08X s0=%08X s1=%08X s2=%08X s3=%08X s4=%08X "
"s5=%08X s6=%08X s7=%08X t8=%08X t9=%08X k0=%08X k1=%08X gp=%08X sp=%08X fp=%08X ra=%08X ldr=%s "
"ldv=%08X\n",
m_system->GetGlobalTickCounter() + m_core->GetPendingTicks(), regs.pc, regs.zero, regs.at,
regs.v0, regs.v1, regs.a0, regs.a1, regs.a2, regs.a3, regs.t0, regs.t1, regs.t2, regs.t3, regs.t4,
regs.t5, regs.t6, regs.t7, regs.s0, regs.s1, regs.s2, regs.s3, regs.s4, regs.s5, regs.s6, regs.s7,
regs.t8, regs.t9, regs.k0, regs.k1, regs.gp, regs.sp, regs.fp, regs.ra,
(m_core->m_next_load_delay_reg == Reg::count) ? "NONE" :
GetRegName(m_core->m_next_load_delay_reg),
(m_core->m_next_load_delay_reg == Reg::count) ? 0 : m_core->m_next_load_delay_value);
TimingEvents::GetGlobalTickCounter() + GetPendingTicks(), regs.pc, regs.zero, regs.at, regs.v0,
regs.v1, regs.a0, regs.a1, regs.a2, regs.a3, regs.t0, regs.t1, regs.t2, regs.t3, regs.t4, regs.t5,
regs.t6, regs.t7, regs.s0, regs.s1, regs.s2, regs.s3, regs.s4, regs.s5, regs.s6, regs.s7, regs.t8,
regs.t9, regs.k0, regs.k1, regs.gp, regs.sp, regs.fp, regs.ra,
(g_state.next_load_delay_reg == Reg::count) ? "NONE" : GetRegName(g_state.next_load_delay_reg),
(g_state.next_load_delay_reg == Reg::count) ? 0 : g_state.next_load_delay_value);
}
CodeBlockKey CodeCache::GetNextBlockKey() const
CodeBlockKey GetNextBlockKey()
{
CodeBlockKey key = {};
key.SetPC(m_core->GetRegs().pc);
key.user_mode = m_core->InUserMode();
key.SetPC(g_state.regs.pc);
key.user_mode = InUserMode();
return key;
}
CodeBlock* CodeCache::LookupBlock(CodeBlockKey key)
CodeBlock* LookupBlock(CodeBlockKey key)
{
BlockMap::iterator iter = m_blocks.find(key.bits);
if (iter != m_blocks.end())
BlockMap::iterator iter = s_blocks.find(key.bits);
if (iter != s_blocks.end())
{
// ensure it hasn't been invalidated
CodeBlock* existing_block = iter->second;
@ -202,17 +244,15 @@ CodeBlock* CodeCache::LookupBlock(CodeBlockKey key)
block = nullptr;
}
iter = m_blocks.emplace(key.bits, block).first;
iter = s_blocks.emplace(key.bits, block).first;
return block;
}
bool CodeCache::RevalidateBlock(CodeBlock* block)
bool RevalidateBlock(CodeBlock* block)
{
for (const CodeBlockInstruction& cbi : block->instructions)
{
u32 new_code = 0;
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(cbi.pc & PHYSICAL_MEMORY_ADDRESS_MASK,
new_code);
u32 new_code = Bus::ReadCacheableAddress(cbi.pc & PHYSICAL_MEMORY_ADDRESS_MASK);
if (cbi.instruction.bits != new_code)
{
Log_DebugPrintf("Block 0x%08X changed at PC 0x%08X - %08X to %08X - recompiling.", block->GetPC(), cbi.pc,
@ -242,7 +282,7 @@ recompile:
return true;
}
bool CodeCache::CompileBlock(CodeBlock* block)
bool CompileBlock(CodeBlock* block)
{
u32 pc = block->GetPC();
bool is_branch_delay_slot = false;
@ -258,12 +298,12 @@ bool CodeCache::CompileBlock(CodeBlock* block)
CodeBlockInstruction cbi = {};
const PhysicalMemoryAddress phys_addr = pc & PHYSICAL_MEMORY_ADDRESS_MASK;
if (!m_bus->IsCacheableAddress(phys_addr) ||
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(phys_addr, cbi.instruction.bits) < 0 ||
!IsInvalidInstruction(cbi.instruction))
{
if (!Bus::IsCacheableAddress(phys_addr))
break;
cbi.instruction.bits = Bus::ReadCacheableAddress(phys_addr);
if (!IsInvalidInstruction(cbi.instruction))
break;
}
cbi.pc = pc;
cbi.is_branch_delay_slot = is_branch_delay_slot;
@ -272,7 +312,7 @@ bool CodeCache::CompileBlock(CodeBlock* block)
cbi.is_load_instruction = IsMemoryLoadInstruction(cbi.instruction);
cbi.is_store_instruction = IsMemoryStoreInstruction(cbi.instruction);
cbi.has_load_delay = InstructionHasLoadDelay(cbi.instruction);
cbi.can_trap = CanInstructionTrap(cbi.instruction, m_core->InUserMode());
cbi.can_trap = CanInstructionTrap(cbi.instruction, InUserMode());
// instruction is decoded now
block->instructions.push_back(cbi);
@ -316,19 +356,19 @@ bool CodeCache::CompileBlock(CodeBlock* block)
}
#ifdef WITH_RECOMPILER
if (m_use_recompiler)
if (s_use_recompiler)
{
// Ensure we're not going to run out of space while compiling this block.
if (m_code_buffer->GetFreeCodeSpace() <
if (s_code_buffer.GetFreeCodeSpace() <
(block->instructions.size() * Recompiler::MAX_NEAR_HOST_BYTES_PER_INSTRUCTION) ||
m_code_buffer->GetFreeFarCodeSpace() <
s_code_buffer.GetFreeFarCodeSpace() <
(block->instructions.size() * Recompiler::MAX_FAR_HOST_BYTES_PER_INSTRUCTION))
{
Log_WarningPrintf("Out of code space, flushing all blocks.");
Flush();
}
Recompiler::CodeGenerator codegen(m_core, m_code_buffer.get(), *m_asm_functions.get());
Recompiler::CodeGenerator codegen(&s_code_buffer);
if (!codegen.CompileBlock(block, &block->host_code, &block->host_code_size))
{
Log_ErrorPrintf("Failed to compile host code for block at 0x%08X", block->key.GetPC());
@ -340,7 +380,7 @@ bool CodeCache::CompileBlock(CodeBlock* block)
return true;
}
void CodeCache::InvalidateBlocksWithPageIndex(u32 page_index)
void InvalidateBlocksWithPageIndex(u32 page_index)
{
DebugAssert(page_index < CPU_CODE_CACHE_PAGE_COUNT);
auto& blocks = m_ram_block_map[page_index];
@ -353,24 +393,26 @@ void CodeCache::InvalidateBlocksWithPageIndex(u32 page_index)
// Block will be re-added next execution.
blocks.clear();
m_bus->ClearRAMCodePage(page_index);
Bus::ClearRAMCodePage(page_index);
}
void CodeCache::FlushBlock(CodeBlock* block)
void FlushBlock(CodeBlock* block)
{
BlockMap::iterator iter = m_blocks.find(block->key.GetPC());
Assert(iter != m_blocks.end() && iter->second == block);
BlockMap::iterator iter = s_blocks.find(block->key.GetPC());
Assert(iter != s_blocks.end() && iter->second == block);
Log_DevPrintf("Flushing block at address 0x%08X", block->GetPC());
// if it's been invalidated it won't be in the page map
if (block->invalidated)
RemoveBlockFromPageMap(block);
m_blocks.erase(iter);
UnlinkBlock(block);
s_blocks.erase(iter);
delete block;
}
void CodeCache::AddBlockToPageMap(CodeBlock* block)
void AddBlockToPageMap(CodeBlock* block)
{
if (!block->IsInRAM())
return;
@ -380,11 +422,11 @@ void CodeCache::AddBlockToPageMap(CodeBlock* block)
for (u32 page = start_page; page <= end_page; page++)
{
m_ram_block_map[page].push_back(block);
m_bus->SetRAMCodePage(page);
Bus::SetRAMCodePage(page);
}
}
void CodeCache::RemoveBlockFromPageMap(CodeBlock* block)
void RemoveBlockFromPageMap(CodeBlock* block)
{
if (!block->IsInRAM())
return;
@ -400,14 +442,14 @@ void CodeCache::RemoveBlockFromPageMap(CodeBlock* block)
}
}
void CodeCache::LinkBlock(CodeBlock* from, CodeBlock* to)
void LinkBlock(CodeBlock* from, CodeBlock* to)
{
Log_DebugPrintf("Linking block %p(%08x) to %p(%08x)", from, from->GetPC(), to, to->GetPC());
from->link_successors.push_back(to);
to->link_predecessors.push_back(from);
}
void CodeCache::UnlinkBlock(CodeBlock* block)
void UnlinkBlock(CodeBlock* block)
{
for (CodeBlock* predecessor : block->link_predecessors)
{
@ -426,82 +468,4 @@ void CodeCache::UnlinkBlock(CodeBlock* block)
block->link_successors.clear();
}
void CodeCache::InterpretCachedBlock(const CodeBlock& block)
{
// set up the state so we've already fetched the instruction
DebugAssert(m_core->m_regs.pc == block.GetPC());
m_core->m_regs.npc = block.GetPC() + 4;
for (const CodeBlockInstruction& cbi : block.instructions)
{
m_core->m_pending_ticks++;
// now executing the instruction we previously fetched
m_core->m_current_instruction.bits = cbi.instruction.bits;
m_core->m_current_instruction_pc = cbi.pc;
m_core->m_current_instruction_in_branch_delay_slot = cbi.is_branch_delay_slot;
m_core->m_current_instruction_was_branch_taken = m_core->m_branch_was_taken;
m_core->m_branch_was_taken = false;
m_core->m_exception_raised = false;
// update pc
m_core->m_regs.pc = m_core->m_regs.npc;
m_core->m_regs.npc += 4;
// execute the instruction we previously fetched
m_core->ExecuteInstruction();
// next load delay
m_core->UpdateLoadDelay();
if (m_core->m_exception_raised)
break;
}
// cleanup so the interpreter can kick in if needed
m_core->m_next_instruction_is_branch_delay_slot = false;
}
void CodeCache::InterpretUncachedBlock()
{
Panic("Fixme with regards to re-fetching PC");
// At this point, pc contains the last address executed (in the previous block). The instruction has not been fetched
// yet. pc shouldn't be updated until the fetch occurs, that way the exception occurs in the delay slot.
bool in_branch_delay_slot = false;
for (;;)
{
m_core->m_pending_ticks++;
// now executing the instruction we previously fetched
m_core->m_current_instruction.bits = m_core->m_next_instruction.bits;
m_core->m_current_instruction_pc = m_core->m_regs.pc;
m_core->m_current_instruction_in_branch_delay_slot = m_core->m_next_instruction_is_branch_delay_slot;
m_core->m_current_instruction_was_branch_taken = m_core->m_branch_was_taken;
m_core->m_next_instruction_is_branch_delay_slot = false;
m_core->m_branch_was_taken = false;
m_core->m_exception_raised = false;
// Fetch the next instruction, except if we're in a branch delay slot. The "fetch" is done in the next block.
if (!m_core->FetchInstruction())
break;
// execute the instruction we previously fetched
m_core->ExecuteInstruction();
// next load delay
m_core->UpdateLoadDelay();
const bool branch = IsBranchInstruction(m_core->m_current_instruction);
if (m_core->m_exception_raised || (!branch && in_branch_delay_slot) ||
IsExitBlockInstruction(m_core->m_current_instruction))
{
break;
}
in_branch_delay_slot = branch;
}
}
} // namespace CPU
} // namespace CPU::CodeCache

View File

@ -1,22 +1,13 @@
#pragma once
#include "common/bitfield.h"
#include "common/jit_code_buffer.h"
#include "cpu_types.h"
#include <array>
#include <memory>
#include <unordered_map>
#include <vector>
class JitCodeBuffer;
class Bus;
class System;
namespace CPU {
class Core;
namespace Recompiler {
class ASMFunctions;
}
union CodeBlockKey
{
@ -58,7 +49,7 @@ struct CodeBlockInstruction
struct CodeBlock
{
using HostCodePointer = void (*)(Core*);
using HostCodePointer = void (*)();
CodeBlock(const CodeBlockKey key_) : key(key_) {}
@ -86,67 +77,24 @@ struct CodeBlock
}
};
class CodeCache
{
public:
CodeCache();
~CodeCache();
namespace CodeCache {
void Initialize(System* system, Core* core, Bus* bus, bool use_recompiler);
void Execute();
void Initialize(bool use_recompiler);
void Shutdown();
void Execute();
/// Flushes the code cache, forcing all blocks to be recompiled.
void Flush();
/// Flushes the code cache, forcing all blocks to be recompiled.
void Flush();
/// Changes whether the recompiler is enabled.
void SetUseRecompiler(bool enable);
/// Changes whether the recompiler is enabled.
void SetUseRecompiler(bool enable);
/// Invalidates all blocks which are in the range of the specified code page.
void InvalidateBlocksWithPageIndex(u32 page_index);
/// Invalidates all blocks which are in the range of the specified code page.
void InvalidateBlocksWithPageIndex(u32 page_index);
private:
using BlockMap = std::unordered_map<u32, CodeBlock*>;
void InterpretCachedBlock(const CodeBlock& block);
void InterpretUncachedBlock();
void LogCurrentState();
}; // namespace CodeCache
/// Returns the block key for the current execution state.
CodeBlockKey GetNextBlockKey() const;
/// Looks up the block in the cache if it's already been compiled.
CodeBlock* LookupBlock(CodeBlockKey key);
/// Can the current block execute? This will re-validate the block if necessary.
/// The block can also be flushed if recompilation failed, so ignore the pointer if false is returned.
bool RevalidateBlock(CodeBlock* block);
bool CompileBlock(CodeBlock* block);
void FlushBlock(CodeBlock* block);
void AddBlockToPageMap(CodeBlock* block);
void RemoveBlockFromPageMap(CodeBlock* block);
/// Link block from to to.
void LinkBlock(CodeBlock* from, CodeBlock* to);
/// Unlink all blocks which point to this block, and any that this block links to.
void UnlinkBlock(CodeBlock* block);
void InterpretCachedBlock(const CodeBlock& block);
void InterpretUncachedBlock();
System* m_system = nullptr;
Core* m_core = nullptr;
Bus* m_bus = nullptr;
#ifdef WITH_RECOMPILER
std::unique_ptr<JitCodeBuffer> m_code_buffer;
std::unique_ptr<Recompiler::ASMFunctions> m_asm_functions;
#endif
BlockMap m_blocks;
bool m_use_recompiler = false;
std::array<std::vector<CodeBlock*>, CPU_CODE_CACHE_PAGE_COUNT> m_ram_block_map;
};
} // namespace CPU
} // namespace CPU

File diff suppressed because it is too large Load Diff

View File

@ -1,186 +1,105 @@
#pragma once
#include "common/bitfield.h"
#include "cpu_types.h"
#include "gte.h"
#include "gte_types.h"
#include "types.h"
#include <array>
#include <optional>
class StateWrapper;
class Bus;
class System;
namespace CPU {
class CodeCache;
namespace Recompiler {
class CodeGenerator;
class Thunks;
} // namespace Recompiler
class Core
enum : VirtualMemoryAddress
{
public:
static constexpr VirtualMemoryAddress RESET_VECTOR = UINT32_C(0xBFC00000);
static constexpr PhysicalMemoryAddress DCACHE_LOCATION = UINT32_C(0x1F800000);
static constexpr PhysicalMemoryAddress DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00);
static constexpr PhysicalMemoryAddress DCACHE_OFFSET_MASK = UINT32_C(0x000003FF);
static constexpr PhysicalMemoryAddress DCACHE_SIZE = UINT32_C(0x00000400);
friend CodeCache;
friend Recompiler::CodeGenerator;
friend Recompiler::Thunks;
Core();
~Core();
void Initialize(Bus* bus);
void Reset();
bool DoState(StateWrapper& sw);
void Execute();
ALWAYS_INLINE Bus* GetBus() const { return m_bus; }
ALWAYS_INLINE const Registers& GetRegs() const { return m_regs; }
ALWAYS_INLINE Registers& GetRegs() { return m_regs; }
ALWAYS_INLINE TickCount GetPendingTicks() const { return m_pending_ticks; }
ALWAYS_INLINE void ResetPendingTicks() { m_pending_ticks = 0; }
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { m_pending_ticks += ticks; }
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
ALWAYS_INLINE void SetDowncount(TickCount downcount) { m_downcount = downcount; }
ALWAYS_INLINE const GTE::Core& GetCop2() const { return m_cop2; }
ALWAYS_INLINE GTE::Core& GetCop2() { return m_cop2; }
// Sets the PC and flushes the pipeline.
void SetPC(u32 new_pc);
// Memory reads variants which do not raise exceptions.
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
// External IRQs
void SetExternalInterrupt(u8 bit);
void ClearExternalInterrupt(u8 bit);
private:
template<MemoryAccessType type, MemoryAccessSize size>
TickCount DoMemoryAccess(VirtualMemoryAddress address, u32& value);
template<MemoryAccessType type, MemoryAccessSize size>
bool DoAlignmentCheck(VirtualMemoryAddress address);
template<MemoryAccessType type, MemoryAccessSize size>
void DoScratchpadAccess(PhysicalMemoryAddress address, u32& value);
bool ReadMemoryByte(VirtualMemoryAddress addr, u8* value);
bool ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
bool ReadMemoryWord(VirtualMemoryAddress addr, u32* value);
bool WriteMemoryByte(VirtualMemoryAddress addr, u8 value);
bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
bool WriteMemoryWord(VirtualMemoryAddress addr, u32 value);
// state helpers
ALWAYS_INLINE bool InUserMode() const { return m_cop0_regs.sr.KUc; }
ALWAYS_INLINE bool InKernelMode() const { return !m_cop0_regs.sr.KUc; }
void DisassembleAndPrint(u32 addr);
void DisassembleAndLog(u32 addr);
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
// Updates load delays - call after each instruction
ALWAYS_INLINE void UpdateLoadDelay()
{
// the old value is needed in case the delay slot instruction overwrites the same register
if (m_load_delay_reg != Reg::count)
m_regs.r[static_cast<u8>(m_load_delay_reg)] = m_load_delay_value;
m_load_delay_reg = m_next_load_delay_reg;
m_load_delay_value = m_next_load_delay_value;
m_next_load_delay_reg = Reg::count;
}
// Fetches the instruction at m_regs.npc
bool FetchInstruction();
void ExecuteInstruction();
void ExecuteCop0Instruction();
void ExecuteCop2Instruction();
void Branch(u32 target);
// exceptions
u32 GetExceptionVector(Exception excode) const;
void RaiseException(Exception excode);
void RaiseException(Exception excode, u32 EPC, bool BD, bool BT, u8 CE);
bool HasPendingInterrupt();
void DispatchInterrupt();
// clears pipeline of load/branch delays
void FlushPipeline();
// helper functions for registers which aren't writable
u32 ReadReg(Reg rs);
void WriteReg(Reg rd, u32 value);
// helper for generating a load delay write
void WriteRegDelayed(Reg rd, u32 value);
// write to cache control register
void WriteCacheControl(u32 value);
// read/write cop0 regs
std::optional<u32> ReadCop0Reg(Cop0Reg reg);
void WriteCop0Reg(Cop0Reg reg, u32 value);
Bus* m_bus = nullptr;
// ticks the CPU has executed
TickCount m_pending_ticks = 0;
TickCount m_downcount = MAX_SLICE_SIZE;
Registers m_regs = {};
Cop0Registers m_cop0_regs = {};
Instruction m_next_instruction = {};
// address of the instruction currently being executed
Instruction m_current_instruction = {};
u32 m_current_instruction_pc = 0;
bool m_current_instruction_in_branch_delay_slot = false;
bool m_current_instruction_was_branch_taken = false;
bool m_next_instruction_is_branch_delay_slot = false;
bool m_branch_was_taken = false;
bool m_exception_raised = false;
bool m_interrupt_delay = false;
// load delays
Reg m_load_delay_reg = Reg::count;
u32 m_load_delay_value = 0;
Reg m_next_load_delay_reg = Reg::count;
u32 m_next_load_delay_value = 0;
u32 m_cache_control = 0;
System* m_system = nullptr;
// data cache (used as scratchpad)
std::array<u8, DCACHE_SIZE> m_dcache = {};
GTE::Core m_cop2;
RESET_VECTOR = UINT32_C(0xBFC00000)
};
enum : PhysicalMemoryAddress
{
DCACHE_LOCATION = UINT32_C(0x1F800000),
DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00),
DCACHE_OFFSET_MASK = UINT32_C(0x000003FF),
DCACHE_SIZE = UINT32_C(0x00000400)
};
extern bool TRACE_EXECUTION;
extern bool LOG_EXECUTION;
struct State
{
// ticks the CPU has executed
TickCount pending_ticks = 0;
TickCount downcount = MAX_SLICE_SIZE;
Registers regs = {};
Cop0Registers cop0_regs = {};
Instruction next_instruction = {};
// address of the instruction currently being executed
Instruction current_instruction = {};
u32 current_instruction_pc = 0;
bool current_instruction_in_branch_delay_slot = false;
bool current_instruction_was_branch_taken = false;
bool next_instruction_is_branch_delay_slot = false;
bool branch_was_taken = false;
bool exception_raised = false;
bool interrupt_delay = false;
bool frame_done = false;
// load delays
Reg load_delay_reg = Reg::count;
u32 load_delay_value = 0;
Reg next_load_delay_reg = Reg::count;
u32 next_load_delay_value = 0;
u32 cache_control = 0;
// GTE registers are stored here so we can access them on ARM with a single instruction
GTE::Regs gte_regs = {};
// data cache (used as scratchpad)
std::array<u8, DCACHE_SIZE> dcache = {};
};
extern State g_state;
void Initialize();
void Shutdown();
void Reset();
bool DoState(StateWrapper& sw);
/// Executes interpreter loop.
void Execute();
ALWAYS_INLINE Registers& GetRegs() { return g_state.regs; }
ALWAYS_INLINE TickCount GetPendingTicks() { return g_state.pending_ticks; }
ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; }
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { g_state.pending_ticks += ticks; }
// state helpers
ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; }
ALWAYS_INLINE bool InKernelMode() { return !g_state.cop0_regs.sr.KUc; }
// Memory reads variants which do not raise exceptions.
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
// External IRQs
void SetExternalInterrupt(u8 bit);
void ClearExternalInterrupt(u8 bit);
bool HasPendingInterrupt();
void DispatchInterrupt();
void DisassembleAndPrint(u32 addr);
void DisassembleAndLog(u32 addr);
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
// Write to CPU execution log file.
void WriteToExecutionLog(const char* format, ...);
} // namespace CPU
extern bool TRACE_EXECUTION;
extern bool LOG_EXECUTION;
#include "cpu_core.inl"
} // namespace CPU

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_core.h"
#include "common/assert.h"
#include <array>
namespace CPU {
@ -166,7 +167,7 @@ static const std::array<std::pair<CopCommonInstruction, const char*>, 5> s_cop_c
static const std::array<std::pair<Cop0Instruction, const char*>, 1> s_cop0_table = {{{Cop0Instruction::rfe, "rfe"}}};
static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core* state, const char* format)
static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Registers* regs, const char* format)
{
dest->Clear();
@ -185,10 +186,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
if (std::strncmp(str, "rs", 2) == 0)
{
dest->AppendString(GetRegName(inst.r.rs));
if (state)
if (regs)
{
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rs),
state->GetRegs().r[static_cast<u8>(inst.r.rs.GetValue())]);
regs->r[static_cast<u8>(inst.r.rs.GetValue())]);
}
str += 2;
@ -196,10 +197,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
else if (std::strncmp(str, "rt", 2) == 0)
{
dest->AppendString(GetRegName(inst.r.rt));
if (state)
if (regs)
{
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rt),
state->GetRegs().r[static_cast<u8>(inst.r.rt.GetValue())]);
regs->r[static_cast<u8>(inst.r.rt.GetValue())]);
}
str += 2;
@ -207,10 +208,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
else if (std::strncmp(str, "rd", 2) == 0)
{
dest->AppendString(GetRegName(inst.r.rd));
if (state)
if (regs)
{
comment.AppendFormattedString("%s%s=0x%08X", comment.IsEmpty() ? "" : ", ", GetRegName(inst.r.rd),
state->GetRegs().r[static_cast<u8>(inst.r.rd.GetValue())]);
regs->r[static_cast<u8>(inst.r.rd.GetValue())]);
}
str += 2;
@ -241,10 +242,10 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
{
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
dest->AppendFormattedString("%d(%s)", offset, GetRegName(inst.i.rs));
if (state)
if (regs)
{
comment.AppendFormattedString("%saddr=0x%08X", comment.IsEmpty() ? "" : ", ",
state->GetRegs().r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
regs->r[static_cast<u8>(inst.i.rs.GetValue())] + offset);
}
str += 8;
@ -291,14 +292,14 @@ static void FormatInstruction(String* dest, const Instruction inst, u32 pc, Core
}
template<typename T>
void FormatCopInstruction(String* dest, u32 pc, Core* state, const Instruction inst,
void FormatCopInstruction(String* dest, u32 pc, Registers* regs, const Instruction inst,
const std::pair<T, const char*>* table, size_t table_size, T table_key)
{
for (size_t i = 0; i < table_size; i++)
{
if (table[i].first == table_key)
{
FormatInstruction(dest, inst, pc, state, table[i].second);
FormatInstruction(dest, inst, pc, regs, table[i].second);
return;
}
}
@ -306,13 +307,13 @@ void FormatCopInstruction(String* dest, u32 pc, Core* state, const Instruction i
dest->Format("<cop%u 0x%08X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
}
void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
void DisassembleInstruction(String* dest, u32 pc, u32 bits, Registers* regs)
{
const Instruction inst{bits};
switch (inst.op)
{
case InstructionOp::funct:
FormatInstruction(dest, inst, pc, state, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
FormatInstruction(dest, inst, pc, regs, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
return;
case InstructionOp::cop0:
@ -322,7 +323,7 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
{
if (inst.cop.IsCommonInstruction())
{
FormatCopInstruction(dest, pc, state, inst, s_cop_common_table.data(), s_cop_common_table.size(),
FormatCopInstruction(dest, pc, regs, inst, s_cop_common_table.data(), s_cop_common_table.size(),
inst.cop.CommonOp());
}
else
@ -331,7 +332,7 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
{
case InstructionOp::cop0:
{
FormatCopInstruction(dest, pc, state, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
FormatCopInstruction(dest, pc, regs, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.Cop0Op());
}
break;
@ -355,14 +356,14 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits, Core* state)
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1));
if (link)
FormatInstruction(dest, inst, pc, state, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
FormatInstruction(dest, inst, pc, regs, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
else
FormatInstruction(dest, inst, pc, state, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
FormatInstruction(dest, inst, pc, regs, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
}
break;
default:
FormatInstruction(dest, inst, pc, state, s_base_table[static_cast<u8>(inst.op.GetValue())]);
FormatInstruction(dest, inst, pc, regs, s_base_table[static_cast<u8>(inst.op.GetValue())]);
break;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
#include "cpu_core.h"
#include "cpu_recompiler_code_generator.h"
#include "cpu_recompiler_thunks.h"
#include "common/align.h"
namespace CPU::Recompiler {
@ -73,8 +74,8 @@ static const Xbyak::Reg64 GetCPUPtrReg()
return GetHostReg64(RCPUPTR);
}
CodeGenerator::CodeGenerator(Core* cpu, JitCodeBuffer* code_buffer, const ASMFunctions& asm_functions)
: m_cpu(cpu), m_code_buffer(code_buffer), m_asm_functions(asm_functions), m_register_cache(*this),
CodeGenerator::CodeGenerator(JitCodeBuffer* code_buffer)
: m_code_buffer(code_buffer), m_register_cache(*this),
m_near_emitter(code_buffer->GetFreeCodeSpace(), code_buffer->GetFreeCodePointer()),
m_far_emitter(code_buffer->GetFreeFarCodeSpace(), code_buffer->GetFreeFarCodePointer()), m_emit(&m_near_emitter)
{
@ -187,7 +188,7 @@ void CodeGenerator::EmitBeginBlock()
// Store the CPU struct pointer.
const bool cpu_reg_allocated = m_register_cache.AllocateHostReg(RCPUPTR);
DebugAssert(cpu_reg_allocated);
m_emit->mov(GetCPUPtrReg(), GetHostReg64(RARG1));
m_emit->mov(GetCPUPtrReg(), reinterpret_cast<size_t>(&g_state));
}
void CodeGenerator::EmitEndBlock()
@ -1305,8 +1306,15 @@ void CodeGenerator::EmitFunctionCallPtr(Value* return_value, const void* ptr)
const u32 adjust_size = PrepareStackForCall();
// actually call the function
m_emit->mov(GetHostReg64(RRETURN), reinterpret_cast<size_t>(ptr));
m_emit->call(GetHostReg64(RRETURN));
if (Xbyak::inner::IsInInt32(reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(m_emit->getCurr())))
{
m_emit->call(ptr);
}
else
{
m_emit->mov(GetHostReg64(RRETURN), reinterpret_cast<size_t>(ptr));
m_emit->call(GetHostReg64(RRETURN));
}
// shadow space release
RestoreStackAfterCall(adjust_size);
@ -1645,15 +1653,15 @@ Value CodeGenerator::EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const
switch (size)
{
case RegSize_8:
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), pc, address);
EmitFunctionCall(&result, &Thunks::ReadMemoryByte, pc, address);
break;
case RegSize_16:
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address);
EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, pc, address);
break;
case RegSize_32:
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), pc, address);
EmitFunctionCall(&result, &Thunks::ReadMemoryWord, pc, address);
break;
default:
@ -1706,15 +1714,15 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
switch (value.size)
{
case RegSize_8:
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), pc, address, value);
EmitFunctionCall(&result, &Thunks::WriteMemoryByte, pc, address, value);
break;
case RegSize_16:
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address, value);
EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, pc, address, value);
break;
case RegSize_32:
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), pc, address, value);
EmitFunctionCall(&result, &Thunks::WriteMemoryWord, pc, address, value);
break;
default:
@ -1735,14 +1743,208 @@ void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const
m_register_cache.PopState();
}
void CodeGenerator::EmitLoadGlobal(HostReg host_reg, RegSize size, const void* ptr)
{
const s64 displacement =
static_cast<s64>(reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(m_emit->getCurr())) + 2;
if (Xbyak::inner::IsInInt32(static_cast<u64>(displacement)))
{
switch (size)
{
case RegSize_8:
m_emit->mov(GetHostReg8(host_reg), m_emit->byte[m_emit->rip + ptr]);
break;
case RegSize_16:
m_emit->mov(GetHostReg16(host_reg), m_emit->word[m_emit->rip + ptr]);
break;
case RegSize_32:
m_emit->mov(GetHostReg32(host_reg), m_emit->dword[m_emit->rip + ptr]);
break;
case RegSize_64:
m_emit->mov(GetHostReg64(host_reg), m_emit->qword[m_emit->rip + ptr]);
break;
default:
{
UnreachableCode();
}
break;
}
}
else
{
Value temp = m_register_cache.AllocateScratch(RegSize_64);
m_emit->mov(GetHostReg64(temp), reinterpret_cast<size_t>(ptr));
switch (size)
{
case RegSize_8:
m_emit->mov(GetHostReg8(host_reg), m_emit->byte[GetHostReg64(temp)]);
break;
case RegSize_16:
m_emit->mov(GetHostReg16(host_reg), m_emit->word[GetHostReg64(temp)]);
break;
case RegSize_32:
m_emit->mov(GetHostReg32(host_reg), m_emit->dword[GetHostReg64(temp)]);
break;
case RegSize_64:
m_emit->mov(GetHostReg64(host_reg), m_emit->qword[GetHostReg64(temp)]);
break;
default:
{
UnreachableCode();
}
break;
}
}
}
void CodeGenerator::EmitStoreGlobal(void* ptr, const Value& value)
{
DebugAssert(value.IsInHostRegister() || value.IsConstant());
const s64 displacement =
static_cast<s64>(reinterpret_cast<size_t>(ptr) - reinterpret_cast<size_t>(m_emit->getCurr()));
if (Xbyak::inner::IsInInt32(static_cast<u64>(displacement)))
{
switch (value.size)
{
case RegSize_8:
{
if (value.IsConstant())
m_emit->mov(m_emit->byte[m_emit->rip + ptr], value.constant_value);
else
m_emit->mov(m_emit->byte[m_emit->rip + ptr], GetHostReg8(value.host_reg));
}
break;
case RegSize_16:
{
if (value.IsConstant())
m_emit->mov(m_emit->word[m_emit->rip + ptr], value.constant_value);
else
m_emit->mov(m_emit->word[m_emit->rip + ptr], GetHostReg16(value.host_reg));
}
break;
case RegSize_32:
{
if (value.IsConstant())
m_emit->mov(m_emit->dword[m_emit->rip + ptr], value.constant_value);
else
m_emit->mov(m_emit->dword[m_emit->rip + ptr], GetHostReg32(value.host_reg));
}
break;
case RegSize_64:
{
if (value.IsConstant())
{
// we need a temporary to load the value if it doesn't fit in 32-bits
if (!Xbyak::inner::IsInInt32(value.constant_value))
{
Value temp = m_register_cache.AllocateScratch(RegSize_64);
EmitCopyValue(temp.host_reg, value);
m_emit->mov(m_emit->qword[m_emit->rip + ptr], GetHostReg64(temp.host_reg));
}
else
{
m_emit->mov(m_emit->qword[m_emit->rip + ptr], value.constant_value);
}
}
else
{
m_emit->mov(m_emit->qword[m_emit->rip + ptr], GetHostReg64(value.host_reg));
}
}
break;
default:
{
UnreachableCode();
}
break;
}
}
else
{
Value address_temp = m_register_cache.AllocateScratch(RegSize_64);
m_emit->mov(GetHostReg64(address_temp), reinterpret_cast<size_t>(ptr));
switch (value.size)
{
case RegSize_8:
{
if (value.IsConstant())
m_emit->mov(m_emit->byte[GetHostReg64(address_temp)], value.constant_value);
else
m_emit->mov(m_emit->byte[GetHostReg64(address_temp)], GetHostReg8(value.host_reg));
}
break;
case RegSize_16:
{
if (value.IsConstant())
m_emit->mov(m_emit->word[GetHostReg64(address_temp)], value.constant_value);
else
m_emit->mov(m_emit->word[GetHostReg64(address_temp)], GetHostReg16(value.host_reg));
}
break;
case RegSize_32:
{
if (value.IsConstant())
m_emit->mov(m_emit->dword[GetHostReg64(address_temp)], value.constant_value);
else
m_emit->mov(m_emit->dword[GetHostReg64(address_temp)], GetHostReg32(value.host_reg));
}
break;
case RegSize_64:
{
if (value.IsConstant())
{
// we need a temporary to load the value if it doesn't fit in 32-bits
if (!Xbyak::inner::IsInInt32(value.constant_value))
{
Value temp = m_register_cache.AllocateScratch(RegSize_64);
EmitCopyValue(temp.host_reg, value);
m_emit->mov(m_emit->qword[GetHostReg64(address_temp)], GetHostReg64(temp.host_reg));
}
else
{
m_emit->mov(m_emit->qword[GetHostReg64(address_temp)], value.constant_value);
}
}
else
{
m_emit->mov(m_emit->qword[GetHostReg64(address_temp)], GetHostReg64(value.host_reg));
}
}
break;
default:
{
UnreachableCode();
}
break;
}
}
}
void CodeGenerator::EmitFlushInterpreterLoadDelay()
{
Value reg = m_register_cache.AllocateScratch(RegSize_8);
Value value = m_register_cache.AllocateScratch(RegSize_32);
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_load_delay_reg)];
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_value)];
auto reg_ptr = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_regs.r[0]) + GetHostReg64(reg.host_reg) * 4];
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, load_delay_value)];
auto reg_ptr = m_emit->dword[GetCPUPtrReg() + offsetof(State, regs.r[0]) + GetHostReg64(reg.host_reg) * 4];
Xbyak::Label skip_flush;
@ -1768,10 +1970,10 @@ void CodeGenerator::EmitMoveNextInterpreterLoadDelay()
Value reg = m_register_cache.AllocateScratch(RegSize_8);
Value value = m_register_cache.AllocateScratch(RegSize_32);
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_load_delay_reg)];
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_load_delay_value)];
auto next_load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_reg)];
auto next_load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(Core, m_next_load_delay_value)];
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
auto load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, load_delay_value)];
auto next_load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, next_load_delay_reg)];
auto next_load_delay_value = m_emit->dword[GetCPUPtrReg() + offsetof(State, next_load_delay_value)];
m_emit->mov(GetHostReg32(value), next_load_delay_value);
m_emit->mov(GetHostReg8(reg), next_load_delay_reg);
@ -1785,7 +1987,7 @@ void CodeGenerator::EmitCancelInterpreterLoadDelayForReg(Reg reg)
if (!m_load_delay_dirty)
return;
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(Core, m_load_delay_reg)];
auto load_delay_reg = m_emit->byte[GetCPUPtrReg() + offsetof(State, load_delay_reg)];
Xbyak::Label skip_cancel;
@ -2047,6 +2249,4 @@ void CodeGenerator::EmitBindLabel(LabelType* label)
m_emit->L(*label);
}
void ASMFunctions::Generate(JitCodeBuffer* code_buffer) {}
} // namespace CPU::Recompiler

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -205,7 +205,7 @@ bool GPU::HandleInterruptRequestCommand()
if (!m_GPUSTAT.interrupt_request)
{
m_GPUSTAT.interrupt_request = true;
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::GPU);
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::GPU);
}
m_fifo.RemoveOne();
@ -496,7 +496,7 @@ bool GPU::HandleCopyRectangleCPUToVRAMCommand()
void GPU::FinishVRAMWrite()
{
if (m_system->GetSettings().debugging.dump_cpu_to_vram_copies)
if (g_settings.debugging.dump_cpu_to_vram_copies)
{
DumpVRAMToFile(StringUtil::StdStringFromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++).c_str(),
m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * m_vram_transfer.width,
@ -535,7 +535,7 @@ bool GPU::HandleCopyRectangleVRAMToCPUCommand()
// ensure VRAM shadow is up to date
ReadVRAM(m_vram_transfer.x, m_vram_transfer.y, m_vram_transfer.width, m_vram_transfer.height);
if (m_system->GetSettings().debugging.dump_vram_to_cpu_copies)
if (g_settings.debugging.dump_vram_to_cpu_copies)
{
DumpVRAMToFile(StringUtil::StdStringFromFormat("vram_to_cpu_copy_%u.png", s_vram_to_cpu_dump_id++).c_str(),
m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * VRAM_WIDTH,

View File

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

View File

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

View File

@ -19,8 +19,7 @@ GPU_HW_D3D11::~GPU_HW_D3D11()
}
}
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dma,
InterruptController* interrupt_controller, Timers* timers)
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display)
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
{
@ -30,7 +29,7 @@ bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dm
SetCapabilities();
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
if (!GPU_HW::Initialize(host_display))
return false;
m_device = static_cast<ID3D11Device*>(host_display->GetRenderDevice());
@ -38,8 +37,8 @@ bool GPU_HW_D3D11::Initialize(HostDisplay* host_display, System* system, DMA* dm
if (!m_device || !m_context)
return false;
m_shader_cache.Open(system->GetHostInterface()->GetShaderCacheBasePath(), m_device->GetFeatureLevel(),
system->GetSettings().gpu_use_debug_device);
m_shader_cache.Open(g_host_interface->GetShaderCacheBasePath(), m_device->GetFeatureLevel(),
g_settings.gpu_use_debug_device);
if (!CreateFramebuffer())
{
@ -370,7 +369,7 @@ bool GPU_HW_D3D11::CompileShaders()
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_true_color, m_scaled_dithering,
m_texture_filtering, m_supports_dual_source_blend);
m_system->GetHostInterface()->DisplayLoadingScreen("Compiling shaders...");
g_host_interface->DisplayLoadingScreen("Compiling shaders...");
m_screen_quad_vertex_shader =
m_shader_cache.GetVertexShader(m_device.Get(), shadergen.GenerateScreenQuadVertexShader());
@ -572,7 +571,7 @@ void GPU_HW_D3D11::UpdateDisplay()
{
GPU_HW::UpdateDisplay();
if (m_system->GetSettings().debugging.show_vram)
if (g_settings.debugging.show_vram)
{
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), m_vram_texture.GetWidth(), m_vram_texture.GetHeight(),
0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
@ -789,7 +788,4 @@ void GPU_HW_D3D11::UpdateDepthBufferFromMaskBit()
RestoreGraphicsAPIState();
}
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer()
{
return std::make_unique<GPU_HW_D3D11>();
}
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer() { return std::make_unique<GPU_HW_D3D11>(); }

View File

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

View File

@ -27,8 +27,7 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
}
}
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* dma,
InterruptController* interrupt_controller, Timers* timers)
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display)
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL &&
host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGLES)
@ -39,9 +38,9 @@ bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display, System* system, DMA* d
SetCapabilities(host_display);
m_shader_cache.Open(IsGLES(), system->GetHostInterface()->GetShaderCacheBasePath());
m_shader_cache.Open(IsGLES(), g_host_interface->GetShaderCacheBasePath());
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
if (!GPU_HW::Initialize(host_display))
return false;
if (!CreateFramebuffer())
@ -337,7 +336,7 @@ bool GPU_HW_OpenGL::CompilePrograms()
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_true_color, m_scaled_dithering,
m_texture_filtering, m_supports_dual_source_blend);
m_system->GetHostInterface()->DisplayLoadingScreen("Compiling Shaders...");
g_host_interface->DisplayLoadingScreen("Compiling Shaders...");
for (u32 render_mode = 0; render_mode < 4; render_mode++)
{
@ -580,7 +579,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
{
GPU_HW::UpdateDisplay();
if (m_system->GetSettings().debugging.show_vram)
if (g_settings.debugging.show_vram)
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
m_vram_texture.GetWidth(), static_cast<s32>(m_vram_texture.GetHeight()), 0,

View File

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

View File

@ -25,8 +25,7 @@ GPU_HW_Vulkan::~GPU_HW_Vulkan()
DestroyResources();
}
bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display, System* system, DMA* dma,
InterruptController* interrupt_controller, Timers* timers)
bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::Vulkan)
{
@ -37,7 +36,7 @@ bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display, System* system, DMA* d
Assert(g_vulkan_shader_cache);
SetCapabilities();
if (!GPU_HW::Initialize(host_display, system, dma, interrupt_controller, timers))
if (!GPU_HW::Initialize(host_display))
return false;
if (!CreatePipelineLayouts())
@ -570,7 +569,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
{VK_PRIMITIVE_TOPOLOGY_LINE_LIST, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST}};
static constexpr std::array<VkPolygonMode, 2> polygon_mode_mapping = {{VK_POLYGON_MODE_LINE, VK_POLYGON_MODE_FILL}};
m_system->GetHostInterface()->DisplayLoadingScreen("Compiling Shaders...");
g_host_interface->DisplayLoadingScreen("Compiling Shaders...");
VkDevice device = g_vulkan_context->GetDevice();
VkPipelineCache pipeline_cache = g_vulkan_shader_cache->GetPipelineCache();
@ -941,7 +940,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
{
GPU_HW::UpdateDisplay();
if (m_system->GetSettings().debugging.show_vram)
if (g_settings.debugging.show_vram)
{
m_host_display->SetDisplayTexture(&m_vram_texture, m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 0, 0,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight());

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,119 +1,24 @@
#pragma once
#include "common/state_wrapper.h"
#include "gte_types.h"
namespace CPU {
class Core;
namespace Recompiler {
class CodeGenerator;
}
} // namespace CPU
class StateWrapper;
namespace GTE {
class Core
{
public:
friend CPU::Core;
friend CPU::Recompiler::CodeGenerator;
void Initialize();
void Reset();
bool DoState(StateWrapper& sw);
Core();
~Core();
// control registers are offset by +32
u32 ReadRegister(u32 index);
void WriteRegister(u32 index, u32 value);
ALWAYS_INLINE void SetWidescreenHack(bool enabled) { m_widescreen_hack = enabled; }
// use with care, direct register access
u32* GetRegisterPtr(u32 index);
void Initialize();
void Reset();
bool DoState(StateWrapper& sw);
void ExecuteInstruction(u32 inst_bits);
// control registers are offset by +32
u32 ReadRegister(u32 index) const;
void WriteRegister(u32 index, u32 value);
using InstructionImpl = void (*)(Instruction);
InstructionImpl GetInstructionImpl(u32 inst_bits);
void ExecuteInstruction(Instruction inst);
private:
static constexpr s64 MAC0_MIN_VALUE = -(INT64_C(1) << 31);
static constexpr s64 MAC0_MAX_VALUE = (INT64_C(1) << 31) - 1;
static constexpr s64 MAC123_MIN_VALUE = -(INT64_C(1) << 43);
static constexpr s64 MAC123_MAX_VALUE = (INT64_C(1) << 43) - 1;
static constexpr s32 IR0_MIN_VALUE = 0x0000;
static constexpr s32 IR0_MAX_VALUE = 0x1000;
static constexpr s32 IR123_MIN_VALUE = -(INT64_C(1) << 15);
static constexpr s32 IR123_MAX_VALUE = (INT64_C(1) << 15) - 1;
// Checks for underflow/overflow.
template<u32 index>
void CheckMACOverflow(s64 value);
// Checks for underflow/overflow, sign-extending to 31/43 bits.
template<u32 index>
s64 SignExtendMACResult(s64 value);
template<u32 index>
void TruncateAndSetMAC(s64 value, u8 shift);
template<u32 index>
void TruncateAndSetMACAndIR(s64 value, u8 shift, bool lm);
template<u32 index>
void TruncateAndSetIR(s32 value, bool lm);
template<u32 index>
u32 TruncateRGB(s32 value);
void SetOTZ(s32 value);
void PushSXY(s32 x, s32 y);
void PushSZ(s32 value);
void PushRGBFromMAC();
// Divide using Unsigned Newton-Raphson algorithm.
u32 UNRDivide(u32 lhs, u32 rhs);
// 3x3 matrix * 3x1 vector, updates MAC[1-3] and IR[1-3]
void MulMatVec(const s16 M[3][3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
// 3x3 matrix * 3x1 vector with translation, updates MAC[1-3] and IR[1-3]
void MulMatVec(const s16 M[3][3], const s32 T[3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
void MulMatVecBuggy(const s16 M[3][3], const s32 T[3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
// Interpolate colour, or as in nocash "MAC+(FC-MAC)*IR0".
void InterpolateColor(s64 in_MAC1, s64 in_MAC2, s64 in_MAC3, u8 shift, bool lm);
void RTPS(const s16 V[3], u8 shift, bool lm, bool last);
void NCS(const s16 V[3], u8 shift, bool lm);
void NCCS(const s16 V[3], u8 shift, bool lm);
void NCDS(const s16 V[3], u8 shift, bool lm);
void DPCS(const u8 color[3], u8 shift, bool lm);
void Execute_MVMVA(Instruction inst);
void Execute_SQR(Instruction inst);
void Execute_OP(Instruction inst);
void Execute_RTPS(Instruction inst);
void Execute_RTPT(Instruction inst);
void Execute_NCLIP(Instruction inst);
void Execute_AVSZ3(Instruction inst);
void Execute_AVSZ4(Instruction inst);
void Execute_NCS(Instruction inst);
void Execute_NCT(Instruction inst);
void Execute_NCCS(Instruction inst);
void Execute_NCCT(Instruction inst);
void Execute_NCDS(Instruction inst);
void Execute_NCDT(Instruction inst);
void Execute_CC(Instruction inst);
void Execute_CDP(Instruction inst);
void Execute_DPCS(Instruction inst);
void Execute_DPCT(Instruction inst);
void Execute_DCPL(Instruction inst);
void Execute_INTPL(Instruction inst);
void Execute_GPL(Instruction inst);
void Execute_GPF(Instruction inst);
Regs m_regs = {};
bool m_widescreen_hack = false;
};
#include "gte.inl"
} // 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"
namespace GTE {
static constexpr u32 NUM_DATA_REGS = 32;
static constexpr u32 NUM_CONTROL_REGS = 32;
static constexpr u32 NUM_REGS = NUM_DATA_REGS + NUM_CONTROL_REGS;
enum : u32
{
NUM_DATA_REGS = 32,
NUM_CONTROL_REGS = 32,
NUM_REGS = NUM_DATA_REGS + NUM_CONTROL_REGS
};
union FLAGS
{
@ -138,4 +142,4 @@ union Instruction
static constexpr u32 REQUIRED_BITS_MASK = ((1 << 20) - 1);
};
} // namespace GTE
} // namespace GTE

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,26 +1,35 @@
#include "mdec.h"
#include "common/log.h"
#include "common/state_wrapper.h"
#include "cpu_core.h"
#include "dma.h"
#include "interrupt_controller.h"
#include "system.h"
#include <imgui.h>
Log_SetChannel(MDEC);
MDEC g_mdec;
MDEC::MDEC() = default;
MDEC::~MDEC() = default;
void MDEC::Initialize(System* system, DMA* dma)
void MDEC::Initialize()
{
m_system = system;
m_dma = dma;
m_block_copy_out_event = system->CreateTimingEvent("MDEC Block Copy Out", TICKS_PER_BLOCK, TICKS_PER_BLOCK,
std::bind(&MDEC::CopyOutBlock, this), false);
m_block_copy_out_event = TimingEvents::CreateTimingEvent("MDEC Block Copy Out", TICKS_PER_BLOCK, TICKS_PER_BLOCK,
std::bind(&MDEC::CopyOutBlock, this), false);
m_total_blocks_decoded = 0;
Reset();
}
void MDEC::Shutdown()
{
m_block_copy_out_event.reset();
}
void MDEC::Reset()
{
m_block_copy_out_event->Deactivate();
SoftReset();
}
@ -176,12 +185,12 @@ void MDEC::UpdateStatus()
// we always want data in if it's enabled
const bool data_in_request = m_enable_dma_in && m_data_in_fifo.GetSpace() >= (32 * 2);
m_status.data_in_request = data_in_request;
m_dma->SetRequest(DMA::Channel::MDECin, data_in_request);
g_dma.SetRequest(DMA::Channel::MDECin, data_in_request);
// we only want to send data out if we have some in the fifo
const bool data_out_request = m_enable_dma_out && !m_data_out_fifo.IsEmpty();
m_status.data_out_request = data_out_request;
m_dma->SetRequest(DMA::Channel::MDECout, data_out_request);
g_dma.SetRequest(DMA::Channel::MDECout, data_out_request);
}
u32 MDEC::ReadDataRegister()
@ -192,7 +201,7 @@ u32 MDEC::ReadDataRegister()
if (HasPendingBlockCopyOut())
{
Log_DevPrint("MDEC data out FIFO empty on read - stalling CPU");
m_system->StallCPU(m_block_copy_out_event->GetTicksUntilNextExecution());
CPU::AddPendingTicks(m_block_copy_out_event->GetTicksUntilNextExecution());
}
else
{
@ -695,7 +704,7 @@ void MDEC::DrawDebugStateWindow()
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
ImGui::SetNextWindowSize(ImVec2(300.0f * framebuffer_scale, 350.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("MDEC State", &m_system->GetSettings().debugging.show_mdec_state))
if (!ImGui::Begin("MDEC State", &g_settings.debugging.show_mdec_state))
{
ImGui::End();
return;

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@
#include <array>
Log_SetChannel(NamcoGunCon);
NamcoGunCon::NamcoGunCon(System* system) : m_system(system) {}
NamcoGunCon::NamcoGunCon() = default;
NamcoGunCon::~NamcoGunCon() = default;
@ -153,14 +153,13 @@ bool NamcoGunCon::Transfer(const u8 data_in, u8* data_out)
void NamcoGunCon::UpdatePosition()
{
// get screen coordinates
const HostDisplay* display = m_system->GetHostInterface()->GetDisplay();
const HostDisplay* display = g_host_interface->GetDisplay();
const s32 mouse_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY();
// are we within the active display area?
u32 tick, line;
if (mouse_x < 0 || mouse_y < 0 ||
!m_system->GetGPU()->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, &tick, &line))
if (mouse_x < 0 || mouse_y < 0 || !g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, &tick, &line))
{
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
m_position_x = 0x01;
@ -169,16 +168,16 @@ void NamcoGunCon::UpdatePosition()
}
// 8MHz units for X = 44100*768*11/7 = 53222400 / 8000000 = 6.6528
const double divider = static_cast<double>(m_system->GetGPU()->GetCRTCFrequency()) / 8000000.0;
const double divider = static_cast<double>(g_gpu->GetCRTCFrequency()) / 8000000.0;
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line);
Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
m_position_x);
}
std::unique_ptr<NamcoGunCon> NamcoGunCon::Create(System* system)
std::unique_ptr<NamcoGunCon> NamcoGunCon::Create()
{
return std::make_unique<NamcoGunCon>(system);
return std::make_unique<NamcoGunCon>();
}
std::optional<s32> NamcoGunCon::StaticGetAxisCodeByName(std::string_view button_name)
@ -234,11 +233,11 @@ Controller::SettingList NamcoGunCon::StaticGetSettings()
return SettingList(settings.begin(), settings.end());
}
void NamcoGunCon::LoadSettings(HostInterface* host_interface, const char* section)
void NamcoGunCon::LoadSettings(const char* section)
{
Controller::LoadSettings(host_interface, section);
Controller::LoadSettings(section);
std::string path = host_interface->GetStringSettingValue(section, "CrosshairImagePath");
std::string path = g_host_interface->GetStringSettingValue(section, "CrosshairImagePath");
if (path != m_crosshair_image_path)
{
m_crosshair_image_path = std::move(path);
@ -255,7 +254,7 @@ void NamcoGunCon::LoadSettings(HostInterface* host_interface, const char* sectio
Resources::CROSSHAIR_IMAGE_DATA.data());
}
m_crosshair_image_scale = host_interface->GetFloatSettingValue(section, "CrosshairScale", 1.0f);
m_crosshair_image_scale = g_host_interface->GetFloatSettingValue(section, "CrosshairScale", 1.0f);
}
bool NamcoGunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale)

View File

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

View File

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

View File

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

View File

@ -9,10 +9,10 @@
#include <array>
Log_SetChannel(PlayStationMouse);
PlayStationMouse::PlayStationMouse(System* system) : m_system(system)
PlayStationMouse::PlayStationMouse()
{
m_last_host_position_x = system->GetHostInterface()->GetDisplay()->GetMousePositionX();
m_last_host_position_y = system->GetHostInterface()->GetDisplay()->GetMousePositionY();
m_last_host_position_x = g_host_interface->GetDisplay()->GetMousePositionX();
m_last_host_position_y = g_host_interface->GetDisplay()->GetMousePositionY();
}
PlayStationMouse::~PlayStationMouse() = default;
@ -142,7 +142,7 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out)
void PlayStationMouse::UpdatePosition()
{
// get screen coordinates
const HostDisplay* display = m_system->GetHostInterface()->GetDisplay();
const HostDisplay* display = g_host_interface->GetDisplay();
const s32 mouse_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY();
const s32 delta_x = mouse_x - m_last_host_position_x;
@ -157,9 +157,9 @@ void PlayStationMouse::UpdatePosition()
m_delta_y = static_cast<s8>(std::clamp<s32>(delta_y, std::numeric_limits<s8>::min(), std::numeric_limits<s8>::max()));
}
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create(System* system)
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create()
{
return std::make_unique<PlayStationMouse>(system);
return std::make_unique<PlayStationMouse>();
}
std::optional<s32> PlayStationMouse::StaticGetAxisCodeByName(std::string_view button_name)

View File

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

View File

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

View File

@ -146,6 +146,9 @@ struct Settings
bool log_to_window = false;
bool log_to_file = false;
ALWAYS_INLINE bool IsUsingRecompiler() const { return (cpu_execution_mode == CPUExecutionMode::Recompiler); }
ALWAYS_INLINE bool IsUsingSoftwareRenderer() const { return (gpu_renderer == GPURenderer::Software); }
bool HasAnyPerGameMemoryCards() const;
enum : u32
@ -216,3 +219,5 @@ struct Settings
static constexpr MemoryCardType DEFAULT_MEMORY_CARD_2_TYPE = MemoryCardType::None;
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
};
extern Settings g_settings;

View File

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

View File

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

View File

@ -11,21 +11,28 @@
#include <imgui.h>
Log_SetChannel(SPU);
SPU g_spu;
SPU::SPU() = default;
SPU::~SPU() = default;
void SPU::Initialize(System* system, DMA* dma, CDROM* cdrom, InterruptController* interrupt_controller)
void SPU::Initialize()
{
m_system = system;
m_dma = dma;
m_cdrom = cdrom;
m_interrupt_controller = interrupt_controller;
m_tick_event = m_system->CreateTimingEvent("SPU Sample", SYSCLK_TICKS_PER_SPU_TICK, SYSCLK_TICKS_PER_SPU_TICK,
std::bind(&SPU::Execute, this, std::placeholders::_1), false);
m_tick_event = TimingEvents::CreateTimingEvent("SPU Sample", SYSCLK_TICKS_PER_SPU_TICK, SYSCLK_TICKS_PER_SPU_TICK,
std::bind(&SPU::Execute, this, std::placeholders::_1), false);
m_transfer_event =
m_system->CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
std::bind(&SPU::ExecuteTransfer, this, std::placeholders::_1), false);
TimingEvents::CreateTimingEvent("SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
std::bind(&SPU::ExecuteTransfer, this, std::placeholders::_1), false);
Reset();
}
void SPU::Shutdown()
{
m_tick_event.reset();
m_transfer_event.reset();
m_dump_writer.reset();
}
void SPU::Reset()
@ -81,6 +88,7 @@ void SPU::Reset()
}
m_transfer_fifo.Clear();
m_transfer_event->Deactivate();
m_ram.fill(0);
UpdateEventInterval();
}
@ -147,7 +155,7 @@ bool SPU::DoState(StateWrapper& sw)
if (sw.IsReading())
{
m_system->GetHostInterface()->GetAudioStream()->EmptyBuffers();
g_host_interface->GetAudioStream()->EmptyBuffers();
UpdateEventInterval();
UpdateTransferEvent();
}
@ -649,7 +657,7 @@ void SPU::CheckRAMIRQ(u32 address)
{
Log_DebugPrintf("SPU IRQ at address 0x%08X", address);
m_SPUSTAT.irq9_flag = true;
m_interrupt_controller->InterruptRequest(InterruptController::IRQ::SPU);
g_interrupt_controller.InterruptRequest(InterruptController::IRQ::SPU);
}
}
@ -675,7 +683,7 @@ void SPU::Execute(TickCount ticks)
while (remaining_frames > 0)
{
AudioStream* const output_stream = m_system->GetHostInterface()->GetAudioStream();
AudioStream* const output_stream = g_host_interface->GetAudioStream();
s16* output_frame_start;
u32 output_frame_space = remaining_frames;
output_stream->BeginWrite(&output_frame_start, &output_frame_space);
@ -730,7 +738,7 @@ void SPU::Execute(TickCount ticks)
UpdateNoise();
// Mix in CD audio.
const auto [cd_audio_left, cd_audio_right] = m_cdrom->GetAudioFrame();
const auto [cd_audio_left, cd_audio_right] = g_cdrom.GetAudioFrame();
if (m_SPUCNT.cd_audio_enable)
{
const s32 cd_audio_volume_left = ApplyVolume(s32(cd_audio_left), m_cd_audio_volume_left);
@ -782,7 +790,7 @@ void SPU::UpdateEventInterval()
// Don't generate more than the audio buffer since in a single slice, otherwise we'll both overflow the buffers when
// we do write it, and the audio thread will underflow since it won't have enough data it the game isn't messing with
// the SPU state.
const u32 max_slice_frames = m_system->GetHostInterface()->GetAudioStream()->GetBufferSize();
const u32 max_slice_frames = g_host_interface->GetAudioStream()->GetBufferSize();
// TODO: Make this predict how long until the interrupt will be hit instead...
const u32 interval = (m_SPUCNT.enable && m_SPUCNT.irq9_enable) ? 1 : max_slice_frames;
@ -930,7 +938,7 @@ void SPU::UpdateDMARequest()
}
// This might call us back directly.
m_dma->SetRequest(DMA::Channel::SPU, m_SPUSTAT.dma_request);
g_dma.SetRequest(DMA::Channel::SPU, m_SPUSTAT.dma_request);
}
void SPU::DMARead(u32* words, u32 word_count)
@ -1744,7 +1752,7 @@ void SPU::DrawDebugStateWindow()
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 800.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("SPU State", &m_system->GetSettings().debugging.show_spu_state))
if (!ImGui::Begin("SPU State", &g_settings.debugging.show_spu_state))
{
ImGui::End();
return;

View File

@ -11,11 +11,7 @@ namespace Common {
class WAVWriter;
}
class System;
class TimingEvent;
class DMA;
class CDROM;
class InterruptController;
class SPU
{
@ -23,7 +19,8 @@ public:
SPU();
~SPU();
void Initialize(System* system, DMA* dma, CDROM* cdrom, InterruptController* interrupt_controller);
void Initialize();
void Shutdown();
void Reset();
bool DoState(StateWrapper& sw);
@ -369,10 +366,6 @@ private:
void UpdateTransferEvent();
void UpdateDMARequest();
System* m_system = nullptr;
DMA* m_dma = nullptr;
CDROM* m_cdrom = nullptr;
InterruptController* m_interrupt_controller = nullptr;
std::unique_ptr<TimingEvent> m_tick_event;
std::unique_ptr<TimingEvent> m_transfer_event;
std::unique_ptr<Common::WAVWriter> m_dump_writer;
@ -421,4 +414,6 @@ private:
InlineFIFOQueue<u16, FIFO_SIZE_IN_HALFWORDS> m_transfer_fifo;
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 StateWrapper;
namespace CPU {
class Core;
class CodeCache;
} // namespace CPU
class Bus;
class DMA;
class InterruptController;
class GPU;
class CDROM;
class Pad;
class Controller;
class Timers;
class SPU;
class MDEC;
class SIO;
struct SystemBootParameters
{
@ -44,227 +29,105 @@ struct SystemBootParameters
bool force_software_renderer = false;
};
class System
namespace System {
enum : u32
{
public:
enum : u32
{
// 5 megabytes is sufficient for now, at the moment they're around 4.2MB.
MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024
};
friend TimingEvent;
~System();
/// Returns the preferred console type for a disc.
static ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
/// Creates a new System.
static std::unique_ptr<System> Create(HostInterface* host_interface);
// Accessing components.
HostInterface* GetHostInterface() const { return m_host_interface; }
CPU::Core* GetCPU() const { return m_cpu.get(); }
Bus* GetBus() const { return m_bus.get(); }
DMA* GetDMA() const { return m_dma.get(); }
InterruptController* GetInterruptController() const { return m_interrupt_controller.get(); }
GPU* GetGPU() const { return m_gpu.get(); }
CDROM* GetCDROM() const { return m_cdrom.get(); }
Pad* GetPad() const { return m_pad.get(); }
Timers* GetTimers() const { return m_timers.get(); }
SPU* GetSPU() const { return m_spu.get(); }
MDEC* GetMDEC() const { return m_mdec.get(); }
ConsoleRegion GetRegion() const { return m_region; }
bool IsPALRegion() const { return m_region == ConsoleRegion::PAL; }
u32 GetFrameNumber() const { return m_frame_number; }
u32 GetInternalFrameNumber() const { return m_internal_frame_number; }
u32 GetGlobalTickCounter() const { return m_global_tick_counter; }
void IncrementFrameNumber()
{
m_frame_number++;
m_frame_done = true;
}
void IncrementInternalFrameNumber() { m_internal_frame_number++; }
const Settings& GetSettings() { return m_host_interface->GetSettings(); }
const std::string& GetRunningPath() const { return m_running_game_path; }
const std::string& GetRunningCode() const { return m_running_game_code; }
const std::string& GetRunningTitle() const { return m_running_game_title; }
float GetFPS() const { return m_fps; }
float GetVPS() const { return m_vps; }
float GetEmulationSpeed() const { return m_speed; }
float GetAverageFrameTime() const { return m_average_frame_time; }
float GetWorstFrameTime() const { return m_worst_frame_time; }
float GetThrottleFrequency() const { return m_throttle_frequency; }
bool Boot(const SystemBootParameters& params);
void Reset();
bool LoadState(ByteStream* state);
bool SaveState(ByteStream* state, u32 screenshot_size = 128);
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
bool RecreateGPU(GPURenderer renderer);
/// Updates GPU settings, without recreating the renderer.
void UpdateGPUSettings();
/// Forcibly changes the CPU execution mode, ignoring settings.
void SetCPUExecutionMode(CPUExecutionMode mode);
void RunFrame();
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
void SetThrottleFrequency(float frequency);
/// Updates the throttle period, call when target emulation speed changes.
void UpdateThrottlePeriod();
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
void Throttle();
void UpdatePerformanceCounters();
void ResetPerformanceCounters();
bool LoadEXE(const char* filename, std::vector<u8>& bios_image);
bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size, std::vector<u8>& bios_image);
bool LoadPSF(const char* filename, std::vector<u8>& bios_image);
bool SetExpansionROM(const char* filename);
// Adds ticks to the global tick counter, simulating the CPU being stalled.
void StallCPU(TickCount ticks);
// Access controllers for simulating input.
Controller* GetController(u32 slot) const;
void UpdateControllers();
void UpdateControllerSettings();
void ResetControllers();
void UpdateMemoryCards();
bool HasMedia() const;
bool InsertMedia(const char* path);
void RemoveMedia();
/// Creates a new event.
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
TimingEventCallback callback, bool activate);
/// Returns the number of entries in the media/disc playlist.
ALWAYS_INLINE u32 GetMediaPlaylistCount() const { return static_cast<u32>(m_media_playlist.size()); }
/// Returns the current image from the media/disc playlist.
u32 GetMediaPlaylistIndex() const;
/// Returns the path to the specified playlist index.
const std::string& GetMediaPlaylistPath(u32 index) const { return m_media_playlist[index]; }
/// Adds a new path to the media playlist.
bool AddMediaPathToPlaylist(const std::string_view& path);
/// Removes a path from the media playlist.
bool RemoveMediaPathFromPlaylist(const std::string_view& path);
bool RemoveMediaPathFromPlaylist(u32 index);
/// Changes a path from the media playlist.
bool ReplaceMediaPathFromPlaylist(u32 index, const std::string_view& path);
/// Switches to the specified media/disc playlist index.
bool SwitchMediaFromPlaylist(u32 index);
private:
System(HostInterface* host_interface);
/// Opens CD image, preloading if needed.
std::unique_ptr<CDImage> OpenCDImage(const char* path, bool force_preload);
bool DoLoadState(ByteStream* stream, bool init_components, bool force_software_renderer);
bool DoState(StateWrapper& sw);
bool CreateGPU(GPURenderer renderer);
bool InitializeComponents(bool force_software_renderer);
void DestroyComponents();
// Active event management
void AddActiveEvent(TimingEvent* event);
void RemoveActiveEvent(TimingEvent* event);
void SortEvents();
// Runs any pending events. Call when CPU downcount is zero.
void RunEvents();
// Updates the downcount of the CPU (event scheduling).
void UpdateCPUDowncount();
bool DoEventsState(StateWrapper& sw);
// Event lookup, use with care.
// If you modify an event, call SortEvents afterwards.
TimingEvent* FindActiveEvent(const char* name);
// Event enumeration, use with care.
// Don't remove an event while enumerating the list, as it will invalidate the iterator.
template<typename T>
void EnumerateActiveEvents(T callback) const
{
for (const TimingEvent* ev : m_events)
callback(ev);
}
void UpdateRunningGame(const char* path, CDImage* image);
HostInterface* m_host_interface;
std::unique_ptr<CPU::Core> m_cpu;
std::unique_ptr<CPU::CodeCache> m_cpu_code_cache;
std::unique_ptr<Bus> m_bus;
std::unique_ptr<DMA> m_dma;
std::unique_ptr<InterruptController> m_interrupt_controller;
std::unique_ptr<GPU> m_gpu;
std::unique_ptr<CDROM> m_cdrom;
std::unique_ptr<Pad> m_pad;
std::unique_ptr<Timers> m_timers;
std::unique_ptr<SPU> m_spu;
std::unique_ptr<MDEC> m_mdec;
std::unique_ptr<SIO> m_sio;
ConsoleRegion m_region = ConsoleRegion::NTSC_U;
CPUExecutionMode m_cpu_execution_mode = CPUExecutionMode::Interpreter;
u32 m_frame_number = 1;
u32 m_internal_frame_number = 1;
u32 m_global_tick_counter = 0;
std::vector<TimingEvent*> m_events;
u32 m_last_event_run_time = 0;
bool m_running_events = false;
bool m_events_need_sorting = false;
bool m_frame_done = false;
std::string m_running_game_path;
std::string m_running_game_code;
std::string m_running_game_title;
float m_throttle_frequency = 60.0f;
s32 m_throttle_period = 0;
u64 m_last_throttle_time = 0;
Common::Timer m_throttle_timer;
Common::Timer m_speed_lost_time_timestamp;
float m_average_frame_time_accumulator = 0.0f;
float m_worst_frame_time_accumulator = 0.0f;
float m_vps = 0.0f;
float m_fps = 0.0f;
float m_speed = 0.0f;
float m_worst_frame_time = 0.0f;
float m_average_frame_time = 0.0f;
u32 m_last_frame_number = 0;
u32 m_last_internal_frame_number = 0;
u32 m_last_global_tick_counter = 0;
Common::Timer m_fps_timer;
Common::Timer m_frame_timer;
// Playlist of disc images.
std::vector<std::string> m_media_playlist;
// 5 megabytes is sufficient for now, at the moment they're around 4.2MB.
MAX_SAVE_STATE_SIZE = 5 * 1024 * 1024
};
enum class State
{
Shutdown,
Starting,
Running,
Paused
};
/// Returns the preferred console type for a disc.
ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
State GetState();
void SetState(State new_state);
bool IsRunning();
bool IsPaused();
bool IsShutdown();
bool IsValid();
ConsoleRegion GetRegion();
bool IsPALRegion();
u32 GetFrameNumber();
u32 GetInternalFrameNumber();
void FrameDone();
void IncrementInternalFrameNumber();
const std::string& GetRunningPath();
const std::string& GetRunningCode();
const std::string& GetRunningTitle();
float GetFPS();
float GetVPS();
float GetEmulationSpeed();
float GetAverageFrameTime();
float GetWorstFrameTime();
float GetThrottleFrequency();
bool Boot(const SystemBootParameters& params);
void Reset();
void Shutdown();
bool LoadState(ByteStream* state);
bool SaveState(ByteStream* state, u32 screenshot_size = 128);
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
bool RecreateGPU(GPURenderer renderer);
void RunFrame();
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
void SetThrottleFrequency(float frequency);
/// Updates the throttle period, call when target emulation speed changes.
void UpdateThrottlePeriod();
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
void Throttle();
void UpdatePerformanceCounters();
void ResetPerformanceCounters();
// Access controllers for simulating input.
Controller* GetController(u32 slot);
void UpdateControllers();
void UpdateControllerSettings();
void ResetControllers();
void UpdateMemoryCards();
bool HasMedia();
bool InsertMedia(const char* path);
void RemoveMedia();
/// Returns the number of entries in the media/disc playlist.
u32 GetMediaPlaylistCount();
/// Returns the current image from the media/disc playlist.
u32 GetMediaPlaylistIndex();
/// Returns the path to the specified playlist index.
const std::string& GetMediaPlaylistPath(u32 index);
/// Adds a new path to the media playlist.
bool AddMediaPathToPlaylist(const std::string_view& path);
/// Removes a path from the media playlist.
bool RemoveMediaPathFromPlaylist(const std::string_view& path);
bool RemoveMediaPathFromPlaylist(u32 index);
/// Changes a path from the media playlist.
bool ReplaceMediaPathFromPlaylist(u32 index, const std::string_view& path);
/// Switches to the specified media/disc playlist index.
bool SwitchMediaFromPlaylist(u32 index);
} // namespace System

View File

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

View File

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

View File

@ -1,34 +1,268 @@
#include "timing_event.h"
#include "common/assert.h"
#include "common/log.h"
#include "common/state_wrapper.h"
#include "cpu_core.h"
#include "system.h"
Log_SetChannel(TimingEvents);
TimingEvent::TimingEvent(System* system, std::string name, TickCount period, TickCount interval,
TimingEventCallback callback)
namespace TimingEvents {
static std::vector<TimingEvent*> s_events;
static u32 s_global_tick_counter = 0;
static u32 s_last_event_run_time = 0;
static bool s_running_events = false;
static bool s_events_need_sorting = false;
u32 GetGlobalTickCounter()
{
return s_global_tick_counter;
}
void Initialize()
{
Reset();
}
void Reset()
{
s_global_tick_counter = 0;
s_last_event_run_time = 0;
}
void Shutdown()
{
Assert(s_events.empty());
}
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
TimingEventCallback callback, bool activate)
{
std::unique_ptr<TimingEvent> event =
std::make_unique<TimingEvent>(std::move(name), period, interval, std::move(callback));
if (activate)
event->Activate();
return event;
}
void UpdateCPUDowncount()
{
if (!CPU::g_state.frame_done)
CPU::g_state.downcount = s_events[0]->GetDowncount();
}
static bool CompareEvents(const TimingEvent* lhs, const TimingEvent* rhs)
{
return lhs->GetDowncount() > rhs->GetDowncount();
}
static void AddActiveEvent(TimingEvent* event)
{
s_events.push_back(event);
if (!s_running_events)
{
std::push_heap(s_events.begin(), s_events.end(), CompareEvents);
UpdateCPUDowncount();
}
else
{
s_events_need_sorting = true;
}
}
static void RemoveActiveEvent(TimingEvent* event)
{
auto iter = std::find_if(s_events.begin(), s_events.end(), [event](const auto& it) { return event == it; });
if (iter == s_events.end())
{
Panic("Attempt to remove inactive event");
return;
}
s_events.erase(iter);
if (!s_running_events)
{
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
if (!s_events.empty())
UpdateCPUDowncount();
}
else
{
s_events_need_sorting = true;
}
}
static TimingEvent* FindActiveEvent(const char* name)
{
auto iter =
std::find_if(s_events.begin(), s_events.end(), [&name](auto& ev) { return ev->GetName().compare(name) == 0; });
return (iter != s_events.end()) ? *iter : nullptr;
}
static void SortEvents()
{
if (!s_running_events)
{
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
UpdateCPUDowncount();
}
else
{
s_events_need_sorting = true;
}
}
void RunEvents()
{
DebugAssert(!s_running_events && !s_events.empty());
s_running_events = true;
TickCount pending_ticks = (s_global_tick_counter + CPU::GetPendingTicks()) - s_last_event_run_time;
CPU::ResetPendingTicks();
while (pending_ticks > 0)
{
const TickCount time = std::min(pending_ticks, s_events[0]->GetDowncount());
s_global_tick_counter += static_cast<u32>(time);
pending_ticks -= time;
// Apply downcount to all events.
// This will result in a negative downcount for those events which are late.
for (TimingEvent* evt : s_events)
{
evt->m_downcount -= time;
evt->m_time_since_last_run += time;
}
// Now we can actually run the callbacks.
while (s_events.front()->m_downcount <= 0)
{
TimingEvent* evt = s_events.front();
std::pop_heap(s_events.begin(), s_events.end(), CompareEvents);
// Factor late time into the time for the next invocation.
const TickCount ticks_late = -evt->m_downcount;
const TickCount ticks_to_execute = evt->m_time_since_last_run;
evt->m_downcount += evt->m_interval;
evt->m_time_since_last_run = 0;
// The cycles_late is only an indicator, it doesn't modify the cycles to execute.
evt->m_callback(ticks_to_execute, ticks_late);
// Place it in the appropriate position in the queue.
if (s_events_need_sorting)
{
// Another event may have been changed by this event, or the interval/downcount changed.
std::make_heap(s_events.begin(), s_events.end(), CompareEvents);
s_events_need_sorting = false;
}
else
{
// Keep the event list in a heap. The event we just serviced will be in the last place,
// so we can use push_here instead of make_heap, which should be faster.
std::push_heap(s_events.begin(), s_events.end(), CompareEvents);
}
}
}
s_last_event_run_time = s_global_tick_counter;
s_running_events = false;
UpdateCPUDowncount();
}
bool DoState(StateWrapper& sw, u32 global_tick_counter)
{
if (sw.IsReading())
{
s_global_tick_counter = global_tick_counter;
// Load timestamps for the clock events.
// Any oneshot events should be recreated by the load state method, so we can fix up their times here.
u32 event_count = 0;
sw.Do(&event_count);
for (u32 i = 0; i < event_count; i++)
{
std::string event_name;
TickCount downcount, time_since_last_run, period, interval;
sw.Do(&event_name);
sw.Do(&downcount);
sw.Do(&time_since_last_run);
sw.Do(&period);
sw.Do(&interval);
if (sw.HasError())
return false;
TimingEvent* event = FindActiveEvent(event_name.c_str());
if (!event)
{
Log_WarningPrintf("Save state has event '%s', but couldn't find this event when loading.", event_name.c_str());
continue;
}
// Using reschedule is safe here since we call sort afterwards.
event->m_downcount = downcount;
event->m_time_since_last_run = time_since_last_run;
event->m_period = period;
event->m_interval = interval;
}
sw.Do(&s_last_event_run_time);
Log_DevPrintf("Loaded %u events from save state.", event_count);
SortEvents();
}
else
{
u32 event_count = static_cast<u32>(s_events.size());
sw.Do(&event_count);
for (TimingEvent* evt : s_events)
{
sw.Do(&evt->m_name);
sw.Do(&evt->m_downcount);
sw.Do(&evt->m_time_since_last_run);
sw.Do(&evt->m_period);
sw.Do(&evt->m_interval);
}
sw.Do(&s_last_event_run_time);
Log_DevPrintf("Wrote %u events to save state.", event_count);
}
return !sw.HasError();
}
} // namespace TimingEvents
TimingEvent::TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback)
: m_downcount(interval), m_time_since_last_run(0), m_period(period), m_interval(interval),
m_callback(std::move(callback)), m_system(system), m_name(std::move(name)), m_active(false)
m_callback(std::move(callback)), m_name(std::move(name)), m_active(false)
{
}
TimingEvent::~TimingEvent()
{
if (m_active)
m_system->RemoveActiveEvent(this);
TimingEvents::RemoveActiveEvent(this);
}
TickCount TimingEvent::GetTicksSinceLastExecution() const
{
return m_system->m_cpu->GetPendingTicks() + m_time_since_last_run;
return CPU::GetPendingTicks() + m_time_since_last_run;
}
TickCount TimingEvent::GetTicksUntilNextExecution() const
{
return std::max(m_downcount - m_system->m_cpu->GetPendingTicks(), static_cast<TickCount>(0));
return std::max(m_downcount - CPU::GetPendingTicks(), static_cast<TickCount>(0));
}
void TimingEvent::Schedule(TickCount ticks)
{
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount = pending_ticks + ticks;
if (!m_active)
@ -36,13 +270,13 @@ void TimingEvent::Schedule(TickCount ticks)
// Event is going active, so we want it to only execute ticks from the current timestamp.
m_time_since_last_run = -pending_ticks;
m_active = true;
m_system->AddActiveEvent(this);
TimingEvents::AddActiveEvent(this);
}
else
{
// Event is already active, so we leave the time since last run alone, and just modify the downcount.
// If this is a call from an IO handler for example, re-sort the event queue.
m_system->SortEvents();
TimingEvents::SortEvents();
}
}
@ -66,7 +300,7 @@ void TimingEvent::Reset()
m_downcount = m_interval;
m_time_since_last_run = 0;
m_system->SortEvents();
TimingEvents::SortEvents();
}
void TimingEvent::InvokeEarly(bool force /* = false */)
@ -74,7 +308,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
if (!m_active)
return;
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
const TickCount pending_ticks = CPU::GetPendingTicks();
const TickCount ticks_to_execute = m_time_since_last_run + pending_ticks;
if (!force && ticks_to_execute < m_period)
return;
@ -84,7 +318,7 @@ void TimingEvent::InvokeEarly(bool force /* = false */)
m_callback(ticks_to_execute, 0);
// Since we've changed the downcount, we need to re-sort the events.
m_system->SortEvents();
TimingEvents::SortEvents();
}
void TimingEvent::Activate()
@ -93,12 +327,12 @@ void TimingEvent::Activate()
return;
// leave the downcount intact
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount += pending_ticks;
m_time_since_last_run -= pending_ticks;
m_active = true;
m_system->AddActiveEvent(this);
TimingEvents::AddActiveEvent(this);
}
void TimingEvent::Deactivate()
@ -106,10 +340,10 @@ void TimingEvent::Deactivate()
if (!m_active)
return;
const TickCount pending_ticks = m_system->m_cpu->GetPendingTicks();
const TickCount pending_ticks = CPU::GetPendingTicks();
m_downcount -= pending_ticks;
m_time_since_last_run += pending_ticks;
m_active = false;
m_system->RemoveActiveEvent(this);
TimingEvents::RemoveActiveEvent(this);
}

View File

@ -6,29 +6,24 @@
#include "types.h"
class System;
class TimingEvent;
class StateWrapper;
// Event callback type. Second parameter is the number of cycles the event was executed "late".
using TimingEventCallback = std::function<void(TickCount ticks, TickCount ticks_late)>;
class TimingEvent
{
friend System;
public:
TimingEvent(System* system, std::string name, TickCount period, TickCount interval, TimingEventCallback callback);
TimingEvent(std::string name, TickCount period, TickCount interval, TimingEventCallback callback);
~TimingEvent();
System* GetSystem() const { return m_system; }
const std::string& GetName() const { return m_name; }
bool IsActive() const { return m_active; }
// Returns the number of ticks between each event.
TickCount GetPeriod() const { return m_period; }
TickCount GetInterval() const { return m_interval; }
TickCount GetDowncount() const { return m_downcount; }
ALWAYS_INLINE TickCount GetPeriod() const { return m_period; }
ALWAYS_INLINE TickCount GetInterval() const { return m_interval; }
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
// Includes pending time.
TickCount GetTicksSinceLastExecution() const;
@ -61,14 +56,35 @@ public:
void SetInterval(TickCount interval) { m_interval = interval; }
void SetPeriod(TickCount period) { m_period = period; }
private:
TickCount m_downcount;
TickCount m_time_since_last_run;
TickCount m_period;
TickCount m_interval;
TimingEventCallback m_callback;
System* m_system;
std::string m_name;
bool m_active;
};
namespace TimingEvents {
u32 GetGlobalTickCounter();
void Initialize();
void Reset();
void Shutdown();
/// Creates a new event.
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
TimingEventCallback callback, bool activate);
/// Serialization.
bool DoState(StateWrapper& sw, u32 global_tick_counter);
void RunEvents();
void UpdateCPUDowncount();
} // namespace TimingEventManager

View File

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

View File

@ -16,7 +16,7 @@ public:
static bool HasCoreVariablesChanged();
static void InitDiskControlInterface();
ALWAYS_INLINE u32 GetResolutionScale() const { return m_settings.gpu_resolution_scale; }
ALWAYS_INLINE u32 GetResolutionScale() const { return g_settings.gpu_resolution_scale; }
bool Initialize() override;
void Shutdown() override;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,17 +21,6 @@ void ControllerInterface::Shutdown()
m_host_interface = nullptr;
}
System* ControllerInterface::GetSystem() const
{
return m_host_interface->GetSystem();
}
Controller* ControllerInterface::GetController(u32 slot) const
{
System* system = GetSystem();
return system ? system->GetController(slot) : nullptr;
}
void ControllerInterface::SetHook(Hook::Callback callback)
{
std::unique_lock<std::mutex> lock(m_event_intercept_mutex);

View File

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

View File

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