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