1 #include <stdio.h>
2 #include <exception>
3 #include <memory>
4 #include <fenv.h>
5 #include "FpUtils.h"
6 #include "make_unique.h"
7 #include "string_format.h"
8 #include "PS2VM.h"
9 #include "PS2VM_Preferences.h"
10 #include "ee/PS2OS.h"
11 #include "ee/EeExecutor.h"
12 #include "Ps2Const.h"
13 #include "iop/Iop_SifManPs2.h"
14 #include "StdStream.h"
15 #include "StdStreamUtils.h"
16 #include "GZipStream.h"
17 #include "states/MemoryStateFile.h"
18 #include "zip/ZipArchiveWriter.h"
19 #include "zip/ZipArchiveReader.h"
20 #include "xml/Node.h"
21 #include "xml/Writer.h"
22 #include "xml/Parser.h"
23 #include "AppConfig.h"
24 #include "PathUtils.h"
25 #include "iop/IopBios.h"
26 #include "iop/ioman/HardDiskDevice.h"
27 #include "iop/ioman/OpticalMediaDevice.h"
28 #include "iop/ioman/PreferenceDirectoryDevice.h"
29 #include "Log.h"
30 #include "ISO9660/BlockProvider.h"
31 #include "DiskUtils.h"
32 
33 #define LOG_NAME ("ps2vm")
34 
35 #define PREF_PS2_ROM0_DIRECTORY_DEFAULT ("vfs/rom0")
36 #define PREF_PS2_HOST_DIRECTORY_DEFAULT ("vfs/host")
37 #define PREF_PS2_MC0_DIRECTORY_DEFAULT ("vfs/mc0")
38 #define PREF_PS2_MC1_DIRECTORY_DEFAULT ("vfs/mc1")
39 #define PREF_PS2_HDD_DIRECTORY_DEFAULT ("vfs/hdd")
40 
CPS2VM()41 CPS2VM::CPS2VM()
42     : m_nStatus(PAUSED)
43     , m_nEnd(false)
44     , m_pad(NULL)
45     , m_singleStepEe(false)
46     , m_singleStepIop(false)
47     , m_singleStepVu0(false)
48     , m_singleStepVu1(false)
49     , m_vblankTicks(0)
50     , m_inVblank(false)
51     , m_eeExecutionTicks(0)
52     , m_iopExecutionTicks(0)
53     , m_spuUpdateTicks(SPU_UPDATE_TICKS)
54     , m_eeProfilerZone(CProfiler::GetInstance().RegisterZone("EE"))
55     , m_iopProfilerZone(CProfiler::GetInstance().RegisterZone("IOP"))
56     , m_spuProfilerZone(CProfiler::GetInstance().RegisterZone("SPU"))
57     , m_gsSyncProfilerZone(CProfiler::GetInstance().RegisterZone("GSSYNC"))
58     , m_otherProfilerZone(CProfiler::GetInstance().RegisterZone("OTHER"))
59 {
60 	// clang-format off
61 	static const std::pair<const char*, const char*> basicDirectorySettings[] =
62 	{
63 		std::make_pair(PREF_PS2_ROM0_DIRECTORY, PREF_PS2_ROM0_DIRECTORY_DEFAULT),
64 		std::make_pair(PREF_PS2_HOST_DIRECTORY, PREF_PS2_HOST_DIRECTORY_DEFAULT),
65 		std::make_pair(PREF_PS2_MC0_DIRECTORY, PREF_PS2_MC0_DIRECTORY_DEFAULT),
66 		std::make_pair(PREF_PS2_MC1_DIRECTORY, PREF_PS2_MC1_DIRECTORY_DEFAULT),
67 		std::make_pair(PREF_PS2_HDD_DIRECTORY, PREF_PS2_HDD_DIRECTORY_DEFAULT),
68 	};
69 	// clang-format on
70 
71 	for(const auto& basicDirectorySetting : basicDirectorySettings)
72 	{
73 		auto setting = basicDirectorySetting.first;
74 		auto path = basicDirectorySetting.second;
75 
76 		auto absolutePath = CAppConfig::GetBasePath() / path;
77 		Framework::PathUtils::EnsurePathExists(absolutePath);
78 		CAppConfig::GetInstance().RegisterPreferencePath(setting, absolutePath);
79 
80 		auto currentPath = CAppConfig::GetInstance().GetPreferencePath(setting);
81 		if(!fs::exists(currentPath))
82 		{
83 			CAppConfig::GetInstance().SetPreferencePath(setting, absolutePath);
84 		}
85 	}
86 
87 	CAppConfig::GetInstance().RegisterPreferencePath(PREF_PS2_CDROM0_PATH, "");
88 
89 	Framework::PathUtils::EnsurePathExists(GetStateDirectoryPath());
90 
91 	m_iop = std::make_unique<Iop::CSubSystem>(true);
92 	auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
93 
94 	m_ee = std::make_unique<Ee::CSubSystem>(m_iop->m_ram, *iopOs);
95 	m_OnRequestLoadExecutableConnection = m_ee->m_os->OnRequestLoadExecutable.Connect(std::bind(&CPS2VM::ReloadExecutable, this, std::placeholders::_1, std::placeholders::_2));
96 	m_OnCrtModeChangeConnection = m_ee->m_os->OnCrtModeChange.Connect(std::bind(&CPS2VM::OnCrtModeChange, this));
97 
98 	CAppConfig::GetInstance().RegisterPreferenceBoolean(PREF_PS2_LIMIT_FRAMERATE, true);
99 	ReloadFrameRateLimit();
100 
101 	CAppConfig::GetInstance().RegisterPreferenceInteger(PREF_AUDIO_SPUBLOCKCOUNT, 100);
102 	m_spuBlockCount = CAppConfig::GetInstance().GetPreferenceInteger(PREF_AUDIO_SPUBLOCKCOUNT);
103 }
104 
105 //////////////////////////////////////////////////
106 //Various Message Functions
107 //////////////////////////////////////////////////
108 
CreateGSHandler(const CGSHandler::FactoryFunction & factoryFunction)109 void CPS2VM::CreateGSHandler(const CGSHandler::FactoryFunction& factoryFunction)
110 {
111 	m_mailBox.SendCall([this, factoryFunction]() { CreateGsHandlerImpl(factoryFunction); }, true);
112 }
113 
GetGSHandler()114 CGSHandler* CPS2VM::GetGSHandler()
115 {
116 	return m_ee->m_gs;
117 }
118 
DestroyGSHandler()119 void CPS2VM::DestroyGSHandler()
120 {
121 	if(m_ee->m_gs == nullptr) return;
122 	m_mailBox.SendCall([this]() { DestroyGsHandlerImpl(); }, true);
123 }
124 
CreatePadHandler(const CPadHandler::FactoryFunction & factoryFunction)125 void CPS2VM::CreatePadHandler(const CPadHandler::FactoryFunction& factoryFunction)
126 {
127 	if(m_pad != nullptr) return;
128 	m_mailBox.SendCall([this, factoryFunction]() { CreatePadHandlerImpl(factoryFunction); }, true);
129 }
130 
GetPadHandler()131 CPadHandler* CPS2VM::GetPadHandler()
132 {
133 	return m_pad;
134 }
135 
DestroyPadHandler()136 void CPS2VM::DestroyPadHandler()
137 {
138 	if(m_pad == nullptr) return;
139 	m_mailBox.SendCall([this]() { DestroyPadHandlerImpl(); }, true);
140 }
141 
CreateSoundHandler(const CSoundHandler::FactoryFunction & factoryFunction)142 void CPS2VM::CreateSoundHandler(const CSoundHandler::FactoryFunction& factoryFunction)
143 {
144 	if(m_soundHandler != nullptr) return;
145 	m_mailBox.SendCall([this, factoryFunction]() { CreateSoundHandlerImpl(factoryFunction); }, true);
146 }
147 
ReloadSpuBlockCount()148 void CPS2VM::ReloadSpuBlockCount()
149 {
150 	m_mailBox.SendCall(
151 	    [this]() {
152 		    m_currentSpuBlock = 0;
153 		    auto spuBlockCount = CAppConfig::GetInstance().GetPreferenceInteger(PREF_AUDIO_SPUBLOCKCOUNT);
154 		    assert(spuBlockCount <= BLOCK_COUNT);
155 		    m_spuBlockCount = spuBlockCount;
156 	    });
157 }
158 
DestroySoundHandler()159 void CPS2VM::DestroySoundHandler()
160 {
161 	if(m_soundHandler == nullptr) return;
162 	m_mailBox.SendCall([this]() { DestroySoundHandlerImpl(); }, true);
163 }
164 
ReloadFrameRateLimit()165 void CPS2VM::ReloadFrameRateLimit()
166 {
167 	uint32 frameRate = 60;
168 	if(m_ee->m_gs != nullptr)
169 	{
170 		frameRate = m_ee->m_gs->GetCrtFrameRate();
171 	}
172 	bool limitFrameRate = CAppConfig::GetInstance().GetPreferenceBoolean(PREF_PS2_LIMIT_FRAMERATE);
173 	m_frameLimiter.SetFrameRate(limitFrameRate ? frameRate : 0);
174 
175 	uint32 frameTicks = PS2::EE_CLOCK_FREQ / frameRate;
176 	m_onScreenTicksTotal = frameTicks * 9 / 10;
177 	m_vblankTicksTotal = frameTicks / 10;
178 }
179 
GetStatus() const180 CVirtualMachine::STATUS CPS2VM::GetStatus() const
181 {
182 	return m_nStatus;
183 }
184 
StepEe()185 void CPS2VM::StepEe()
186 {
187 	if(GetStatus() == RUNNING) return;
188 	m_singleStepEe = true;
189 	m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
190 }
191 
StepIop()192 void CPS2VM::StepIop()
193 {
194 	if(GetStatus() == RUNNING) return;
195 	m_singleStepIop = true;
196 	m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
197 }
198 
StepVu0()199 void CPS2VM::StepVu0()
200 {
201 	if(GetStatus() == RUNNING) return;
202 	m_singleStepVu0 = true;
203 	m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
204 }
205 
StepVu1()206 void CPS2VM::StepVu1()
207 {
208 	if(GetStatus() == RUNNING) return;
209 	m_singleStepVu1 = true;
210 	m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
211 }
212 
Resume()213 void CPS2VM::Resume()
214 {
215 	if(m_nStatus == RUNNING) return;
216 	m_mailBox.SendCall(std::bind(&CPS2VM::ResumeImpl, this), true);
217 	OnRunningStateChange();
218 }
219 
Pause()220 void CPS2VM::Pause()
221 {
222 	if(m_nStatus == PAUSED) return;
223 	m_mailBox.SendCall(std::bind(&CPS2VM::PauseImpl, this), true);
224 	OnMachineStateChange();
225 	OnRunningStateChange();
226 }
227 
PauseAsync()228 void CPS2VM::PauseAsync()
229 {
230 	if(m_nStatus == PAUSED) return;
231 	m_mailBox.SendCall([this]() {
232 		PauseImpl();
233 		OnMachineStateChange();
234 		OnRunningStateChange();
235 	});
236 }
237 
Reset()238 void CPS2VM::Reset()
239 {
240 	assert(m_nStatus == PAUSED);
241 	ResetVM();
242 }
243 
DumpEEIntcHandlers()244 void CPS2VM::DumpEEIntcHandlers()
245 {
246 	//	if(m_pOS == NULL) return;
247 	if(m_nStatus != PAUSED) return;
248 	m_ee->m_os->DumpIntcHandlers();
249 }
250 
DumpEEDmacHandlers()251 void CPS2VM::DumpEEDmacHandlers()
252 {
253 	//	if(m_pOS == NULL) return;
254 	if(m_nStatus != PAUSED) return;
255 	m_ee->m_os->DumpDmacHandlers();
256 }
257 
Initialize()258 void CPS2VM::Initialize()
259 {
260 	CreateVM();
261 	m_nEnd = false;
262 	m_thread = std::thread([&]() { EmuThread(); });
263 }
264 
Destroy()265 void CPS2VM::Destroy()
266 {
267 	m_mailBox.SendCall(std::bind(&CPS2VM::DestroyImpl, this));
268 	m_thread.join();
269 	DestroyVM();
270 }
271 
GetStateDirectoryPath()272 fs::path CPS2VM::GetStateDirectoryPath()
273 {
274 	return CAppConfig::GetBasePath() / fs::path("states/");
275 }
276 
GenerateStatePath(unsigned int slot) const277 fs::path CPS2VM::GenerateStatePath(unsigned int slot) const
278 {
279 	auto stateFileName = string_format("%s.st%d.zip", m_ee->m_os->GetExecutableName(), slot);
280 	return GetStateDirectoryPath() / fs::path(stateFileName);
281 }
282 
SaveState(const fs::path & statePath)283 std::future<bool> CPS2VM::SaveState(const fs::path& statePath)
284 {
285 	auto promise = std::make_shared<std::promise<bool>>();
286 	auto future = promise->get_future();
287 	m_mailBox.SendCall(
288 	    [this, promise, statePath]() {
289 		    auto result = SaveVMState(statePath);
290 		    promise->set_value(result);
291 	    });
292 	return future;
293 }
294 
LoadState(const fs::path & statePath)295 std::future<bool> CPS2VM::LoadState(const fs::path& statePath)
296 {
297 	auto promise = std::make_shared<std::promise<bool>>();
298 	auto future = promise->get_future();
299 	m_mailBox.SendCall(
300 	    [this, promise, statePath]() {
301 		    auto result = LoadVMState(statePath);
302 		    promise->set_value(result);
303 	    });
304 	return future;
305 }
306 
TriggerFrameDump(const FrameDumpCallback & frameDumpCallback)307 void CPS2VM::TriggerFrameDump(const FrameDumpCallback& frameDumpCallback)
308 {
309 	m_mailBox.SendCall(
310 	    [=]() {
311 		    std::unique_lock<std::mutex> frameDumpCallbackMutexLock(m_frameDumpCallbackMutex);
312 		    if(m_frameDumpCallback) return;
313 		    m_frameDumpCallback = frameDumpCallback;
314 	    },
315 	    false);
316 }
317 
GetCpuUtilisationInfo() const318 CPS2VM::CPU_UTILISATION_INFO CPS2VM::GetCpuUtilisationInfo() const
319 {
320 	return m_cpuUtilisation;
321 }
322 
323 #ifdef DEBUGGER_INCLUDED
324 
325 #define TAGS_SECTION_TAGS ("tags")
326 #define TAGS_SECTION_EE_FUNCTIONS ("ee_functions")
327 #define TAGS_SECTION_EE_COMMENTS ("ee_comments")
328 #define TAGS_SECTION_VU1_FUNCTIONS ("vu1_functions")
329 #define TAGS_SECTION_VU1_COMMENTS ("vu1_comments")
330 #define TAGS_SECTION_IOP ("iop")
331 #define TAGS_SECTION_IOP_FUNCTIONS ("functions")
332 #define TAGS_SECTION_IOP_COMMENTS ("comments")
333 
334 #define TAGS_PATH ("tags/")
335 
MakeDebugTagsPackagePath(const char * packageName)336 std::string CPS2VM::MakeDebugTagsPackagePath(const char* packageName)
337 {
338 	auto tagsPath = CAppConfig::GetBasePath() / fs::path(TAGS_PATH);
339 	Framework::PathUtils::EnsurePathExists(tagsPath);
340 	auto tagsPackagePath = tagsPath / (std::string(packageName) + std::string(".tags.xml"));
341 	return tagsPackagePath.string();
342 }
343 
LoadDebugTags(const char * packageName)344 void CPS2VM::LoadDebugTags(const char* packageName)
345 {
346 	try
347 	{
348 		std::string packagePath = MakeDebugTagsPackagePath(packageName);
349 		Framework::CStdStream stream(packagePath.c_str(), "rb");
350 		std::unique_ptr<Framework::Xml::CNode> document(Framework::Xml::CParser::ParseDocument(stream));
351 		Framework::Xml::CNode* tagsNode = document->Select(TAGS_SECTION_TAGS);
352 		if(!tagsNode) return;
353 		m_ee->m_EE.m_Functions.Unserialize(tagsNode, TAGS_SECTION_EE_FUNCTIONS);
354 		m_ee->m_EE.m_Comments.Unserialize(tagsNode, TAGS_SECTION_EE_COMMENTS);
355 		m_ee->m_VU1.m_Functions.Unserialize(tagsNode, TAGS_SECTION_VU1_FUNCTIONS);
356 		m_ee->m_VU1.m_Comments.Unserialize(tagsNode, TAGS_SECTION_VU1_COMMENTS);
357 		{
358 			Framework::Xml::CNode* sectionNode = tagsNode->Select(TAGS_SECTION_IOP);
359 			if(sectionNode)
360 			{
361 				m_iop->m_cpu.m_Functions.Unserialize(sectionNode, TAGS_SECTION_IOP_FUNCTIONS);
362 				m_iop->m_cpu.m_Comments.Unserialize(sectionNode, TAGS_SECTION_IOP_COMMENTS);
363 				m_iop->m_bios->LoadDebugTags(sectionNode);
364 			}
365 		}
366 	}
367 	catch(...)
368 	{
369 	}
370 }
371 
SaveDebugTags(const char * packageName)372 void CPS2VM::SaveDebugTags(const char* packageName)
373 {
374 	try
375 	{
376 		std::string packagePath = MakeDebugTagsPackagePath(packageName);
377 		Framework::CStdStream stream(packagePath.c_str(), "wb");
378 		std::unique_ptr<Framework::Xml::CNode> document(new Framework::Xml::CNode(TAGS_SECTION_TAGS, true));
379 		m_ee->m_EE.m_Functions.Serialize(document.get(), TAGS_SECTION_EE_FUNCTIONS);
380 		m_ee->m_EE.m_Comments.Serialize(document.get(), TAGS_SECTION_EE_COMMENTS);
381 		m_ee->m_VU1.m_Functions.Serialize(document.get(), TAGS_SECTION_VU1_FUNCTIONS);
382 		m_ee->m_VU1.m_Comments.Serialize(document.get(), TAGS_SECTION_VU1_COMMENTS);
383 		{
384 			Framework::Xml::CNode* iopNode = new Framework::Xml::CNode(TAGS_SECTION_IOP, true);
385 			m_iop->m_cpu.m_Functions.Serialize(iopNode, TAGS_SECTION_IOP_FUNCTIONS);
386 			m_iop->m_cpu.m_Comments.Serialize(iopNode, TAGS_SECTION_IOP_COMMENTS);
387 			m_iop->m_bios->SaveDebugTags(iopNode);
388 			document->InsertNode(iopNode);
389 		}
390 		Framework::Xml::CWriter::WriteDocument(stream, document.get());
391 	}
392 	catch(...)
393 	{
394 	}
395 }
396 
397 #endif
398 
399 //////////////////////////////////////////////////
400 //Non extern callable methods
401 //////////////////////////////////////////////////
402 
CreateVM()403 void CPS2VM::CreateVM()
404 {
405 	ResetVM();
406 }
407 
ResetVM()408 void CPS2VM::ResetVM()
409 {
410 	m_ee->Reset();
411 	m_iop->Reset();
412 
413 	//LoadBIOS();
414 
415 	if(m_ee->m_gs != NULL)
416 	{
417 		m_ee->m_gs->Reset();
418 	}
419 
420 	{
421 		auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
422 		assert(iopOs);
423 
424 		iopOs->Reset(std::make_shared<Iop::CSifManPs2>(m_ee->m_sif, m_ee->m_ram, m_iop->m_ram));
425 
426 		iopOs->GetIoman()->RegisterDevice("rom0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_ROM0_DIRECTORY));
427 		iopOs->GetIoman()->RegisterDevice("host", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_HOST_DIRECTORY));
428 		iopOs->GetIoman()->RegisterDevice("host0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_HOST_DIRECTORY));
429 		iopOs->GetIoman()->RegisterDevice("mc0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_MC0_DIRECTORY));
430 		iopOs->GetIoman()->RegisterDevice("mc1", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_MC1_DIRECTORY));
431 		iopOs->GetIoman()->RegisterDevice("cdrom", Iop::CIoman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
432 		iopOs->GetIoman()->RegisterDevice("cdrom0", Iop::CIoman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
433 		iopOs->GetIoman()->RegisterDevice("hdd0", std::make_shared<Iop::Ioman::CHardDiskDevice>());
434 
435 		iopOs->GetLoadcore()->SetLoadExecutableHandler(std::bind(&CPS2OS::LoadExecutable, m_ee->m_os, std::placeholders::_1, std::placeholders::_2));
436 	}
437 
438 	CDROM0_SyncPath();
439 
440 	m_vblankTicks = m_onScreenTicksTotal;
441 	m_inVblank = false;
442 
443 	m_eeExecutionTicks = 0;
444 	m_iopExecutionTicks = 0;
445 
446 	m_spuUpdateTicks = SPU_UPDATE_TICKS;
447 	m_currentSpuBlock = 0;
448 
449 	RegisterModulesInPadHandler();
450 }
451 
DestroyVM()452 void CPS2VM::DestroyVM()
453 {
454 	CDROM0_Reset();
455 }
456 
SaveVMState(const fs::path & statePath)457 bool CPS2VM::SaveVMState(const fs::path& statePath)
458 {
459 	if(m_ee->m_gs == NULL)
460 	{
461 		printf("PS2VM: GS Handler was not instancied. Cannot save state.\r\n");
462 		return false;
463 	}
464 
465 	try
466 	{
467 		auto stateStream = Framework::CreateOutputStdStream(statePath.native());
468 		Framework::CZipArchiveWriter archive;
469 
470 		m_ee->SaveState(archive);
471 		m_iop->SaveState(archive);
472 		m_ee->m_gs->SaveState(archive);
473 
474 		archive.Write(stateStream);
475 	}
476 	catch(...)
477 	{
478 		return false;
479 	}
480 
481 	return true;
482 }
483 
LoadVMState(const fs::path & statePath)484 bool CPS2VM::LoadVMState(const fs::path& statePath)
485 {
486 	if(m_ee->m_gs == NULL)
487 	{
488 		printf("PS2VM: GS Handler was not instancied. Cannot load state.\r\n");
489 		return false;
490 	}
491 
492 	try
493 	{
494 		auto stateStream = Framework::CreateInputStdStream(statePath.native());
495 		Framework::CZipArchiveReader archive(stateStream);
496 
497 		try
498 		{
499 			m_ee->LoadState(archive);
500 			m_iop->LoadState(archive);
501 			m_ee->m_gs->LoadState(archive);
502 		}
503 		catch(...)
504 		{
505 			//Any error that occurs in the previous block is critical
506 			PauseImpl();
507 			throw;
508 		}
509 	}
510 	catch(...)
511 	{
512 		return false;
513 	}
514 
515 	OnMachineStateChange();
516 
517 	return true;
518 }
519 
PauseImpl()520 void CPS2VM::PauseImpl()
521 {
522 	m_nStatus = PAUSED;
523 }
524 
ResumeImpl()525 void CPS2VM::ResumeImpl()
526 {
527 #ifdef DEBUGGER_INCLUDED
528 	m_ee->m_EE.m_executor->DisableBreakpointsOnce();
529 	m_iop->m_cpu.m_executor->DisableBreakpointsOnce();
530 	m_ee->m_VU1.m_executor->DisableBreakpointsOnce();
531 #endif
532 	m_nStatus = RUNNING;
533 }
534 
DestroyImpl()535 void CPS2VM::DestroyImpl()
536 {
537 	DestroyGsHandlerImpl();
538 	DestroyPadHandlerImpl();
539 	DestroySoundHandlerImpl();
540 	m_nEnd = true;
541 }
542 
CreateGsHandlerImpl(const CGSHandler::FactoryFunction & factoryFunction)543 void CPS2VM::CreateGsHandlerImpl(const CGSHandler::FactoryFunction& factoryFunction)
544 {
545 	auto gs = m_ee->m_gs;
546 	m_ee->m_gs = factoryFunction();
547 	m_ee->m_gs->SetIntc(&m_ee->m_intc);
548 	m_ee->m_gs->Initialize();
549 	m_ee->m_gs->SendGSCall([this]() {
550 		static_cast<CEeExecutor*>(m_ee->m_EE.m_executor.get())->AttachExceptionHandlerToThread();
551 	});
552 	if(gs)
553 	{
554 		m_ee->m_gs->Copy(gs);
555 		gs->Release();
556 		delete gs;
557 	}
558 	m_OnNewFrameConnection = m_ee->m_gs->OnNewFrame.Connect(std::bind(&CPS2VM::OnGsNewFrame, this));
559 }
560 
DestroyGsHandlerImpl()561 void CPS2VM::DestroyGsHandlerImpl()
562 {
563 	if(m_ee->m_gs == nullptr) return;
564 	m_ee->m_gs->Release();
565 	delete m_ee->m_gs;
566 	m_ee->m_gs = nullptr;
567 }
568 
CreatePadHandlerImpl(const CPadHandler::FactoryFunction & factoryFunction)569 void CPS2VM::CreatePadHandlerImpl(const CPadHandler::FactoryFunction& factoryFunction)
570 {
571 	m_pad = factoryFunction();
572 	RegisterModulesInPadHandler();
573 }
574 
DestroyPadHandlerImpl()575 void CPS2VM::DestroyPadHandlerImpl()
576 {
577 	if(m_pad == nullptr) return;
578 	delete m_pad;
579 	m_pad = nullptr;
580 }
581 
CreateSoundHandlerImpl(const CSoundHandler::FactoryFunction & factoryFunction)582 void CPS2VM::CreateSoundHandlerImpl(const CSoundHandler::FactoryFunction& factoryFunction)
583 {
584 	m_soundHandler = factoryFunction();
585 }
586 
GetSoundHandler()587 CSoundHandler* CPS2VM::GetSoundHandler()
588 {
589 	return m_soundHandler;
590 }
591 
DestroySoundHandlerImpl()592 void CPS2VM::DestroySoundHandlerImpl()
593 {
594 	if(m_soundHandler == nullptr) return;
595 	delete m_soundHandler;
596 	m_soundHandler = nullptr;
597 }
598 
OnGsNewFrame()599 void CPS2VM::OnGsNewFrame()
600 {
601 #ifdef DEBUGGER_INCLUDED
602 	std::unique_lock<std::mutex> dumpFrameCallbackMutexLock(m_frameDumpCallbackMutex);
603 	if(m_dumpingFrame && !m_frameDump.GetPackets().empty())
604 	{
605 		m_ee->m_gs->SetFrameDump(nullptr);
606 		m_frameDumpCallback(m_frameDump);
607 		m_dumpingFrame = false;
608 		m_frameDumpCallback = FrameDumpCallback();
609 	}
610 	else if(m_frameDumpCallback)
611 	{
612 		m_frameDump.Reset();
613 		memcpy(m_frameDump.GetInitialGsRam(), m_ee->m_gs->GetRam(), CGSHandler::RAMSIZE);
614 		memcpy(m_frameDump.GetInitialGsRegisters(), m_ee->m_gs->GetRegisters(), CGSHandler::REGISTER_MAX * sizeof(uint64));
615 		m_frameDump.SetInitialSMODE2(m_ee->m_gs->GetSMODE2());
616 		m_ee->m_gs->SetFrameDump(&m_frameDump);
617 		m_dumpingFrame = true;
618 	}
619 #endif
620 }
621 
UpdateEe()622 void CPS2VM::UpdateEe()
623 {
624 #ifdef PROFILE
625 	CProfilerZone profilerZone(m_eeProfilerZone);
626 #endif
627 
628 	while(m_eeExecutionTicks > 0)
629 	{
630 		int executed = m_ee->ExecuteCpu(m_singleStepEe ? 1 : m_eeExecutionTicks);
631 		if(m_ee->IsCpuIdle())
632 		{
633 #ifdef PROFILE
634 			m_cpuUtilisation.eeIdleTicks += (m_eeExecutionTicks - executed);
635 #endif
636 			executed = m_eeExecutionTicks;
637 		}
638 #ifdef PROFILE
639 		m_cpuUtilisation.eeTotalTicks += executed;
640 #endif
641 
642 		m_ee->m_vpu0->Execute(m_singleStepVu0 ? 1 : executed);
643 		m_ee->m_vpu1->Execute(m_singleStepVu1 ? 1 : executed);
644 
645 		m_eeExecutionTicks -= executed;
646 		m_ee->CountTicks(executed);
647 		m_vblankTicks -= executed;
648 
649 #ifdef DEBUGGER_INCLUDED
650 		if(m_singleStepEe) break;
651 		if(m_ee->m_EE.m_executor->MustBreak()) break;
652 #endif
653 	}
654 }
655 
UpdateIop()656 void CPS2VM::UpdateIop()
657 {
658 #ifdef PROFILE
659 	CProfilerZone profilerZone(m_iopProfilerZone);
660 #endif
661 
662 	while(m_iopExecutionTicks > 0)
663 	{
664 		int executed = m_iop->ExecuteCpu(m_singleStepIop ? 1 : m_iopExecutionTicks);
665 		if(m_iop->IsCpuIdle())
666 		{
667 #ifdef PROFILE
668 			m_cpuUtilisation.iopIdleTicks += (m_iopExecutionTicks - executed);
669 #endif
670 			executed = m_iopExecutionTicks;
671 		}
672 #ifdef PROFILE
673 		m_cpuUtilisation.iopTotalTicks += executed;
674 #endif
675 
676 		m_iopExecutionTicks -= executed;
677 		m_spuUpdateTicks -= executed;
678 		m_iop->CountTicks(executed);
679 
680 #ifdef DEBUGGER_INCLUDED
681 		if(m_singleStepIop) break;
682 		if(m_iop->m_cpu.m_executor->MustBreak()) break;
683 #endif
684 	}
685 }
686 
UpdateSpu()687 void CPS2VM::UpdateSpu()
688 {
689 #ifdef PROFILE
690 	CProfilerZone profilerZone(m_spuProfilerZone);
691 #endif
692 
693 	unsigned int blockOffset = (BLOCK_SIZE * m_currentSpuBlock);
694 	int16* samplesSpu0 = m_samples + blockOffset;
695 
696 	m_iop->m_spuCore0.Render(samplesSpu0, BLOCK_SIZE, DST_SAMPLE_RATE);
697 
698 	if(m_iop->m_spuCore1.IsEnabled())
699 	{
700 		int16 samplesSpu1[BLOCK_SIZE];
701 		m_iop->m_spuCore1.Render(samplesSpu1, BLOCK_SIZE, DST_SAMPLE_RATE);
702 
703 		for(unsigned int i = 0; i < BLOCK_SIZE; i++)
704 		{
705 			int32 resultSample = static_cast<int32>(samplesSpu0[i]) + static_cast<int32>(samplesSpu1[i]);
706 			resultSample = std::max<int32>(resultSample, SHRT_MIN);
707 			resultSample = std::min<int32>(resultSample, SHRT_MAX);
708 			samplesSpu0[i] = static_cast<int16>(resultSample);
709 		}
710 	}
711 
712 	m_currentSpuBlock++;
713 	if(m_currentSpuBlock == m_spuBlockCount)
714 	{
715 		if(m_soundHandler)
716 		{
717 			m_soundHandler->RecycleBuffers();
718 			m_soundHandler->Write(m_samples, BLOCK_SIZE * m_spuBlockCount, DST_SAMPLE_RATE);
719 		}
720 		m_currentSpuBlock = 0;
721 	}
722 }
723 
CDROM0_SyncPath()724 void CPS2VM::CDROM0_SyncPath()
725 {
726 	//TODO: Check if there's an m_cdrom0 already
727 	//TODO: Check if files are linked to this m_cdrom0 too and do something with them
728 
729 	CDROM0_Reset();
730 
731 	auto path = CAppConfig::GetInstance().GetPreferencePath(PREF_PS2_CDROM0_PATH);
732 	if(!path.empty())
733 	{
734 		try
735 		{
736 			m_cdrom0 = DiskUtils::CreateOpticalMediaFromPath(path);
737 			SetIopOpticalMedia(m_cdrom0.get());
738 		}
739 		catch(const std::exception& Exception)
740 		{
741 			printf("PS2VM: Error mounting cdrom0 device: %s\r\n", Exception.what());
742 		}
743 	}
744 }
745 
CDROM0_Reset()746 void CPS2VM::CDROM0_Reset()
747 {
748 	SetIopOpticalMedia(nullptr);
749 	m_cdrom0.reset();
750 }
751 
SetIopOpticalMedia(COpticalMedia * opticalMedia)752 void CPS2VM::SetIopOpticalMedia(COpticalMedia* opticalMedia)
753 {
754 	auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
755 	assert(iopOs);
756 
757 	iopOs->GetCdvdfsv()->SetOpticalMedia(opticalMedia);
758 	iopOs->GetCdvdman()->SetOpticalMedia(opticalMedia);
759 }
760 
RegisterModulesInPadHandler()761 void CPS2VM::RegisterModulesInPadHandler()
762 {
763 	if(m_pad == nullptr) return;
764 
765 	auto iopOs = dynamic_cast<CIopBios*>(m_iop->m_bios.get());
766 	assert(iopOs);
767 
768 	m_pad->RemoveAllListeners();
769 	m_pad->InsertListener(iopOs->GetPadman());
770 	m_pad->InsertListener(&m_iop->m_sio2);
771 }
772 
ReloadExecutable(const char * executablePath,const CPS2OS::ArgumentList & arguments)773 void CPS2VM::ReloadExecutable(const char* executablePath, const CPS2OS::ArgumentList& arguments)
774 {
775 	ResetVM();
776 	m_ee->m_os->BootFromVirtualPath(executablePath, arguments);
777 }
778 
OnCrtModeChange()779 void CPS2VM::OnCrtModeChange()
780 {
781 	ReloadFrameRateLimit();
782 }
783 
EmuThread()784 void CPS2VM::EmuThread()
785 {
786 	fesetround(FE_TOWARDZERO);
787 	FpUtils::SetDenormalHandlingMode();
788 	CProfiler::GetInstance().SetWorkThread();
789 #ifdef PROFILE
790 	CProfilerZone profilerZone(m_otherProfilerZone);
791 #endif
792 	static_cast<CEeExecutor*>(m_ee->m_EE.m_executor.get())->AddExceptionHandler();
793 	m_frameLimiter.BeginFrame();
794 	while(1)
795 	{
796 		while(m_mailBox.IsPending())
797 		{
798 			m_mailBox.ReceiveCall();
799 		}
800 		if(m_nEnd) break;
801 		if(m_nStatus == PAUSED)
802 		{
803 			std::this_thread::sleep_for(std::chrono::milliseconds(100));
804 		}
805 		if(m_nStatus == RUNNING)
806 		{
807 			if(m_spuUpdateTicks <= 0)
808 			{
809 				UpdateSpu();
810 				m_spuUpdateTicks += SPU_UPDATE_TICKS;
811 			}
812 
813 			//EE execution
814 			{
815 				//Check vblank stuff
816 				if(m_vblankTicks <= 0)
817 				{
818 					m_inVblank = !m_inVblank;
819 					if(m_inVblank)
820 					{
821 						m_vblankTicks += m_vblankTicksTotal;
822 						m_ee->NotifyVBlankStart();
823 						m_iop->NotifyVBlankStart();
824 
825 						if(m_ee->m_gs != NULL)
826 						{
827 #ifdef PROFILE
828 							CProfilerZone profilerZone(m_gsSyncProfilerZone);
829 #endif
830 							m_ee->m_gs->SetVBlank();
831 						}
832 
833 						if(m_pad != NULL)
834 						{
835 							m_pad->Update(m_ee->m_ram);
836 						}
837 #ifdef PROFILE
838 						{
839 							CProfiler::GetInstance().CountCurrentZone();
840 							auto stats = CProfiler::GetInstance().GetStats();
841 							ProfileFrameDone(stats);
842 							CProfiler::GetInstance().Reset();
843 						}
844 
845 						m_cpuUtilisation = CPU_UTILISATION_INFO();
846 #endif
847 					}
848 					else
849 					{
850 						m_vblankTicks += m_onScreenTicksTotal;
851 						m_ee->NotifyVBlankEnd();
852 						m_iop->NotifyVBlankEnd();
853 						if(m_ee->m_gs != NULL)
854 						{
855 							m_ee->m_gs->ResetVBlank();
856 						}
857 						m_frameLimiter.EndFrame();
858 						m_frameLimiter.BeginFrame();
859 					}
860 				}
861 
862 				//EE CPU is 8 times faster than the IOP CPU
863 				static const int tickStep = 4800;
864 				m_eeExecutionTicks += tickStep;
865 				m_iopExecutionTicks += tickStep / 8;
866 
867 				UpdateEe();
868 				UpdateIop();
869 			}
870 #ifdef DEBUGGER_INCLUDED
871 			if(
872 			    m_ee->m_EE.m_executor->MustBreak() ||
873 			    m_iop->m_cpu.m_executor->MustBreak() ||
874 			    m_ee->m_VU1.m_executor->MustBreak() ||
875 			    m_singleStepEe || m_singleStepIop || m_singleStepVu0 || m_singleStepVu1)
876 			{
877 				m_nStatus = PAUSED;
878 				m_singleStepEe = false;
879 				m_singleStepIop = false;
880 				m_singleStepVu0 = false;
881 				m_singleStepVu1 = false;
882 				OnRunningStateChange();
883 				OnMachineStateChange();
884 			}
885 #endif
886 		}
887 	}
888 	static_cast<CEeExecutor*>(m_ee->m_EE.m_executor.get())->RemoveExceptionHandler();
889 }
890