2012-09-12 01:03:53 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <exception>
|
2015-05-06 04:54:15 +00:00
|
|
|
#include <boost/filesystem.hpp>
|
2012-09-12 01:03:53 +00:00
|
|
|
#include <memory>
|
2015-04-20 04:22:53 +00:00
|
|
|
#include <fenv.h>
|
2014-11-27 08:39:03 +00:00
|
|
|
#include "make_unique.h"
|
2012-09-12 01:03:53 +00:00
|
|
|
#include "PS2VM.h"
|
|
|
|
#include "PS2VM_Preferences.h"
|
2015-05-06 04:54:15 +00:00
|
|
|
#include "ee/PS2OS.h"
|
2012-09-12 01:03:53 +00:00
|
|
|
#include "Ps2Const.h"
|
|
|
|
#include "iop/Iop_SifManPs2.h"
|
|
|
|
#include "StdStream.h"
|
|
|
|
#include "GZipStream.h"
|
|
|
|
#include "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"
|
|
|
|
#include "iop/IopBios.h"
|
|
|
|
#include "iop/DirectoryDevice.h"
|
|
|
|
#include "iop/IsoDevice.h"
|
|
|
|
#include "Log.h"
|
2015-05-18 01:47:31 +00:00
|
|
|
#include "ISO9660/BlockProvider.h"
|
2015-07-22 04:30:06 +00:00
|
|
|
#include "DiskUtils.h"
|
2012-09-12 01:03:53 +00:00
|
|
|
|
|
|
|
#define LOG_NAME ("ps2vm")
|
|
|
|
|
|
|
|
#define PREF_PS2_HOST_DIRECTORY_DEFAULT ("vfs/host")
|
|
|
|
#define PREF_PS2_MC0_DIRECTORY_DEFAULT ("vfs/mc0")
|
|
|
|
#define PREF_PS2_MC1_DIRECTORY_DEFAULT ("vfs/mc1")
|
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
#define FRAME_TICKS (PS2::EE_CLOCK_FREQ / 60)
|
|
|
|
#define ONSCREEN_TICKS (FRAME_TICKS * 9 / 10)
|
|
|
|
#define VBLANK_TICKS (FRAME_TICKS / 10)
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2014-10-21 02:34:44 +00:00
|
|
|
#define SPU_UPDATE_TICKS (PS2::IOP_CLOCK_OVER_FREQ / 1000)
|
2012-09-12 01:03:53 +00:00
|
|
|
|
|
|
|
namespace filesystem = boost::filesystem;
|
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
CPS2VM::CPS2VM()
|
|
|
|
: m_nStatus(PAUSED)
|
2012-09-12 01:03:53 +00:00
|
|
|
, m_nEnd(false)
|
2012-12-23 21:35:04 +00:00
|
|
|
, m_pad(NULL)
|
2012-09-12 01:03:53 +00:00
|
|
|
, m_singleStepEe(false)
|
|
|
|
, m_singleStepIop(false)
|
|
|
|
, m_singleStepVu0(false)
|
|
|
|
, m_singleStepVu1(false)
|
|
|
|
, m_vblankTicks(0)
|
|
|
|
, m_inVblank(false)
|
2012-09-25 03:17:46 +00:00
|
|
|
, m_eeExecutionTicks(0)
|
|
|
|
, m_iopExecutionTicks(0)
|
2012-09-12 01:03:53 +00:00
|
|
|
, m_spuUpdateTicks(SPU_UPDATE_TICKS)
|
2014-10-05 05:41:06 +00:00
|
|
|
, m_eeProfilerZone(CProfiler::GetInstance().RegisterZone("EE"))
|
|
|
|
, m_iopProfilerZone(CProfiler::GetInstance().RegisterZone("IOP"))
|
|
|
|
, m_spuProfilerZone(CProfiler::GetInstance().RegisterZone("SPU"))
|
|
|
|
, m_gsSyncProfilerZone(CProfiler::GetInstance().RegisterZone("GSSYNC"))
|
|
|
|
, m_otherProfilerZone(CProfiler::GetInstance().RegisterZone("OTHER"))
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2016-09-22 01:53:47 +00:00
|
|
|
static const char* basicDirectorySettings[] =
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
|
|
|
PREF_PS2_HOST_DIRECTORY, PREF_PS2_HOST_DIRECTORY_DEFAULT,
|
|
|
|
PREF_PS2_MC0_DIRECTORY, PREF_PS2_MC0_DIRECTORY_DEFAULT,
|
|
|
|
PREF_PS2_MC1_DIRECTORY, PREF_PS2_MC1_DIRECTORY_DEFAULT,
|
|
|
|
NULL, NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
for(unsigned int i = 0; basicDirectorySettings[i] != NULL; i += 2)
|
|
|
|
{
|
|
|
|
const char* setting = basicDirectorySettings[i + 0];
|
|
|
|
const char* path = basicDirectorySettings[i + 1];
|
|
|
|
|
|
|
|
Framework::CConfig::PathType absolutePath = CAppConfig::GetBasePath() / path;
|
|
|
|
Framework::PathUtils::EnsurePathExists(absolutePath);
|
|
|
|
//TODO: We ought to add a function to write a "path" in the settings. Since it can be wchar_t or char.
|
|
|
|
CAppConfig::GetInstance().RegisterPreferenceString(setting, absolutePath.string().c_str());
|
|
|
|
}
|
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
m_iop = std::make_unique<Iop::CSubSystem>(true);
|
2016-03-17 23:20:02 +00:00
|
|
|
m_iopOs = std::make_shared<CIopBios>(m_iop->m_cpu, m_iop->m_ram, PS2::IOP_RAM_SIZE, m_iop->m_scratchPad);
|
2014-10-05 03:02:32 +00:00
|
|
|
|
|
|
|
m_ee = std::make_unique<Ee::CSubSystem>(m_iop->m_ram, *m_iopOs);
|
|
|
|
m_ee->m_os->OnRequestLoadExecutable.connect(boost::bind(&CPS2VM::ReloadExecutable, this, _1, _2));
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CPS2VM::~CPS2VM()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
//Big hack to force deletion of the IopBios
|
2014-10-05 03:02:32 +00:00
|
|
|
m_iop->SetBios(Iop::BiosBasePtr());
|
2012-10-21 02:08:14 +00:00
|
|
|
m_iopOs.reset();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
//Various Message Functions
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void CPS2VM::CreateGSHandler(const CGSHandler::FactoryFunction& factoryFunction)
|
|
|
|
{
|
2015-04-06 19:52:09 +00:00
|
|
|
if(m_ee->m_gs != nullptr) return;
|
2016-04-03 18:06:11 +00:00
|
|
|
m_mailBox.SendCall([this, factoryFunction] () { CreateGsHandlerImpl(factoryFunction); }, true);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CGSHandler* CPS2VM::GetGSHandler()
|
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
return m_ee->m_gs;
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DestroyGSHandler()
|
|
|
|
{
|
2015-04-06 19:52:09 +00:00
|
|
|
if(m_ee->m_gs == nullptr) return;
|
2016-04-03 18:06:11 +00:00
|
|
|
m_mailBox.SendCall([this] () { DestroyGsHandlerImpl(); }, true);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::CreatePadHandler(const CPadHandler::FactoryFunction& factoryFunction)
|
|
|
|
{
|
2015-04-06 19:52:09 +00:00
|
|
|
if(m_pad != nullptr) return;
|
2016-04-03 18:06:11 +00:00
|
|
|
m_mailBox.SendCall([this, factoryFunction] () { CreatePadHandlerImpl(factoryFunction); }, true);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
2015-05-06 05:49:23 +00:00
|
|
|
CPadHandler* CPS2VM::GetPadHandler()
|
|
|
|
{
|
|
|
|
return m_pad;
|
|
|
|
}
|
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
void CPS2VM::DestroyPadHandler()
|
|
|
|
{
|
2015-04-06 19:52:09 +00:00
|
|
|
if(m_pad == nullptr) return;
|
2016-04-03 18:06:11 +00:00
|
|
|
m_mailBox.SendCall([this] () { DestroyPadHandlerImpl(); }, true);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
2015-04-06 19:51:59 +00:00
|
|
|
void CPS2VM::CreateSoundHandler(const CSoundHandler::FactoryFunction& factoryFunction)
|
|
|
|
{
|
|
|
|
if(m_soundHandler != nullptr) return;
|
2016-04-03 18:06:11 +00:00
|
|
|
m_mailBox.SendCall([this, factoryFunction] () { CreateSoundHandlerImpl(factoryFunction); }, true);
|
2015-04-06 19:51:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DestroySoundHandler()
|
|
|
|
{
|
|
|
|
if(m_soundHandler == nullptr) return;
|
2016-04-03 18:06:11 +00:00
|
|
|
m_mailBox.SendCall([this] () { DestroySoundHandlerImpl(); }, true);
|
2015-04-06 19:51:59 +00:00
|
|
|
}
|
|
|
|
|
2012-09-12 01:03:53 +00: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);
|
|
|
|
}
|
|
|
|
|
2014-12-30 00:45:53 +00:00
|
|
|
void CPS2VM::StepVu0()
|
|
|
|
{
|
|
|
|
if(GetStatus() == RUNNING) return;
|
|
|
|
m_singleStepVu0 = true;
|
|
|
|
m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
|
|
|
|
}
|
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::Reset()
|
|
|
|
{
|
|
|
|
assert(m_nStatus == PAUSED);
|
|
|
|
ResetVM();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DumpEEIntcHandlers()
|
|
|
|
{
|
|
|
|
// if(m_pOS == NULL) return;
|
|
|
|
if(m_nStatus != PAUSED) return;
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_os->DumpIntcHandlers();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DumpEEDmacHandlers()
|
|
|
|
{
|
|
|
|
// if(m_pOS == NULL) return;
|
|
|
|
if(m_nStatus != PAUSED) return;
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_os->DumpDmacHandlers();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::Initialize()
|
|
|
|
{
|
|
|
|
CreateVM();
|
|
|
|
m_nEnd = false;
|
2013-04-25 05:51:31 +00:00
|
|
|
m_thread = std::thread([&] () { EmuThread(); });
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::Destroy()
|
|
|
|
{
|
|
|
|
m_mailBox.SendCall(std::bind(&CPS2VM::DestroyImpl, this));
|
2013-04-25 05:51:31 +00:00
|
|
|
m_thread.join();
|
2012-09-12 01:03:53 +00:00
|
|
|
DestroyVM();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int CPS2VM::SaveState(const char* sPath)
|
|
|
|
{
|
|
|
|
unsigned int result = 0;
|
|
|
|
m_mailBox.SendCall(std::bind(&CPS2VM::SaveVMState, this, sPath, std::ref(result)), true);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int CPS2VM::LoadState(const char* sPath)
|
|
|
|
{
|
|
|
|
unsigned int result = 0;
|
|
|
|
m_mailBox.SendCall(std::bind(&CPS2VM::LoadVMState, this, sPath, std::ref(result)), true);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-06-09 05:47:33 +00:00
|
|
|
void CPS2VM::TriggerFrameDump(const FrameDumpCallback& frameDumpCallback)
|
|
|
|
{
|
|
|
|
m_mailBox.SendCall(
|
|
|
|
[=] ()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> frameDumpCallbackMutexLock(m_frameDumpCallbackMutex);
|
|
|
|
if(m_frameDumpCallback) return;
|
|
|
|
m_frameDumpCallback = frameDumpCallback;
|
|
|
|
},
|
|
|
|
false
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
|
2013-06-09 05:47:33 +00: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")
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2015-05-04 04:22:45 +00:00
|
|
|
#define TAGS_PATH ("tags/")
|
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
std::string CPS2VM::MakeDebugTagsPackagePath(const char* packageName)
|
|
|
|
{
|
2015-05-04 04:22:45 +00:00
|
|
|
auto tagsPath = CAppConfig::GetBasePath() / boost::filesystem::path(TAGS_PATH);
|
|
|
|
Framework::PathUtils::EnsurePathExists(tagsPath);
|
|
|
|
auto tagsPackagePath = tagsPath / (std::string(packageName) + std::string(".tags.xml"));
|
|
|
|
return tagsPackagePath.string();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::LoadDebugTags(const char* packageName)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::string packagePath = MakeDebugTagsPackagePath(packageName);
|
|
|
|
Framework::CStdStream stream(packagePath.c_str(), "rb");
|
2012-12-23 21:35:04 +00:00
|
|
|
boost::scoped_ptr<Framework::Xml::CNode> document(Framework::Xml::CParser::ParseDocument(stream));
|
2012-09-12 01:03:53 +00:00
|
|
|
Framework::Xml::CNode* tagsNode = document->Select(TAGS_SECTION_TAGS);
|
|
|
|
if(!tagsNode) return;
|
2014-10-05 03:02:32 +00:00
|
|
|
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);
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
|
|
|
Framework::Xml::CNode* sectionNode = tagsNode->Select(TAGS_SECTION_IOP);
|
|
|
|
if(sectionNode)
|
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_iop->m_cpu.m_Functions.Unserialize(sectionNode, TAGS_SECTION_IOP_FUNCTIONS);
|
|
|
|
m_iop->m_cpu.m_Comments.Unserialize(sectionNode, TAGS_SECTION_IOP_COMMENTS);
|
2012-09-12 01:03:53 +00:00
|
|
|
m_iopOs->LoadDebugTags(sectionNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::SaveDebugTags(const char* packageName)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::string packagePath = MakeDebugTagsPackagePath(packageName);
|
|
|
|
Framework::CStdStream stream(packagePath.c_str(), "wb");
|
|
|
|
boost::scoped_ptr<Framework::Xml::CNode> document(new Framework::Xml::CNode(TAGS_SECTION_TAGS, true));
|
2014-10-05 03:02:32 +00:00
|
|
|
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);
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
|
|
|
Framework::Xml::CNode* iopNode = new Framework::Xml::CNode(TAGS_SECTION_IOP, true);
|
2014-10-05 03:02:32 +00:00
|
|
|
m_iop->m_cpu.m_Functions.Serialize(iopNode, TAGS_SECTION_IOP_FUNCTIONS);
|
|
|
|
m_iop->m_cpu.m_Comments.Serialize(iopNode, TAGS_SECTION_IOP_COMMENTS);
|
2012-09-12 01:03:53 +00:00
|
|
|
m_iopOs->SaveDebugTags(iopNode);
|
|
|
|
document->InsertNode(iopNode);
|
|
|
|
}
|
2013-01-29 05:46:29 +00:00
|
|
|
Framework::Xml::CWriter::WriteDocument(stream, document.get());
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
//Non extern callable methods
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void CPS2VM::CreateVM()
|
|
|
|
{
|
|
|
|
CDROM0_Initialize();
|
|
|
|
ResetVM();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::ResetVM()
|
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->Reset();
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
m_iop->Reset();
|
|
|
|
m_iop->SetBios(m_iopOs);
|
2012-09-12 01:03:53 +00:00
|
|
|
|
|
|
|
//LoadBIOS();
|
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
if(m_ee->m_gs != NULL)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_gs->Reset();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
2016-01-14 03:18:10 +00:00
|
|
|
m_iopOs->Reset(std::make_shared<Iop::CSifManPs2>(m_ee->m_sif, m_ee->m_ram, m_iop->m_ram));
|
2012-09-12 01:03:53 +00:00
|
|
|
|
|
|
|
CDROM0_Reset();
|
|
|
|
|
|
|
|
m_iopOs->GetIoman()->RegisterDevice("host", Iop::CIoman::DevicePtr(new Iop::Ioman::CDirectoryDevice(PREF_PS2_HOST_DIRECTORY)));
|
|
|
|
m_iopOs->GetIoman()->RegisterDevice("mc0", Iop::CIoman::DevicePtr(new Iop::Ioman::CDirectoryDevice(PREF_PS2_MC0_DIRECTORY)));
|
|
|
|
m_iopOs->GetIoman()->RegisterDevice("mc1", Iop::CIoman::DevicePtr(new Iop::Ioman::CDirectoryDevice(PREF_PS2_MC1_DIRECTORY)));
|
2016-06-04 02:18:09 +00:00
|
|
|
m_iopOs->GetIoman()->RegisterDevice("cdrom", Iop::CIoman::DevicePtr(new Iop::Ioman::CIsoDevice(m_cdrom0)));
|
2015-05-17 23:19:00 +00:00
|
|
|
m_iopOs->GetIoman()->RegisterDevice("cdrom0", Iop::CIoman::DevicePtr(new Iop::Ioman::CIsoDevice(m_cdrom0)));
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
m_iopOs->GetLoadcore()->SetLoadExecutableHandler(std::bind(&CPS2OS::LoadExecutable, m_ee->m_os, std::placeholders::_1, std::placeholders::_2));
|
2012-10-24 06:39:29 +00:00
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
m_vblankTicks = ONSCREEN_TICKS;
|
|
|
|
m_inVblank = false;
|
|
|
|
|
2012-09-25 03:17:46 +00:00
|
|
|
m_eeExecutionTicks = 0;
|
|
|
|
m_iopExecutionTicks = 0;
|
|
|
|
|
2015-04-06 04:32:43 +00:00
|
|
|
m_spuUpdateTicks = SPU_UPDATE_TICKS;
|
|
|
|
m_currentSpuBlock = 0;
|
2014-10-21 02:34:44 +00:00
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
RegisterModulesInPadHandler();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DestroyVM()
|
|
|
|
{
|
|
|
|
CDROM0_Destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::SaveVMState(const char* sPath, unsigned int& result)
|
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
if(m_ee->m_gs == NULL)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
|
|
|
printf("PS2VM: GS Handler was not instancied. Cannot save state.\r\n");
|
|
|
|
result = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Framework::CStdStream stateStream(sPath, "wb");
|
|
|
|
Framework::CZipArchiveWriter archive;
|
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->SaveState(archive);
|
|
|
|
m_iop->SaveState(archive);
|
|
|
|
m_ee->m_gs->SaveState(archive);
|
2012-09-12 01:03:53 +00:00
|
|
|
m_iopOs->GetPadman()->SaveState(archive);
|
|
|
|
//TODO: Save CDVDFSV state
|
|
|
|
|
|
|
|
archive.Write(stateStream);
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
result = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("PS2VM: Saved state to file '%s'.\r\n", sPath);
|
|
|
|
|
|
|
|
result = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::LoadVMState(const char* sPath, unsigned int& result)
|
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
if(m_ee->m_gs == NULL)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
|
|
|
printf("PS2VM: GS Handler was not instancied. Cannot load state.\r\n");
|
|
|
|
result = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Framework::CStdStream stateStream(sPath, "rb");
|
|
|
|
Framework::CZipArchiveReader archive(stateStream);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->LoadState(archive);
|
|
|
|
m_iop->LoadState(archive);
|
|
|
|
m_ee->m_gs->LoadState(archive);
|
2012-09-12 01:03:53 +00:00
|
|
|
m_iopOs->GetPadman()->LoadState(archive);
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
//Any error that occurs in the previous block is critical
|
|
|
|
PauseImpl();
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(...)
|
|
|
|
{
|
|
|
|
result = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("PS2VM: Loaded state from file '%s'.\r\n", sPath);
|
|
|
|
|
|
|
|
OnMachineStateChange();
|
|
|
|
|
|
|
|
result = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::PauseImpl()
|
|
|
|
{
|
|
|
|
m_nStatus = PAUSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::ResumeImpl()
|
|
|
|
{
|
2012-09-29 01:28:23 +00:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_executor.DisableBreakpointsOnce();
|
|
|
|
m_iop->m_executor.DisableBreakpointsOnce();
|
2015-06-17 20:14:51 +00:00
|
|
|
m_ee->m_vpu1->DisableBreakpointsOnce();
|
2012-09-29 01:28:23 +00:00
|
|
|
#endif
|
2012-09-12 01:03:53 +00:00
|
|
|
m_nStatus = RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DestroyImpl()
|
|
|
|
{
|
2016-04-03 18:06:11 +00:00
|
|
|
DestroyGsHandlerImpl();
|
|
|
|
DestroyPadHandlerImpl();
|
|
|
|
DestroySoundHandlerImpl();
|
2012-09-12 01:03:53 +00:00
|
|
|
m_nEnd = true;
|
|
|
|
}
|
|
|
|
|
2016-04-03 18:06:11 +00:00
|
|
|
void CPS2VM::CreateGsHandlerImpl(const CGSHandler::FactoryFunction& factoryFunction)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_gs = factoryFunction();
|
|
|
|
m_ee->m_gs->Initialize();
|
|
|
|
m_ee->m_gs->OnNewFrame.connect(boost::bind(&CPS2VM::OnGsNewFrame, this));
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
2016-04-03 18:06:11 +00:00
|
|
|
void CPS2VM::DestroyGsHandlerImpl()
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2016-04-03 18:06:11 +00:00
|
|
|
if(m_ee->m_gs == nullptr) return;
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_gs->Release();
|
2016-04-03 18:06:11 +00:00
|
|
|
delete m_ee->m_gs;
|
|
|
|
m_ee->m_gs = nullptr;
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::CreatePadHandlerImpl(const CPadHandler::FactoryFunction& factoryFunction)
|
|
|
|
{
|
2012-12-23 21:35:04 +00:00
|
|
|
m_pad = factoryFunction();
|
2012-09-12 01:03:53 +00:00
|
|
|
RegisterModulesInPadHandler();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DestroyPadHandlerImpl()
|
|
|
|
{
|
2016-04-03 18:06:11 +00:00
|
|
|
if(m_pad == nullptr) return;
|
|
|
|
delete m_pad;
|
|
|
|
m_pad = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::CreateSoundHandlerImpl(const CSoundHandler::FactoryFunction& factoryFunction)
|
|
|
|
{
|
|
|
|
m_soundHandler = factoryFunction();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::DestroySoundHandlerImpl()
|
|
|
|
{
|
|
|
|
if(m_soundHandler == nullptr) return;
|
|
|
|
delete m_soundHandler;
|
|
|
|
m_soundHandler = nullptr;
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::OnGsNewFrame()
|
|
|
|
{
|
2013-06-09 05:47:33 +00:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
std::unique_lock<std::mutex> dumpFrameCallbackMutexLock(m_frameDumpCallbackMutex);
|
2015-05-09 02:39:34 +00:00
|
|
|
if(m_dumpingFrame && !m_frameDump.GetPackets().empty())
|
2013-06-09 05:47:33 +00:00
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_gs->SetFrameDump(nullptr);
|
2013-06-09 05:47:33 +00:00
|
|
|
m_frameDumpCallback(m_frameDump);
|
|
|
|
m_dumpingFrame = false;
|
|
|
|
m_frameDumpCallback = FrameDumpCallback();
|
|
|
|
}
|
|
|
|
else if(m_frameDumpCallback)
|
|
|
|
{
|
|
|
|
m_frameDump.Reset();
|
2014-10-05 03:02:32 +00:00
|
|
|
memcpy(m_frameDump.GetInitialGsRam(), m_ee->m_gs->GetRam(), CGSHandler::RAMSIZE);
|
|
|
|
memcpy(m_frameDump.GetInitialGsRegisters(), m_ee->m_gs->GetRegisters(), CGSHandler::REGISTER_MAX * sizeof(uint64));
|
2014-11-22 06:23:49 +00:00
|
|
|
m_frameDump.SetInitialSMODE2(m_ee->m_gs->GetSMODE2());
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_gs->SetFrameDump(&m_frameDump);
|
2013-06-09 05:47:33 +00:00
|
|
|
m_dumpingFrame = true;
|
|
|
|
}
|
|
|
|
#endif
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
2014-10-05 05:41:06 +00:00
|
|
|
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())
|
|
|
|
{
|
|
|
|
executed = m_eeExecutionTicks;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_eeExecutionTicks -= executed;
|
|
|
|
m_ee->CountTicks(executed);
|
|
|
|
m_vblankTicks -= executed;
|
|
|
|
|
2015-01-01 23:42:34 +00:00
|
|
|
//Stop executing if executing VU subroutine
|
|
|
|
if(m_ee->m_EE.m_State.callMsEnabled) break;
|
|
|
|
|
2014-10-05 05:41:06 +00:00
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
if(m_singleStepEe) break;
|
|
|
|
if(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())
|
|
|
|
{
|
|
|
|
executed = m_iopExecutionTicks;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_iopExecutionTicks -= executed;
|
2014-10-21 02:34:44 +00:00
|
|
|
m_spuUpdateTicks -= executed;
|
2014-10-05 05:41:06 +00:00
|
|
|
m_iop->CountTicks(executed);
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
if(m_singleStepIop) break;
|
|
|
|
if(m_iop->m_executor.MustBreak()) break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
void CPS2VM::UpdateSpu()
|
|
|
|
{
|
2014-10-05 05:41:06 +00:00
|
|
|
#ifdef PROFILE
|
|
|
|
CProfilerZone profilerZone(m_spuProfilerZone);
|
|
|
|
#endif
|
|
|
|
|
2015-04-06 04:32:43 +00:00
|
|
|
unsigned int blockOffset = (BLOCK_SIZE * m_currentSpuBlock);
|
2014-10-21 02:34:44 +00:00
|
|
|
int16* samplesSpu0 = m_samples + blockOffset;
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2014-10-21 02:34:44 +00:00
|
|
|
m_iop->m_spuCore0.Render(samplesSpu0, BLOCK_SIZE, 44100);
|
|
|
|
|
|
|
|
if(m_iop->m_spuCore1.IsEnabled())
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-21 02:34:44 +00:00
|
|
|
int16 samplesSpu1[BLOCK_SIZE];
|
|
|
|
m_iop->m_spuCore1.Render(samplesSpu1, BLOCK_SIZE, 44100);
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2014-10-21 02:34:44 +00:00
|
|
|
for(unsigned int i = 0; i < BLOCK_SIZE; i++)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-21 02:34:44 +00: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 04:32:43 +00:00
|
|
|
m_currentSpuBlock++;
|
|
|
|
if(m_currentSpuBlock == BLOCK_COUNT)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-21 02:34:44 +00:00
|
|
|
if(m_soundHandler)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-21 02:34:44 +00:00
|
|
|
if(m_soundHandler->HasFreeBuffers())
|
|
|
|
{
|
|
|
|
m_soundHandler->RecycleBuffers();
|
|
|
|
}
|
|
|
|
m_soundHandler->Write(m_samples, BLOCK_SIZE * BLOCK_COUNT, 44100);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
2015-04-06 04:32:43 +00:00
|
|
|
m_currentSpuBlock = 0;
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::CDROM0_Initialize()
|
|
|
|
{
|
|
|
|
CAppConfig::GetInstance().RegisterPreferenceString(PS2VM_CDROM0PATH, "");
|
2015-05-17 23:19:00 +00:00
|
|
|
m_cdrom0.reset();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::CDROM0_Reset()
|
|
|
|
{
|
2015-05-17 23:19:00 +00:00
|
|
|
m_cdrom0.reset();
|
2012-09-12 01:03:53 +00:00
|
|
|
CDROM0_Mount(CAppConfig::GetInstance().GetPreferenceString(PS2VM_CDROM0PATH));
|
|
|
|
}
|
|
|
|
|
2015-04-01 02:20:49 +00:00
|
|
|
void CPS2VM::CDROM0_Mount(const char* path)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2015-07-22 04:30:06 +00:00
|
|
|
//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
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2015-04-01 02:20:49 +00:00
|
|
|
size_t pathLength = strlen(path);
|
2012-09-12 01:03:53 +00:00
|
|
|
if(pathLength != 0)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2015-07-22 04:30:06 +00:00
|
|
|
m_cdrom0 = DiskUtils::CreateDiskImageFromPath(path);
|
2015-05-17 23:19:00 +00:00
|
|
|
SetIopCdImage(m_cdrom0.get());
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
catch(const std::exception& Exception)
|
|
|
|
{
|
|
|
|
printf("PS2VM: Error mounting cdrom0 device: %s\r\n", Exception.what());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-01 02:20:49 +00:00
|
|
|
CAppConfig::GetInstance().SetPreferenceString(PS2VM_CDROM0PATH, path);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::CDROM0_Destroy()
|
|
|
|
{
|
2015-05-17 23:19:00 +00:00
|
|
|
SetIopCdImage(nullptr);
|
|
|
|
m_cdrom0.reset();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::SetIopCdImage(CISO9660* image)
|
|
|
|
{
|
|
|
|
m_iopOs->GetCdvdfsv()->SetIsoImage(image);
|
|
|
|
m_iopOs->GetCdvdman()->SetIsoImage(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::RegisterModulesInPadHandler()
|
|
|
|
{
|
2015-11-12 15:14:17 +00:00
|
|
|
if(m_pad == nullptr) return;
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2012-12-23 21:35:04 +00:00
|
|
|
m_pad->RemoveAllListeners();
|
|
|
|
m_pad->InsertListener(m_iopOs->GetPadman());
|
2014-10-05 03:02:32 +00:00
|
|
|
m_pad->InsertListener(&m_iop->m_sio2);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPS2VM::ReloadExecutable(const char* executablePath, const CPS2OS::ArgumentList& arguments)
|
|
|
|
{
|
|
|
|
ResetVM();
|
2015-12-03 03:40:54 +00:00
|
|
|
m_ee->m_os->BootFromVirtualPath(executablePath, arguments);
|
2012-09-25 03:17:46 +00:00
|
|
|
}
|
|
|
|
|
2012-09-12 01:03:53 +00:00
|
|
|
void CPS2VM::EmuThread()
|
|
|
|
{
|
2015-04-20 04:22:53 +00:00
|
|
|
fesetround(FE_TOWARDZERO);
|
2012-12-23 21:35:04 +00:00
|
|
|
CProfiler::GetInstance().SetWorkThread();
|
2016-07-17 20:28:06 +00:00
|
|
|
#ifdef PROFILE
|
|
|
|
CProfilerZone profilerZone(m_otherProfilerZone);
|
|
|
|
#endif
|
2015-06-09 06:49:54 +00:00
|
|
|
m_ee->m_executor.AddExceptionHandler();
|
2012-09-12 01:03:53 +00:00
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
while(m_mailBox.IsPending())
|
|
|
|
{
|
|
|
|
m_mailBox.ReceiveCall();
|
|
|
|
}
|
|
|
|
if(m_nEnd) break;
|
|
|
|
if(m_nStatus == PAUSED)
|
|
|
|
{
|
2013-06-17 05:06:47 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
if(m_nStatus == RUNNING)
|
|
|
|
{
|
2013-03-06 05:36:03 +00:00
|
|
|
if(m_spuUpdateTicks <= 0)
|
|
|
|
{
|
|
|
|
UpdateSpu();
|
|
|
|
m_spuUpdateTicks += SPU_UPDATE_TICKS;
|
|
|
|
}
|
2012-09-12 01:03:53 +00:00
|
|
|
|
|
|
|
//EE execution
|
|
|
|
{
|
|
|
|
//Check vblank stuff
|
|
|
|
if(m_vblankTicks <= 0)
|
|
|
|
{
|
|
|
|
m_inVblank = !m_inVblank;
|
|
|
|
if(m_inVblank)
|
|
|
|
{
|
|
|
|
m_vblankTicks += VBLANK_TICKS;
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->NotifyVBlankStart();
|
|
|
|
m_iop->NotifyVBlankStart();
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2014-10-05 03:02:32 +00:00
|
|
|
if(m_ee->m_gs != NULL)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-05 05:41:06 +00:00
|
|
|
#ifdef PROFILE
|
|
|
|
CProfilerZone profilerZone(m_gsSyncProfilerZone);
|
|
|
|
#endif
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_gs->SetVBlank();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
|
2012-12-23 21:35:04 +00:00
|
|
|
if(m_pad != NULL)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_pad->Update(m_ee->m_ram);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
2014-10-05 05:41:06 +00:00
|
|
|
#ifdef PROFILE
|
|
|
|
{
|
2016-07-17 20:28:06 +00:00
|
|
|
CProfiler::GetInstance().CountCurrentZone();
|
2014-10-05 05:41:06 +00:00
|
|
|
auto stats = CProfiler::GetInstance().GetStats();
|
|
|
|
ProfileFrameDone(stats);
|
|
|
|
CProfiler::GetInstance().Reset();
|
|
|
|
}
|
|
|
|
#endif
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_vblankTicks += ONSCREEN_TICKS;
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->NotifyVBlankEnd();
|
|
|
|
m_iop->NotifyVBlankEnd();
|
|
|
|
if(m_ee->m_gs != NULL)
|
2012-09-12 01:03:53 +00:00
|
|
|
{
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_gs->ResetVBlank();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-01 23:23:45 +00:00
|
|
|
//EE CPU is 8 times faster than the IOP CPU
|
|
|
|
static const int tickStep = 480;
|
|
|
|
m_eeExecutionTicks += tickStep;
|
|
|
|
m_iopExecutionTicks += tickStep / 8;
|
|
|
|
|
2014-10-05 05:41:06 +00:00
|
|
|
UpdateEe();
|
|
|
|
UpdateIop();
|
2012-09-12 01:03:53 +00:00
|
|
|
|
2015-06-17 20:14:51 +00:00
|
|
|
m_ee->m_vpu0->Execute(m_singleStepVu0);
|
|
|
|
m_ee->m_vpu1->Execute(m_singleStepVu1);
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|
|
|
|
#ifdef DEBUGGER_INCLUDED
|
|
|
|
if(
|
2014-10-05 03:02:32 +00:00
|
|
|
m_ee->m_executor.MustBreak() ||
|
|
|
|
m_iop->m_executor.MustBreak() ||
|
2015-06-17 20:14:51 +00:00
|
|
|
m_ee->m_vpu1->MustBreak() ||
|
2012-09-12 01:03:53 +00: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
|
|
|
|
}
|
|
|
|
}
|
2015-06-09 07:31:19 +00:00
|
|
|
m_ee->m_executor.RemoveExceptionHandler();
|
2012-09-12 01:03:53 +00:00
|
|
|
}
|