Play-/Source/PS2VM.cpp

933 lines
25 KiB
C++
Raw Normal View History

2021-08-04 11:48:57 -04:00
#include <cstdio>
#include <exception>
#include <memory>
2021-08-04 11:48:57 -04:00
#include <climits>
#include <fenv.h>
2020-03-06 13:07:01 -05:00
#include "FpUtils.h"
#include "make_unique.h"
#include "string_format.h"
#include "PS2VM.h"
#include "PS2VM_Preferences.h"
#include "ee/PS2OS.h"
#include "ee/EeExecutor.h"
#include "Ps2Const.h"
#include "iop/Iop_SifManPs2.h"
#include "StdStream.h"
#include "StdStreamUtils.h"
#include "states/MemoryStateFile.h"
#include "zip/ZipArchiveWriter.h"
#include "zip/ZipArchiveReader.h"
#include "xml/Node.h"
#include "xml/Writer.h"
#include "xml/Parser.h"
#include "AppConfig.h"
#include "PathUtils.h"
2022-11-02 11:32:24 -04:00
#include "ThreadUtils.h"
#include "iop/IopBios.h"
2021-01-05 17:25:38 -05:00
#include "iop/ioman/HardDiskDevice.h"
#include "iop/ioman/OpticalMediaDevice.h"
#include "iop/ioman/PreferenceDirectoryDevice.h"
#include "Log.h"
#include "DiskUtils.h"
#ifdef __ANDROID__
#include "android/JavaVM.h"
#endif
2018-04-30 21:01:23 +01:00
#define LOG_NAME ("ps2vm")
#define THREAD_NAME ("PS2VM Thread")
#define STATE_VM_TIMING_XML ("vm_timing.xml")
#define STATE_VM_TIMING_VBLANK_TICKS ("vblankTicks")
#define STATE_VM_TIMING_IN_VBLANK ("inVblank")
#define STATE_VM_TIMING_EE_EXECUTION_TICKS ("eeExecutionTicks")
#define STATE_VM_TIMING_IOP_EXECUTION_TICKS ("iopExecutionTicks")
2020-04-26 02:26:49 +01:00
#define PREF_PS2_ROM0_DIRECTORY_DEFAULT ("vfs/rom0")
2018-04-30 21:01:23 +01:00
#define PREF_PS2_HOST_DIRECTORY_DEFAULT ("vfs/host")
#define PREF_PS2_MC0_DIRECTORY_DEFAULT ("vfs/mc0")
#define PREF_PS2_MC1_DIRECTORY_DEFAULT ("vfs/mc1")
2021-01-05 12:35:28 -05:00
#define PREF_PS2_HDD_DIRECTORY_DEFAULT ("vfs/hdd")
2022-09-29 16:02:50 -04:00
#define PREF_PS2_ARCADEROMS_DIRECTORY_DEFAULT ("arcaderoms")
CPS2VM::CPS2VM()
2023-05-02 08:56:25 -04:00
: m_eeProfilerZone(CProfiler::GetInstance().RegisterZone("EE"))
2018-04-30 21:01:23 +01:00
, m_iopProfilerZone(CProfiler::GetInstance().RegisterZone("IOP"))
, m_spuProfilerZone(CProfiler::GetInstance().RegisterZone("SPU"))
, m_gsSyncProfilerZone(CProfiler::GetInstance().RegisterZone("GSSYNC"))
, m_otherProfilerZone(CProfiler::GetInstance().RegisterZone("OTHER"))
{
2020-09-09 15:51:42 -04:00
// clang-format off
static const std::pair<const char*, const char*> basicDirectorySettings[] =
2020-09-09 15:51:42 -04:00
{
std::make_pair(PREF_PS2_ROM0_DIRECTORY, PREF_PS2_ROM0_DIRECTORY_DEFAULT),
std::make_pair(PREF_PS2_HOST_DIRECTORY, PREF_PS2_HOST_DIRECTORY_DEFAULT),
std::make_pair(PREF_PS2_MC0_DIRECTORY, PREF_PS2_MC0_DIRECTORY_DEFAULT),
std::make_pair(PREF_PS2_MC1_DIRECTORY, PREF_PS2_MC1_DIRECTORY_DEFAULT),
2021-01-05 12:35:28 -05:00
std::make_pair(PREF_PS2_HDD_DIRECTORY, PREF_PS2_HDD_DIRECTORY_DEFAULT),
2022-09-29 16:02:50 -04:00
std::make_pair(PREF_PS2_ARCADEROMS_DIRECTORY, PREF_PS2_ARCADEROMS_DIRECTORY_DEFAULT),
2020-09-09 15:51:42 -04:00
};
// clang-format on
for(const auto& basicDirectorySetting : basicDirectorySettings)
{
auto setting = basicDirectorySetting.first;
auto path = basicDirectorySetting.second;
auto absolutePath = CAppConfig::GetBasePath() / path;
Framework::PathUtils::EnsurePathExists(absolutePath);
CAppConfig::GetInstance().RegisterPreferencePath(setting, absolutePath);
2019-01-22 23:33:41 -05:00
auto currentPath = CAppConfig::GetInstance().GetPreferencePath(setting);
2019-10-16 20:51:11 -04:00
if(!fs::exists(currentPath))
{
CAppConfig::GetInstance().SetPreferencePath(setting, absolutePath);
}
}
2018-04-30 21:01:23 +01:00
2018-01-15 14:14:59 -05:00
CAppConfig::GetInstance().RegisterPreferencePath(PREF_PS2_CDROM0_PATH, "");
2017-09-05 00:16:05 -04:00
Framework::PathUtils::EnsurePathExists(GetStateDirectoryPath());
2019-01-22 23:33:41 -05:00
2021-02-24 11:55:09 -05:00
CAppConfig::GetInstance().RegisterPreferenceBoolean(PREF_PS2_LIMIT_FRAMERATE, true);
ReloadFrameRateLimit();
2019-10-12 18:11:40 +01:00
CAppConfig::GetInstance().RegisterPreferenceInteger(PREF_AUDIO_SPUBLOCKCOUNT, 100);
m_spuBlockCount = CAppConfig::GetInstance().GetPreferenceInteger(PREF_AUDIO_SPUBLOCKCOUNT);
}
//////////////////////////////////////////////////
//Various Message Functions
//////////////////////////////////////////////////
void CPS2VM::CreateGSHandler(const CGSHandler::FactoryFunction& factoryFunction)
{
2018-04-30 21:01:23 +01:00
m_mailBox.SendCall([this, factoryFunction]() { CreateGsHandlerImpl(factoryFunction); }, true);
}
CGSHandler* CPS2VM::GetGSHandler()
{
return m_ee->m_gs;
}
void CPS2VM::DestroyGSHandler()
{
2015-04-06 15:52:09 -04:00
if(m_ee->m_gs == nullptr) return;
2018-04-30 21:01:23 +01:00
m_mailBox.SendCall([this]() { DestroyGsHandlerImpl(); }, true);
}
void CPS2VM::CreatePadHandler(const CPadHandler::FactoryFunction& factoryFunction)
{
2015-04-06 15:52:09 -04:00
if(m_pad != nullptr) return;
2018-04-30 21:01:23 +01:00
m_mailBox.SendCall([this, factoryFunction]() { CreatePadHandlerImpl(factoryFunction); }, true);
}
CPadHandler* CPS2VM::GetPadHandler()
{
return m_pad;
}
bool CPS2VM::HasGunListener() const
{
return m_gunListener != nullptr;
}
2023-03-19 22:24:29 -04:00
void CPS2VM::SetGunListener(CGunListener* listener)
{
m_gunListener = listener;
}
void CPS2VM::ReportGunPosition(float x, float y)
{
if(m_gunListener)
{
m_gunListener->SetGunPosition(x, y);
}
}
void CPS2VM::DestroyPadHandler()
{
2015-04-06 15:52:09 -04:00
if(m_pad == nullptr) return;
2018-04-30 21:01:23 +01:00
m_mailBox.SendCall([this]() { DestroyPadHandlerImpl(); }, true);
}
void CPS2VM::CreateSoundHandler(const CSoundHandler::FactoryFunction& factoryFunction)
{
if(m_soundHandler != nullptr) return;
2018-04-30 21:01:23 +01:00
m_mailBox.SendCall([this, factoryFunction]() { CreateSoundHandlerImpl(factoryFunction); }, true);
}
void CPS2VM::ReloadSpuBlockCount()
2019-10-10 15:42:06 +01:00
{
m_mailBox.SendCall(
[this]() {
2019-10-10 15:42:06 +01:00
m_currentSpuBlock = 0;
auto spuBlockCount = CAppConfig::GetInstance().GetPreferenceInteger(PREF_AUDIO_SPUBLOCKCOUNT);
assert(spuBlockCount <= BLOCK_COUNT);
2019-10-11 12:03:22 +01:00
m_spuBlockCount = spuBlockCount;
2019-10-10 15:42:06 +01:00
});
}
void CPS2VM::DestroySoundHandler()
{
if(m_soundHandler == nullptr) return;
2018-04-30 21:01:23 +01:00
m_mailBox.SendCall([this]() { DestroySoundHandlerImpl(); }, true);
}
2021-02-24 11:55:09 -05:00
void CPS2VM::ReloadFrameRateLimit()
{
2021-02-24 16:04:10 -05:00
uint32 frameRate = 60;
2022-02-09 20:47:48 -05:00
if(m_ee && m_ee->m_gs)
2021-02-24 16:04:10 -05:00
{
frameRate = m_ee->m_gs->GetCrtFrameRate();
}
2021-02-24 11:55:09 -05:00
bool limitFrameRate = CAppConfig::GetInstance().GetPreferenceBoolean(PREF_PS2_LIMIT_FRAMERATE);
2021-02-24 16:04:10 -05:00
m_frameLimiter.SetFrameRate(limitFrameRate ? frameRate : 0);
uint32 frameTicks = PS2::EE_CLOCK_FREQ / frameRate;
m_onScreenTicksTotal = frameTicks * 9 / 10;
m_vblankTicksTotal = frameTicks / 10;
2021-02-24 11:55:09 -05:00
}
CVirtualMachine::STATUS CPS2VM::GetStatus() const
{
return m_nStatus;
}
void CPS2VM::StepEe()
{
if(GetStatus() == RUNNING) return;
m_singleStepEe = true;
m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
}
void CPS2VM::StepIop()
{
if(GetStatus() == RUNNING) return;
m_singleStepIop = true;
m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
}
void CPS2VM::StepVu0()
{
if(GetStatus() == RUNNING) return;
m_singleStepVu0 = true;
m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
}
void CPS2VM::StepVu1()
{
if(GetStatus() == RUNNING) return;
m_singleStepVu1 = true;
m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
}
void CPS2VM::Resume()
{
if(m_nStatus == RUNNING) return;
m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
OnRunningStateChange();
}
void CPS2VM::Pause()
{
if(m_nStatus == PAUSED) return;
m_mailBox.SendCall(std::bind(&CPS2VM::PauseImpl, this), true);
OnMachineStateChange();
OnRunningStateChange();
}
2020-12-24 13:36:40 +00:00
void CPS2VM::PauseAsync()
{
if(m_nStatus == PAUSED) return;
2020-12-28 01:24:54 +00:00
m_mailBox.SendCall([this]() {
2020-12-24 13:36:40 +00:00
PauseImpl();
OnMachineStateChange();
OnRunningStateChange();
});
}
void CPS2VM::Reset()
{
assert(m_nStatus == PAUSED);
BeforeExecutableReloaded = ExecutableReloadedHandler();
AfterExecutableReloaded = ExecutableReloadedHandler();
ResetVM();
}
void CPS2VM::Initialize()
{
m_nEnd = false;
2018-04-30 21:01:23 +01:00
m_thread = std::thread([&]() { EmuThread(); });
Framework::ThreadUtils::SetThreadName(m_thread, THREAD_NAME);
}
void CPS2VM::Destroy()
{
m_mailBox.SendCall(std::bind(&CPS2VM::DestroyImpl, this));
m_thread.join();
DestroyVM();
}
2019-10-16 20:51:11 -04:00
fs::path CPS2VM::GetStateDirectoryPath()
{
2019-10-16 20:51:11 -04:00
return CAppConfig::GetBasePath() / fs::path("states/");
}
2019-10-16 20:51:11 -04:00
fs::path CPS2VM::GenerateStatePath(unsigned int slot) const
{
auto stateFileName = string_format("%s.st%d.zip", m_ee->m_os->GetExecutableName(), slot);
2019-10-16 20:51:11 -04:00
return GetStateDirectoryPath() / fs::path(stateFileName);
}
2019-10-16 20:51:11 -04:00
std::future<bool> CPS2VM::SaveState(const fs::path& statePath)
{
auto promise = std::make_shared<std::promise<bool>>();
auto future = promise->get_future();
m_mailBox.SendCall(
2018-04-30 21:01:23 +01:00
[this, promise, statePath]() {
auto result = SaveVMState(statePath);
promise->set_value(result);
});
return future;
}
2019-10-16 20:51:11 -04:00
std::future<bool> CPS2VM::LoadState(const fs::path& statePath)
{
auto promise = std::make_shared<std::promise<bool>>();
auto future = promise->get_future();
m_mailBox.SendCall(
2018-04-30 21:01:23 +01:00
[this, promise, statePath]() {
auto result = LoadVMState(statePath);
promise->set_value(result);
});
return future;
}
void CPS2VM::TriggerFrameDump(const FrameDumpCallback& frameDumpCallback)
{
2022-07-27 17:04:32 -04:00
#ifdef DEBUGGER_INCLUDED
m_mailBox.SendCall(
2018-04-30 21:01:23 +01:00
[=]() {
std::unique_lock<std::mutex> frameDumpCallbackMutexLock(m_frameDumpCallbackMutex);
if(m_frameDumpCallback) return;
m_frameDumpCallback = frameDumpCallback;
},
false);
2022-07-27 17:04:32 -04:00
#endif
}
CPS2VM::CPU_UTILISATION_INFO CPS2VM::GetCpuUtilisationInfo() const
{
return m_cpuUtilisation;
}
#ifdef DEBUGGER_INCLUDED
2018-04-30 21:01:23 +01:00
#define TAGS_SECTION_TAGS ("tags")
#define TAGS_SECTION_EE_FUNCTIONS ("ee_functions")
#define TAGS_SECTION_EE_COMMENTS ("ee_comments")
#define TAGS_SECTION_VU1_FUNCTIONS ("vu1_functions")
#define TAGS_SECTION_VU1_COMMENTS ("vu1_comments")
#define TAGS_SECTION_IOP ("iop")
#define TAGS_SECTION_IOP_FUNCTIONS ("functions")
#define TAGS_SECTION_IOP_COMMENTS ("comments")
2018-04-30 21:01:23 +01:00
#define TAGS_PATH ("tags/")
fs::path CPS2VM::MakeDebugTagsPackagePath(const char* packageName)
{
2019-10-16 20:51:11 -04:00
auto tagsPath = CAppConfig::GetBasePath() / fs::path(TAGS_PATH);
Framework::PathUtils::EnsurePathExists(tagsPath);
auto tagsPackagePath = tagsPath / (std::string(packageName) + std::string(".tags.xml"));
return tagsPackagePath;
}
void CPS2VM::LoadDebugTags(const char* packageName)
{
try
{
auto packagePath = MakeDebugTagsPackagePath(packageName);
auto stream = Framework::CreateInputStdStream(packagePath.native());
std::unique_ptr<Framework::Xml::CNode> document(Framework::Xml::CParser::ParseDocument(stream));
Framework::Xml::CNode* tagsNode = document->Select(TAGS_SECTION_TAGS);
if(!tagsNode) return;
m_ee->m_EE.m_Functions.Unserialize(tagsNode, TAGS_SECTION_EE_FUNCTIONS);
m_ee->m_EE.m_Comments.Unserialize(tagsNode, TAGS_SECTION_EE_COMMENTS);
m_ee->m_VU1.m_Functions.Unserialize(tagsNode, TAGS_SECTION_VU1_FUNCTIONS);
m_ee->m_VU1.m_Comments.Unserialize(tagsNode, TAGS_SECTION_VU1_COMMENTS);
{
Framework::Xml::CNode* sectionNode = tagsNode->Select(TAGS_SECTION_IOP);
if(sectionNode)
{
m_iop->m_cpu.m_Functions.Unserialize(sectionNode, TAGS_SECTION_IOP_FUNCTIONS);
m_iop->m_cpu.m_Comments.Unserialize(sectionNode, TAGS_SECTION_IOP_COMMENTS);
m_iop->m_bios->LoadDebugTags(sectionNode);
}
}
}
catch(...)
{
}
}
void CPS2VM::SaveDebugTags(const char* packageName)
{
try
{
auto packagePath = MakeDebugTagsPackagePath(packageName);
auto stream = Framework::CreateOutputStdStream(packagePath.native());
std::unique_ptr<Framework::Xml::CNode> document(new Framework::Xml::CNode(TAGS_SECTION_TAGS, true));
m_ee->m_EE.m_Functions.Serialize(document.get(), TAGS_SECTION_EE_FUNCTIONS);
m_ee->m_EE.m_Comments.Serialize(document.get(), TAGS_SECTION_EE_COMMENTS);
m_ee->m_VU1.m_Functions.Serialize(document.get(), TAGS_SECTION_VU1_FUNCTIONS);
m_ee->m_VU1.m_Comments.Serialize(document.get(), TAGS_SECTION_VU1_COMMENTS);
{
Framework::Xml::CNode* iopNode = new Framework::Xml::CNode(TAGS_SECTION_IOP, true);
m_iop->m_cpu.m_Functions.Serialize(iopNode, TAGS_SECTION_IOP_FUNCTIONS);
m_iop->m_cpu.m_Comments.Serialize(iopNode, TAGS_SECTION_IOP_COMMENTS);
m_iop->m_bios->SaveDebugTags(iopNode);
document->InsertNode(iopNode);
}
Framework::Xml::CWriter::WriteDocument(stream, document.get());
}
catch(...)
{
}
}
#endif
//////////////////////////////////////////////////
//Non extern callable methods
//////////////////////////////////////////////////
void CPS2VM::CreateVM()
{
2021-11-27 10:19:57 -05:00
m_iop = std::make_unique<Iop::CSubSystem>(true);
auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
m_ee = std::make_unique<Ee::CSubSystem>(m_iop->m_ram, *iopOs);
m_OnRequestLoadExecutableConnection = m_ee->m_os->OnRequestLoadExecutable.Connect(std::bind(&CPS2VM::ReloadExecutable, this, std::placeholders::_1, std::placeholders::_2));
m_OnCrtModeChangeConnection = m_ee->m_os->OnCrtModeChange.Connect(std::bind(&CPS2VM::OnCrtModeChange, this));
ResetVM();
}
void CPS2VM::ResetVM()
{
m_ee->Reset();
m_iop->Reset();
if(m_ee->m_gs != NULL)
{
m_ee->m_gs->Reset();
}
{
auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
assert(iopOs);
iopOs->Reset(std::make_shared<Iop::CSifManPs2>(m_ee->m_sif, m_ee->m_ram, m_iop->m_ram));
iopOs->GetIoman()->RegisterDevice("rom0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_ROM0_DIRECTORY));
iopOs->GetIoman()->RegisterDevice("host", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_HOST_DIRECTORY));
iopOs->GetIoman()->RegisterDevice("host0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_HOST_DIRECTORY));
iopOs->GetIoman()->RegisterDevice("mc0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_MC0_DIRECTORY));
iopOs->GetIoman()->RegisterDevice("mc1", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_MC1_DIRECTORY));
2023-01-03 21:18:18 -05:00
iopOs->GetIoman()->RegisterDevice("cdrom", Iop::Ioman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
iopOs->GetIoman()->RegisterDevice("cdrom0", Iop::Ioman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
2021-01-02 22:28:44 -05:00
iopOs->GetIoman()->RegisterDevice("hdd0", std::make_shared<Iop::Ioman::CHardDiskDevice>());
iopOs->GetLoadcore()->SetLoadExecutableHandler(std::bind(&CPS2OS::LoadExecutable, m_ee->m_os, std::placeholders::_1, std::placeholders::_2));
}
CDROM0_SyncPath();
m_vblankTicks = m_onScreenTicksTotal;
m_inVblank = false;
m_eeExecutionTicks = 0;
m_iopExecutionTicks = 0;
2015-04-06 00:32:43 -04:00
m_spuUpdateTicks = SPU_UPDATE_TICKS;
m_currentSpuBlock = 0;
2014-10-20 22:34:44 -04:00
RegisterModulesInPadHandler();
2023-03-25 14:01:16 -04:00
m_gunListener = nullptr;
}
void CPS2VM::DestroyVM()
{
2017-09-05 00:16:05 -04:00
CDROM0_Reset();
}
2019-10-16 20:51:11 -04:00
bool CPS2VM::SaveVMState(const fs::path& statePath)
{
if(m_ee->m_gs == NULL)
{
printf("PS2VM: GS Handler was not instancied. Cannot save state.\r\n");
return false;
}
try
{
auto stateStream = Framework::CreateOutputStdStream(statePath.native());
Framework::CZipArchiveWriter archive;
m_ee->SaveState(archive);
m_iop->SaveState(archive);
m_ee->m_gs->SaveState(archive);
SaveVmTimingState(archive);
archive.Write(stateStream);
}
catch(...)
{
return false;
}
return true;
}
2019-10-16 20:51:11 -04:00
bool CPS2VM::LoadVMState(const fs::path& statePath)
{
if(m_ee->m_gs == NULL)
{
printf("PS2VM: GS Handler was not instancied. Cannot load state.\r\n");
return false;
}
try
{
auto stateStream = Framework::CreateInputStdStream(statePath.native());
Framework::CZipArchiveReader archive(stateStream);
2018-04-30 21:01:23 +01:00
try
{
m_ee->LoadState(archive);
m_iop->LoadState(archive);
m_ee->m_gs->LoadState(archive);
LoadVmTimingState(archive);
2022-01-14 11:49:18 -05:00
ReloadFrameRateLimit();
}
catch(...)
{
//Any error that occurs in the previous block is critical
PauseImpl();
throw;
}
}
catch(...)
{
return false;
}
OnMachineStateChange();
return true;
}
void CPS2VM::SaveVmTimingState(Framework::CZipArchiveWriter& archive)
{
CRegisterStateFile* registerFile = new CRegisterStateFile(STATE_VM_TIMING_XML);
registerFile->SetRegister32(STATE_VM_TIMING_VBLANK_TICKS, m_vblankTicks);
registerFile->SetRegister32(STATE_VM_TIMING_IN_VBLANK, m_inVblank);
registerFile->SetRegister32(STATE_VM_TIMING_EE_EXECUTION_TICKS, m_eeExecutionTicks);
registerFile->SetRegister32(STATE_VM_TIMING_IOP_EXECUTION_TICKS, m_iopExecutionTicks);
archive.InsertFile(registerFile);
}
void CPS2VM::LoadVmTimingState(Framework::CZipArchiveReader& archive)
{
CRegisterStateFile registerFile(*archive.BeginReadFile(STATE_VM_TIMING_XML));
m_vblankTicks = registerFile.GetRegister32(STATE_VM_TIMING_VBLANK_TICKS);
m_inVblank = registerFile.GetRegister32(STATE_VM_TIMING_IN_VBLANK) != 0;
m_eeExecutionTicks = registerFile.GetRegister32(STATE_VM_TIMING_EE_EXECUTION_TICKS);
m_iopExecutionTicks = registerFile.GetRegister32(STATE_VM_TIMING_IOP_EXECUTION_TICKS);
}
void CPS2VM::PauseImpl()
{
m_nStatus = PAUSED;
}
void CPS2VM::ResumeImpl()
{
#ifdef DEBUGGER_INCLUDED
m_ee->m_EE.m_executor->DisableBreakpointsOnce();
m_iop->m_cpu.m_executor->DisableBreakpointsOnce();
m_ee->m_VU1.m_executor->DisableBreakpointsOnce();
#endif
m_nStatus = RUNNING;
}
void CPS2VM::DestroyImpl()
{
DestroyGsHandlerImpl();
DestroyPadHandlerImpl();
DestroySoundHandlerImpl();
m_nEnd = true;
}
void CPS2VM::CreateGsHandlerImpl(const CGSHandler::FactoryFunction& factoryFunction)
{
2020-02-09 23:05:24 +00:00
auto gs = m_ee->m_gs;
m_ee->m_gs = factoryFunction();
m_ee->m_gs->SetIntc(&m_ee->m_intc);
m_ee->m_gs->Initialize();
2020-12-02 18:43:42 -05:00
m_ee->m_gs->SendGSCall([this]() {
static_cast<CEeExecutor*>(m_ee->m_EE.m_executor.get())->AttachExceptionHandlerToThread();
});
2020-02-09 23:05:24 +00:00
if(gs)
{
2020-02-10 12:20:28 +00:00
m_ee->m_gs->Copy(gs);
2020-02-09 23:05:24 +00:00
gs->Release();
delete gs;
}
2019-07-01 13:11:23 +01:00
m_OnNewFrameConnection = m_ee->m_gs->OnNewFrame.Connect(std::bind(&CPS2VM::OnGsNewFrame, this));
}
void CPS2VM::DestroyGsHandlerImpl()
{
if(m_ee->m_gs == nullptr) return;
m_ee->m_gs->Release();
delete m_ee->m_gs;
m_ee->m_gs = nullptr;
}
void CPS2VM::CreatePadHandlerImpl(const CPadHandler::FactoryFunction& factoryFunction)
{
m_pad = factoryFunction();
RegisterModulesInPadHandler();
}
void CPS2VM::DestroyPadHandlerImpl()
{
if(m_pad == nullptr) return;
delete m_pad;
m_pad = nullptr;
}
void CPS2VM::CreateSoundHandlerImpl(const CSoundHandler::FactoryFunction& factoryFunction)
{
m_soundHandler = factoryFunction();
}
CSoundHandler* CPS2VM::GetSoundHandler()
{
return m_soundHandler;
}
void CPS2VM::DestroySoundHandlerImpl()
{
if(m_soundHandler == nullptr) return;
delete m_soundHandler;
m_soundHandler = nullptr;
}
void CPS2VM::OnGsNewFrame()
{
#ifdef DEBUGGER_INCLUDED
std::unique_lock<std::mutex> dumpFrameCallbackMutexLock(m_frameDumpCallbackMutex);
if(m_dumpingFrame && !m_frameDump.GetPackets().empty())
{
m_ee->m_gs->EndFrameDump();
m_frameDumpCallback(m_frameDump);
m_dumpingFrame = false;
m_frameDumpCallback = FrameDumpCallback();
}
else if(m_frameDumpCallback)
{
m_frameDump.Reset();
m_ee->m_gs->BeginFrameDump(&m_frameDump);
m_dumpingFrame = true;
}
#endif
}
void CPS2VM::UpdateEe()
{
#ifdef PROFILE
CProfilerZone profilerZone(m_eeProfilerZone);
#endif
while(m_eeExecutionTicks > 0)
{
int executed = m_ee->ExecuteCpu(m_singleStepEe ? 1 : m_eeExecutionTicks);
if(m_ee->IsCpuIdle())
{
m_cpuUtilisation.eeIdleTicks += (m_eeExecutionTicks - executed);
executed = m_eeExecutionTicks;
}
m_cpuUtilisation.eeTotalTicks += executed;
m_ee->m_vpu0->Execute(m_singleStepVu0 ? 1 : executed);
m_ee->m_vpu1->Execute(m_singleStepVu1 ? 1 : executed);
m_eeExecutionTicks -= executed;
m_ee->CountTicks(executed);
m_vblankTicks -= executed;
#ifdef DEBUGGER_INCLUDED
if(m_singleStepEe || m_singleStepVu0 || m_singleStepVu1) break;
if(m_ee->m_EE.m_executor->MustBreak()) break;
#endif
}
}
void CPS2VM::UpdateIop()
{
#ifdef PROFILE
CProfilerZone profilerZone(m_iopProfilerZone);
#endif
while(m_iopExecutionTicks > 0)
{
int executed = m_iop->ExecuteCpu(m_singleStepIop ? 1 : m_iopExecutionTicks);
if(m_iop->IsCpuIdle())
{
m_cpuUtilisation.iopIdleTicks += (m_iopExecutionTicks - executed);
executed = m_iopExecutionTicks;
}
m_cpuUtilisation.iopTotalTicks += executed;
m_iopExecutionTicks -= executed;
2014-10-20 22:34:44 -04:00
m_spuUpdateTicks -= executed;
m_iop->CountTicks(executed);
#ifdef DEBUGGER_INCLUDED
if(m_singleStepIop) break;
if(m_iop->m_cpu.m_executor->MustBreak()) break;
#endif
}
}
void CPS2VM::UpdateSpu()
{
#ifdef PROFILE
CProfilerZone profilerZone(m_spuProfilerZone);
#endif
2015-04-06 00:32:43 -04:00
unsigned int blockOffset = (BLOCK_SIZE * m_currentSpuBlock);
2014-10-20 22:34:44 -04:00
int16* samplesSpu0 = m_samples + blockOffset;
2018-04-16 09:14:24 -04:00
m_iop->m_spuCore0.Render(samplesSpu0, BLOCK_SIZE, DST_SAMPLE_RATE);
2014-10-20 22:34:44 -04:00
if(m_iop->m_spuCore1.IsEnabled())
{
2014-10-20 22:34:44 -04:00
int16 samplesSpu1[BLOCK_SIZE];
2018-04-16 09:14:24 -04:00
m_iop->m_spuCore1.Render(samplesSpu1, BLOCK_SIZE, DST_SAMPLE_RATE);
2014-10-20 22:34:44 -04:00
for(unsigned int i = 0; i < BLOCK_SIZE; i++)
{
2014-10-20 22:34:44 -04:00
int32 resultSample = static_cast<int32>(samplesSpu0[i]) + static_cast<int32>(samplesSpu1[i]);
resultSample = std::max<int32>(resultSample, SHRT_MIN);
resultSample = std::min<int32>(resultSample, SHRT_MAX);
samplesSpu0[i] = static_cast<int16>(resultSample);
}
}
2015-04-06 00:32:43 -04:00
m_currentSpuBlock++;
2019-10-11 12:03:22 +01:00
if(m_currentSpuBlock == m_spuBlockCount)
{
2014-10-20 22:34:44 -04:00
if(m_soundHandler)
{
m_soundHandler->RecycleBuffers();
2019-10-11 12:03:22 +01:00
m_soundHandler->Write(m_samples, BLOCK_SIZE * m_spuBlockCount, DST_SAMPLE_RATE);
}
2015-04-06 00:32:43 -04:00
m_currentSpuBlock = 0;
}
}
2017-09-05 00:16:05 -04:00
void CPS2VM::CDROM0_SyncPath()
{
//TODO: Check if there's an m_cdrom0 already
//TODO: Check if files are linked to this m_cdrom0 too and do something with them
2017-09-05 00:16:05 -04:00
CDROM0_Reset();
2018-01-15 14:14:59 -05:00
auto path = CAppConfig::GetInstance().GetPreferencePath(PREF_PS2_CDROM0_PATH);
if(!path.empty())
{
try
{
m_cdrom0 = DiskUtils::CreateOpticalMediaFromPath(path);
SetIopOpticalMedia(m_cdrom0.get());
}
catch(const std::exception& Exception)
{
printf("PS2VM: Error mounting cdrom0 device: %s\r\n", Exception.what());
}
}
}
2017-09-05 00:16:05 -04:00
void CPS2VM::CDROM0_Reset()
{
SetIopOpticalMedia(nullptr);
m_cdrom0.reset();
}
void CPS2VM::SetIopOpticalMedia(COpticalMedia* opticalMedia)
{
auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
assert(iopOs);
iopOs->GetCdvdfsv()->SetOpticalMedia(opticalMedia);
iopOs->GetCdvdman()->SetOpticalMedia(opticalMedia);
}
void CPS2VM::RegisterModulesInPadHandler()
{
2015-11-12 10:14:17 -05:00
if(m_pad == nullptr) return;
auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
assert(iopOs);
m_pad->RemoveAllListeners();
m_pad->InsertListener(iopOs->GetPadman());
m_pad->InsertListener(&m_iop->m_sio2);
}
void CPS2VM::ReloadExecutable(const char* executablePath, const CPS2OS::ArgumentList& arguments)
{
{
//SPU RAM is not cleared by a LoadExecPS2 operation, we must keep its contents
//Deus Ex uses SPU RAM to keep game state in between executable reloads
auto savedSpuRam = std::vector<uint8>(PS2::SPU_RAM_SIZE);
memcpy(savedSpuRam.data(), m_iop->m_spuRam, PS2::SPU_RAM_SIZE);
ResetVM();
memcpy(m_iop->m_spuRam, savedSpuRam.data(), PS2::SPU_RAM_SIZE);
}
if(BeforeExecutableReloaded)
{
BeforeExecutableReloaded(this);
}
m_ee->m_os->BootFromVirtualPath(executablePath, arguments);
if(AfterExecutableReloaded)
{
AfterExecutableReloaded(this);
}
}
2021-02-24 16:04:10 -05:00
void CPS2VM::OnCrtModeChange()
{
ReloadFrameRateLimit();
}
void CPS2VM::EmuThread()
{
2021-11-27 10:19:57 -05:00
CreateVM();
fesetround(FE_TOWARDZERO);
2020-03-06 13:07:01 -05:00
FpUtils::SetDenormalHandlingMode();
CProfiler::GetInstance().SetWorkThread();
#ifdef __ANDROID__
JNIEnv* env = nullptr;
Framework::CJavaVM::AttachCurrentThread(&env, THREAD_NAME);
#endif
#ifdef PROFILE
CProfilerZone profilerZone(m_otherProfilerZone);
#endif
static_cast<CEeExecutor*>(m_ee->m_EE.m_executor.get())->AddExceptionHandler();
m_frameLimiter.BeginFrame();
while(1)
{
while(m_mailBox.IsPending())
{
m_mailBox.ReceiveCall();
}
if(m_nEnd) break;
if(m_nStatus == PAUSED)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if(m_nStatus == RUNNING)
{
if(m_spuUpdateTicks <= 0)
{
UpdateSpu();
m_spuUpdateTicks += SPU_UPDATE_TICKS;
}
//EE execution
{
//Check vblank stuff
if(m_vblankTicks <= 0)
{
m_inVblank = !m_inVblank;
if(m_inVblank)
{
m_vblankTicks += m_vblankTicksTotal;
m_ee->NotifyVBlankStart();
m_iop->NotifyVBlankStart();
if(m_ee->m_gs != NULL)
{
#ifdef PROFILE
CProfilerZone profilerZone(m_gsSyncProfilerZone);
#endif
m_ee->m_gs->SetVBlank();
}
if(m_pad != NULL)
{
m_pad->Update(m_ee->m_ram);
}
#ifdef PROFILE
{
CProfiler::GetInstance().CountCurrentZone();
auto stats = CProfiler::GetInstance().GetStats();
ProfileFrameDone(stats);
CProfiler::GetInstance().Reset();
}
#endif
m_cpuUtilisation = CPU_UTILISATION_INFO();
}
else
{
m_vblankTicks += m_onScreenTicksTotal;
m_ee->NotifyVBlankEnd();
m_iop->NotifyVBlankEnd();
if(m_ee->m_gs != NULL)
{
m_ee->m_gs->ResetVBlank();
}
m_frameLimiter.EndFrame();
m_frameLimiter.BeginFrame();
}
}
//EE CPU is 8 times faster than the IOP CPU
static const int tickStep = 4800;
m_eeExecutionTicks += tickStep;
m_iopExecutionTicks += tickStep / 8;
UpdateEe();
UpdateIop();
}
#ifdef DEBUGGER_INCLUDED
if(
m_ee->m_EE.m_executor->MustBreak() ||
m_iop->m_cpu.m_executor->MustBreak() ||
m_ee->m_VU1.m_executor->MustBreak() ||
2018-04-30 21:01:23 +01:00
m_singleStepEe || m_singleStepIop || m_singleStepVu0 || m_singleStepVu1)
{
m_nStatus = PAUSED;
m_singleStepEe = false;
m_singleStepIop = false;
m_singleStepVu0 = false;
m_singleStepVu1 = false;
OnRunningStateChange();
OnMachineStateChange();
}
#endif
}
}
static_cast<CEeExecutor*>(m_ee->m_EE.m_executor.get())->RemoveExceptionHandler();
#ifdef __ANDROID__
Framework::CJavaVM::DetachCurrentThread();
#endif
}