1 #include "stdafx.h"
2 #include <random>
3 #include <thread>
4 #include "Console.h"
5 #include "CPU.h"
6 #include "PPU.h"
7 #include "APU.h"
8 #include "MemoryManager.h"
9 #include "AutoSaveManager.h"
10 #include "BaseMapper.h"
11 #include "ControlManager.h"
12 #include "VsControlManager.h"
13 #include "MapperFactory.h"
14 #include "Debugger.h"
15 #include "MessageManager.h"
16 #include "EmulationSettings.h"
17 #include "../Utilities/sha1.h"
18 #include "../Utilities/Timer.h"
19 #include "../Utilities/FolderUtilities.h"
20 #include "../Utilities/PlatformUtilities.h"
21 #include "VirtualFile.h"
22 #include "HdBuilderPpu.h"
23 #include "HdPpu.h"
24 #include "NsfPpu.h"
25 #include "SoundMixer.h"
26 #include "NsfMapper.h"
27 #include "MovieManager.h"
28 #include "RewindManager.h"
29 #include "SaveStateManager.h"
30 #include "HdPackBuilder.h"
31 #include "HdAudioDevice.h"
32 #include "FDS.h"
33 #include "SystemActionManager.h"
34 #include "FdsSystemActionManager.h"
35 #include "VsSystemActionManager.h"
36 #include "FamilyBasicDataRecorder.h"
37 #include "IBarcodeReader.h"
38 #include "IBattery.h"
39 #include "KeyManager.h"
40 #include "BatteryManager.h"
41 #include "DebugHud.h"
42 #include "RomLoader.h"
43 #include "CheatManager.h"
44 #include "VideoDecoder.h"
45 #include "VideoRenderer.h"
46 #include "DebugHud.h"
47 #include "NotificationManager.h"
48 #include "HistoryViewer.h"
49 #include "ConsolePauseHelper.h"
50 #include "PgoUtilities.h"
51 
Console(shared_ptr<Console> master,EmulationSettings * initialSettings)52 Console::Console(shared_ptr<Console> master, EmulationSettings* initialSettings)
53 {
54 	_master = master;
55 
56 	if(_master) {
57 		//Slave console should use the same settings as the master
58 		_settings = _master->_settings;
59 	} else {
60 		if(initialSettings) {
61 			_settings.reset(new EmulationSettings(*initialSettings));
62 		} else {
63 			_settings.reset(new EmulationSettings());
64 		}
65 		KeyManager::SetSettings(_settings.get());
66 	}
67 
68 	_pauseCounter = 0;
69 	_model = NesModel::NTSC;
70 }
71 
~Console()72 Console::~Console()
73 {
74 	MovieManager::Stop();
75 }
76 
Init()77 void Console::Init()
78 {
79 	_notificationManager.reset(new NotificationManager());
80 	_batteryManager.reset(new BatteryManager());
81 
82 	_videoRenderer.reset(new VideoRenderer(shared_from_this()));
83 	_videoDecoder.reset(new VideoDecoder(shared_from_this()));
84 
85 	_saveStateManager.reset(new SaveStateManager(shared_from_this()));
86 	_cheatManager.reset(new CheatManager(shared_from_this()));
87 	_debugHud.reset(new DebugHud());
88 
89 	_soundMixer.reset(new SoundMixer(shared_from_this()));
90 	_soundMixer->SetNesModel(_model);
91 
92 	if(_master) {
93 		_emulationThreadId = _master->_emulationThreadId;
94 	}
95 }
96 
Release(bool forShutdown)97 void Console::Release(bool forShutdown)
98 {
99 	if(_slave) {
100 		_slave->Release(true);
101 		_slave.reset();
102 	}
103 
104 	if(forShutdown) {
105 		_videoDecoder->StopThread();
106 		_videoRenderer->StopThread();
107 
108 		_videoDecoder.reset();
109 		_videoRenderer.reset();
110 
111 		_debugHud.reset();
112 		_saveStateManager.reset();
113 		_cheatManager.reset();
114 
115 		_soundMixer.reset();
116 		_notificationManager.reset();
117 	}
118 
119 	if(_master) {
120 		_master->_notificationManager->SendNotification(ConsoleNotificationType::VsDualSystemStopped);
121 	}
122 
123 	_rewindManager.reset();
124 	_autoSaveManager.reset();
125 
126 	_hdPackBuilder.reset();
127 	_hdData.reset();
128 	_hdAudioDevice.reset();
129 
130 	_systemActionManager.reset();
131 
132 	_master.reset();
133 	_cpu.reset();
134 	_ppu.reset();
135 	_apu.reset();
136 	_debugger.reset();
137 	_mapper.reset();
138 	_memoryManager.reset();
139 	_controlManager.reset();
140 }
141 
GetBatteryManager()142 shared_ptr<BatteryManager> Console::GetBatteryManager()
143 {
144 	return _batteryManager;
145 }
146 
GetSaveStateManager()147 shared_ptr<SaveStateManager> Console::GetSaveStateManager()
148 {
149 	return _saveStateManager;
150 }
151 
GetVideoDecoder()152 shared_ptr<VideoDecoder> Console::GetVideoDecoder()
153 {
154 	return _videoDecoder;
155 }
156 
GetVideoRenderer()157 shared_ptr<VideoRenderer> Console::GetVideoRenderer()
158 {
159 	return _videoRenderer;
160 }
161 
GetDebugHud()162 shared_ptr<DebugHud> Console::GetDebugHud()
163 {
164 	return _debugHud;
165 }
166 
SaveBatteries()167 void Console::SaveBatteries()
168 {
169 	shared_ptr<BaseMapper> mapper = _mapper;
170 	shared_ptr<ControlManager> controlManager = _controlManager;
171 
172 	if(mapper) {
173 		mapper->SaveBattery();
174 	}
175 
176 	if(controlManager) {
177 		shared_ptr<IBattery> device = std::dynamic_pointer_cast<IBattery>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort));
178 		if(device) {
179 			device->SaveBattery();
180 		}
181 	}
182 }
183 
LoadMatchingRom(string romName,HashInfo hashInfo)184 bool Console::LoadMatchingRom(string romName, HashInfo hashInfo)
185 {
186 	if(_initialized) {
187 		string currentRomFilepath = GetRomPath().GetFilePath();
188 		if(!currentRomFilepath.empty()) {
189 			HashInfo gameHashInfo = GetRomInfo().Hash;
190 			if(gameHashInfo.Crc32 == hashInfo.Crc32 || gameHashInfo.Sha1.compare(hashInfo.Sha1) == 0 || gameHashInfo.PrgChrMd5.compare(hashInfo.PrgChrMd5) == 0) {
191 				//Current game matches, power cycle game and return
192 				PowerCycle();
193 				return true;
194 			}
195 		}
196 	}
197 
198 	string match = FindMatchingRom(romName, hashInfo);
199 	if(!match.empty()) {
200 		return Initialize(match);
201 	}
202 	return false;
203 }
204 
FindMatchingRom(string romName,HashInfo hashInfo)205 string Console::FindMatchingRom(string romName, HashInfo hashInfo)
206 {
207 	if(_initialized) {
208 		VirtualFile currentRom = GetRomPath();
209 		if(currentRom.IsValid() && !GetPatchFile().IsValid()) {
210 			HashInfo gameHashInfo = GetRomInfo().Hash;
211 			if(gameHashInfo.Crc32 == hashInfo.Crc32 || gameHashInfo.Sha1.compare(hashInfo.Sha1) == 0 || gameHashInfo.PrgChrMd5.compare(hashInfo.PrgChrMd5) == 0) {
212 				//Current game matches
213 				return currentRom;
214 			}
215 		}
216 	}
217 
218 	string lcRomname = romName;
219 	std::transform(lcRomname.begin(), lcRomname.end(), lcRomname.begin(), ::tolower);
220 	std::unordered_set<string> validExtensions = { { ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe", "*.7z", "*.zip" } };
221 	vector<string> romFiles;
222 	for(string folder : FolderUtilities::GetKnownGameFolders()) {
223 		vector<string> files = FolderUtilities::GetFilesInFolder(folder, validExtensions, true);
224 		romFiles.insert(romFiles.end(), files.begin(), files.end());
225 	}
226 
227 	if(!romName.empty()) {
228 		//Perform quick search based on file name
229 		string match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, true);
230 		if(!match.empty()) {
231 			return match;
232 		}
233 	}
234 
235 	//Perform slow CRC32 search for ROM
236 	string match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, false);
237 	if(!match.empty()) {
238 		return match;
239 	}
240 
241 	return "";
242 }
243 
Initialize(string romFile,string patchFile)244 bool Console::Initialize(string romFile, string patchFile)
245 {
246 	VirtualFile rom = romFile;
247 	VirtualFile patch = patchFile;
248 	return Initialize(rom, patch);
249 }
250 
Initialize(VirtualFile & romFile)251 bool Console::Initialize(VirtualFile &romFile)
252 {
253 	VirtualFile patchFile;
254 	return Initialize(romFile, patchFile);
255 }
256 
Initialize(VirtualFile & romFile,VirtualFile & patchFile)257 bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
258 {
259 	Pause();
260 	if(!_romFilepath.empty() && _mapper) {
261 		//Ensure we save any battery file before loading a new game
262 		SaveBatteries();
263 	}
264 
265 	if(romFile.IsValid()) {
266 		_videoDecoder->StopThread();
267 
268 		shared_ptr<HdPackData> originalHdPackData = _hdData;
269 		LoadHdPack(romFile, patchFile);
270 		if(patchFile.IsValid()) {
271 			if(romFile.ApplyPatch(patchFile)) {
272 				MessageManager::DisplayMessage("Patch", "ApplyingPatch", patchFile.GetFileName());
273 			} else {
274 				//Patch failed
275 			}
276 		}
277 		vector<uint8_t> fileData;
278 		romFile.ReadFile(fileData);
279 
280 		_batteryManager->Initialize(FolderUtilities::GetFilename(romFile.GetFileName(), false));
281 
282 		RomData romData;
283 		shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(shared_from_this(), romFile.GetFileName(), fileData, romData);
284 		if(mapper) {
285 			_soundMixer->StopAudio(true);
286 
287 			bool isDifferentGame = _romFilepath != (string)romFile || _patchFilename != (string)patchFile;
288 			if(_mapper) {
289 				if(isDifferentGame) {
290 					//Save current game state before loading another one
291 					_saveStateManager->SaveRecentGame(GetRomInfo().RomName, _romFilepath, _patchFilename);
292 				}
293 
294 				//Send notification only if a game was already running and we successfully loaded the new one
295 				_notificationManager->SendNotification(ConsoleNotificationType::GameStopped, (void*)1);
296 			}
297 
298 			if(isDifferentGame) {
299 				_romFilepath = romFile;
300 				_patchFilename = patchFile;
301 
302 				//Changed game, stop all recordings
303 				MovieManager::Stop();
304 				_soundMixer->StopRecording();
305 				StopRecordingHdPack();
306 			}
307 
308 			_mapper = mapper;
309 			_memoryManager.reset(new MemoryManager(shared_from_this()));
310 			_cpu.reset(new CPU(shared_from_this()));
311 			_apu.reset(new APU(shared_from_this()));
312 
313 			_mapper->SetConsole(shared_from_this());
314 			_mapper->Initialize(romData);
315 			GetNotificationManager()->RegisterNotificationListener(_mapper);
316 
317 			if(_slave) {
318 				_slave->Release(false);
319 				_slave.reset();
320 			}
321 
322 			RomInfo romInfo = _mapper->GetRomInfo();
323 			if(!_master && romInfo.VsType == VsSystemType::VsDualSystem) {
324 				_slave.reset(new Console(shared_from_this()));
325 				_slave->Init();
326 				_slave->Initialize(romFile, patchFile);
327 			}
328 
329 			switch(romInfo.System) {
330 				case GameSystem::FDS:
331 					_settings->SetPpuModel(PpuModel::Ppu2C02);
332 					_systemActionManager.reset(new FdsSystemActionManager(shared_from_this(), _mapper));
333 					break;
334 
335 				case GameSystem::VsSystem:
336 					_settings->SetPpuModel(romInfo.VsPpuModel);
337 					_systemActionManager.reset(new VsSystemActionManager(shared_from_this()));
338 					break;
339 
340 				default:
341 					_settings->SetPpuModel(PpuModel::Ppu2C02);
342 					_systemActionManager.reset(new SystemActionManager(shared_from_this())); break;
343 			}
344 
345 			//Temporarely disable battery saves to prevent battery files from being created for the wrong game (for Battle Box & Turbo File)
346 			_batteryManager->SetSaveEnabled(false);
347 
348 			uint32_t pollCounter = 0;
349 			if(_controlManager && !isDifferentGame) {
350 				//When power cycling, poll counter must be preserved to allow movies to playback properly
351 				pollCounter = _controlManager->GetPollCounter();
352 			}
353 
354 			if(romInfo.System == GameSystem::VsSystem) {
355 				_controlManager.reset(new VsControlManager(shared_from_this(), _systemActionManager, _mapper->GetMapperControlDevice()));
356 			} else {
357 				_controlManager.reset(new ControlManager(shared_from_this(), _systemActionManager, _mapper->GetMapperControlDevice()));
358 			}
359 			_controlManager->SetPollCounter(pollCounter);
360 			_controlManager->UpdateControlDevices();
361 
362 			//Re-enable battery saves
363 			_batteryManager->SetSaveEnabled(true);
364 
365 			if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
366 				_ppu.reset(new HdPpu(shared_from_this(), _hdData.get()));
367 			} else if(std::dynamic_pointer_cast<NsfMapper>(_mapper)) {
368 				//Disable most of the PPU for NSFs
369 				_ppu.reset(new NsfPpu(shared_from_this()));
370 			} else {
371 				_ppu.reset(new PPU(shared_from_this()));
372 			}
373 
374 			_memoryManager->SetMapper(_mapper);
375 			_memoryManager->RegisterIODevice(_ppu.get());
376 			_memoryManager->RegisterIODevice(_apu.get());
377 			_memoryManager->RegisterIODevice(_controlManager.get());
378 			_memoryManager->RegisterIODevice(_mapper.get());
379 
380 			if(_hdData && (!_hdData->BgmFilesById.empty() || !_hdData->SfxFilesById.empty())) {
381 				_hdAudioDevice.reset(new HdAudioDevice(shared_from_this(), _hdData.get()));
382 				_memoryManager->RegisterIODevice(_hdAudioDevice.get());
383 			} else {
384 				_hdAudioDevice.reset();
385 			}
386 
387 			_model = NesModel::Auto;
388 			UpdateNesModel(false);
389 
390 			_initialized = true;
391 
392 			if(_debugger) {
393 				//Reset debugger if it was running before
394 				auto lock = _debuggerLock.AcquireSafe();
395 				StopDebugger();
396 				GetDebugger();
397 			}
398 
399 
400 			ResetComponents(false);
401 
402 			//Reset components before creating rewindmanager, otherwise the first save state it takes will be invalid
403 			_rewindManager.reset(new RewindManager(shared_from_this()));
404 			_notificationManager->RegisterNotificationListener(_rewindManager);
405 
406 			//Poll controller input after creating rewind manager, to make sure it catches the first frame's input
407 			_controlManager->UpdateInputState();
408 
409 #ifndef LIBRETRO
410 			//Don't use auto-save manager for libretro
411 			//Only enable auto-save for the master console (VS Dualsystem)
412 			if(IsMaster()) {
413 				_autoSaveManager.reset(new AutoSaveManager(shared_from_this()));
414 			}
415 #endif
416 			_videoDecoder->StartThread();
417 
418 			FolderUtilities::AddKnownGameFolder(romFile.GetFolderPath());
419 
420 			if(IsMaster()) {
421 				string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC");
422 				string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
423 				MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomName, false));
424 				if(_settings->GetOverclockRate() != 100) {
425 					MessageManager::DisplayMessage("ClockRate", std::to_string(_settings->GetOverclockRate()) + "%");
426 				}
427 				_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
428 
429 				if(_slave) {
430 					_notificationManager->SendNotification(ConsoleNotificationType::VsDualSystemStarted);
431 				}
432 			}
433 			Resume();
434 			return true;
435 		} else {
436 			_hdData = originalHdPackData;
437 		}
438 	}
439 
440 	//Reset battery source to current game if new game failed to load
441 	_batteryManager->Initialize(FolderUtilities::GetFilename(GetRomInfo().RomName, false));
442 	if(_mapper) {
443 		_videoDecoder->StartThread();
444 	}
445 
446 	MessageManager::DisplayMessage("Error", "CouldNotLoadFile", romFile.GetFileName());
447 	Resume();
448 	return false;
449 }
450 
ProcessCpuClock()451 void Console::ProcessCpuClock()
452 {
453 	_mapper->ProcessCpuClock();
454 	_ppu->ProcessCpuClock();
455 	_apu->ProcessCpuClock();
456 }
457 
GetCpu()458 CPU* Console::GetCpu()
459 {
460 	return _cpu.get();
461 }
462 
GetPpu()463 PPU* Console::GetPpu()
464 {
465 	return _ppu.get();
466 }
467 
GetApu()468 APU* Console::GetApu()
469 {
470 	return _apu.get();
471 }
472 
GetSoundMixer()473 shared_ptr<SoundMixer> Console::GetSoundMixer()
474 {
475 	return _soundMixer;
476 }
477 
GetNotificationManager()478 shared_ptr<NotificationManager> Console::GetNotificationManager()
479 {
480 	return _notificationManager;
481 }
482 
GetSettings()483 EmulationSettings* Console::GetSettings()
484 {
485 	return _settings.get();
486 }
487 
IsDualSystem()488 bool Console::IsDualSystem()
489 {
490 	return _slave != nullptr || _master != nullptr;
491 }
492 
GetDualConsole()493 shared_ptr<Console> Console::GetDualConsole()
494 {
495 	//When called from the master, returns the slave.
496 	//When called from the slave, returns the master.
497 	//Returns a null pointer when not running a dualsystem game
498 	return _slave ? _slave : _master;
499 }
500 
IsMaster()501 bool Console::IsMaster()
502 {
503 	return !_master;
504 }
505 
GetMapper()506 BaseMapper* Console::GetMapper()
507 {
508 	return _mapper.get();
509 }
510 
GetControlManager()511 ControlManager* Console::GetControlManager()
512 {
513 	return _controlManager.get();
514 }
515 
GetMemoryManager()516 MemoryManager* Console::GetMemoryManager()
517 {
518 	return _memoryManager.get();
519 }
520 
GetCheatManager()521 CheatManager* Console::GetCheatManager()
522 {
523 	return _cheatManager.get();
524 }
525 
GetRewindManager()526 shared_ptr<RewindManager> Console::GetRewindManager()
527 {
528 	return _rewindManager;
529 }
530 
GetHistoryViewer()531 HistoryViewer* Console::GetHistoryViewer()
532 {
533 	return _historyViewer.get();
534 }
535 
GetRomPath()536 VirtualFile Console::GetRomPath()
537 {
538 	return static_cast<VirtualFile>(_romFilepath);
539 }
540 
GetPatchFile()541 VirtualFile Console::GetPatchFile()
542 {
543 	return (VirtualFile)_patchFilename;
544 }
545 
GetRomInfo()546 RomInfo Console::GetRomInfo()
547 {
548 	return _mapper ? _mapper->GetRomInfo() : (RomInfo {});
549 }
550 
GetFrameCount()551 uint32_t Console::GetFrameCount()
552 {
553 	return _ppu ? _ppu->GetFrameCount() : 0;
554 }
555 
GetModel()556 NesModel Console::GetModel()
557 {
558 	return _model;
559 }
560 
GetSystemActionManager()561 shared_ptr<SystemActionManager> Console::GetSystemActionManager()
562 {
563 	return _systemActionManager;
564 }
565 
PowerCycle()566 void Console::PowerCycle()
567 {
568 	if(_initialized && !_romFilepath.empty()) {
569 		VirtualFile romFile = _romFilepath;
570 		VirtualFile patchFile = _patchFilename;
571 		Initialize(romFile, patchFile);
572 	}
573 }
574 
Reset(bool softReset)575 void Console::Reset(bool softReset)
576 {
577 	if(_initialized) {
578 		bool needSuspend = softReset ? _systemActionManager->Reset() : _systemActionManager->PowerCycle();
579 
580 		if(needSuspend) {
581 			//Only do this if a reset/power cycle is not already pending - otherwise we'll end up calling Suspend() too many times
582 			//Resume from code break if needed (otherwise reset doesn't happen right away)
583 			shared_ptr<Debugger> debugger = _debugger;
584 			if(debugger) {
585 				debugger->Suspend();
586 				debugger->Run();
587 			}
588 		}
589 	}
590 }
591 
ResetComponents(bool softReset)592 void Console::ResetComponents(bool softReset)
593 {
594 	if(_slave) {
595 		//Always reset/power cycle the slave alongside the master CPU
596 		_slave->ResetComponents(softReset);
597 	}
598 
599 	_soundMixer->StopAudio(true);
600 	_debugHud->ClearScreen();
601 
602 	_memoryManager->Reset(softReset);
603 	if(!_settings->CheckFlag(EmulationFlags::DisablePpuReset) || !softReset || IsNsf()) {
604 		_ppu->Reset();
605 	}
606 	_apu->Reset(softReset);
607 	_cpu->Reset(softReset, _model);
608 	_controlManager->Reset(softReset);
609 
610 	_resetRunTimers = true;
611 
612 	KeyManager::UpdateDevices();
613 
614 	//This notification MUST be sent before the UpdateInputState() below to allow MovieRecorder to grab the first frame's worth of inputs
615 	_notificationManager->SendNotification(softReset ? ConsoleNotificationType::GameReset : ConsoleNotificationType::GameLoaded);
616 
617 	if(softReset) {
618 		shared_ptr<Debugger> debugger = _debugger;
619 		if(debugger) {
620 			debugger->ResetCounters();
621 			debugger->ProcessEvent(EventType::Reset);
622 			debugger->Resume();
623 		}
624 	}
625 }
626 
Stop(int stopCode)627 void Console::Stop(int stopCode)
628 {
629 	_stop = true;
630 	_stopCode = stopCode;
631 
632 	shared_ptr<Debugger> debugger = _debugger;
633 	if(debugger) {
634 		debugger->Suspend();
635 	}
636 	_stopLock.Acquire();
637 	_stopLock.Release();
638 }
639 
Pause()640 void Console::Pause()
641 {
642 	shared_ptr<Debugger> debugger = _debugger;
643 	if(debugger) {
644 		//Make sure debugger resumes if we try to pause the emu, otherwise we will get deadlocked.
645 		debugger->Suspend();
646 	}
647 
648 	if(_master) {
649 		//When trying to pause/resume the slave, we need to pause/resume the master instead
650 		_master->Pause();
651 	} else {
652 		_pauseCounter++;
653 		_runLock.Acquire();
654 	}
655 }
656 
Resume()657 void Console::Resume()
658 {
659 	if(_master) {
660 		//When trying to pause/resume the slave, we need to pause/resume the master instead
661 		_master->Resume();
662 	} else {
663 		_runLock.Release();
664 		_pauseCounter--;
665 	}
666 
667 	shared_ptr<Debugger> debugger = _debugger;
668 	if(debugger) {
669 		//Make sure debugger resumes if we try to pause the emu, otherwise we will get deadlocked.
670 		debugger->Resume();
671 	}
672 }
673 
RunSingleFrame()674 void Console::RunSingleFrame()
675 {
676 	//Used by Libretro
677 	uint32_t lastFrameNumber = _ppu->GetFrameCount();
678 	_emulationThreadId = std::this_thread::get_id();
679 	UpdateNesModel(true);
680 
681 	while(_ppu->GetFrameCount() == lastFrameNumber) {
682 		_cpu->Exec();
683 		if(_slave) {
684 			RunSlaveCpu();
685 		}
686 	}
687 
688 	_settings->DisableOverclocking(_disableOcNextFrame || IsNsf());
689 	_disableOcNextFrame = false;
690 
691 	_systemActionManager->ProcessSystemActions();
692 	_apu->EndFrame();
693 }
694 
RunSlaveCpu()695 void Console::RunSlaveCpu()
696 {
697 	int64_t cycleGap;
698 	while(true) {
699 		//Run the slave until it catches up to the master CPU (and take into account the CPU count overflow that occurs every ~20mins)
700 		cycleGap = (int64_t)(_cpu->GetCycleCount() - _slave->_cpu->GetCycleCount());
701 		if(cycleGap > 5 || _ppu->GetFrameCount() > _slave->_ppu->GetFrameCount()) {
702 			_slave->_cpu->Exec();
703 		} else {
704 			break;
705 		}
706 	}
707 }
708 
Run()709 void Console::Run()
710 {
711 	Timer clockTimer;
712 	Timer lastFrameTimer;
713 	double frameDurations[60] = {};
714 	uint32_t frameDurationIndex = 0;
715 	double targetTime;
716 	double lastFrameMin = 9999;
717 	double lastFrameMax = 0;
718 	uint32_t lastFrameNumber = -1;
719 	double lastDelay = GetFrameDelay();
720 
721 	_runLock.Acquire();
722 	_stopLock.Acquire();
723 
724 	_emulationThreadId = std::this_thread::get_id();
725 	if(_slave) {
726 		_slave->_emulationThreadId = std::this_thread::get_id();
727 	}
728 
729 	targetTime = lastDelay;
730 
731 	_videoDecoder->StartThread();
732 
733 	PlatformUtilities::DisableScreensaver();
734 
735 	UpdateNesModel(true);
736 
737 	_running = true;
738 
739 	bool crashed = false;
740 	try {
741 		while(true) {
742 			_cpu->Exec();
743 
744 			if(_slave) {
745 				RunSlaveCpu();
746 			}
747 
748 			if(_ppu->GetFrameCount() != lastFrameNumber) {
749 				_soundMixer->ProcessEndOfFrame();
750 				if(_slave) {
751 					_slave->_soundMixer->ProcessEndOfFrame();
752 				}
753 
754 				if(_historyViewer) {
755 					_historyViewer->ProcessEndOfFrame();
756 				}
757 				_rewindManager->ProcessEndOfFrame();
758 				_settings->DisableOverclocking(_disableOcNextFrame || IsNsf());
759 				_disableOcNextFrame = false;
760 
761 				//Update model (ntsc/pal) and get delay for next frame
762 				UpdateNesModel(true);
763 				double delay = GetFrameDelay();
764 
765 				if(_resetRunTimers || delay != lastDelay || (clockTimer.GetElapsedMS() - targetTime) > 300) {
766 					//Reset the timers, this can happen in 3 scenarios:
767 					//1) Target frame rate changed
768 					//2) The console was reset/power cycled or the emulation was paused (with or without the debugger)
769 					//3) As a satefy net, if we overshoot our target by over 300 milliseconds, the timer is reset, too.
770 					//   This can happen when something slows the emulator down severely (or when breaking execution in VS when debugging Mesen itself, etc.)
771 					clockTimer.Reset();
772 					targetTime = 0;
773 
774 					_resetRunTimers = false;
775 					lastDelay = delay;
776 				}
777 
778 				targetTime += delay;
779 
780 				bool displayDebugInfo = _settings->CheckFlag(EmulationFlags::DisplayDebugInfo);
781 				if(displayDebugInfo) {
782 					double lastFrameTime = lastFrameTimer.GetElapsedMS();
783 					lastFrameTimer.Reset();
784 					frameDurations[frameDurationIndex] = lastFrameTime;
785 					frameDurationIndex = (frameDurationIndex + 1) % 60;
786 
787 					DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
788 					if(_slave) {
789 						_slave->DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
790 					}
791 				}
792 
793 				//Sleep until we're ready to start the next frame
794 				clockTimer.WaitUntil(targetTime);
795 
796 				if(_pauseCounter > 0) {
797 					//Need to temporarely pause the emu (to save/load a state, etc.)
798 					_runLock.Release();
799 
800 					//Spin wait until we are allowed to start again
801 					while(_pauseCounter > 0) { }
802 
803 					_runLock.Acquire();
804 				}
805 
806 				if(_pauseOnNextFrameRequested) {
807 					//Used by "Run Single Frame" option
808 					_settings->SetFlags(EmulationFlags::Paused);
809 					_pauseOnNextFrameRequested = false;
810 				}
811 
812 				bool pausedRequired = _settings->NeedsPause();
813 				if(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
814 					_notificationManager->SendNotification(ConsoleNotificationType::GamePaused);
815 
816 					//Prevent audio from looping endlessly while game is paused
817 					_soundMixer->StopAudio();
818 					if(_slave) {
819 						_slave->_soundMixer->StopAudio();
820 					}
821 
822 					_runLock.Release();
823 
824 					PlatformUtilities::EnableScreensaver();
825 					PlatformUtilities::RestoreTimerResolution();
826 					while(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
827 						//Sleep until emulation is resumed
828 						std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(30));
829 						pausedRequired = _settings->NeedsPause();
830 						_paused = true;
831 					}
832 					_paused = false;
833 
834 					PlatformUtilities::DisableScreensaver();
835 					_runLock.Acquire();
836 					_notificationManager->SendNotification(ConsoleNotificationType::GameResumed);
837 					lastFrameTimer.Reset();
838 
839 					//Reset the timer to avoid speed up after a pause
840 					_resetRunTimers = true;
841 				}
842 
843 				if(_settings->CheckFlag(EmulationFlags::UseHighResolutionTimer)) {
844 					PlatformUtilities::EnableHighResolutionTimer();
845 				} else {
846 					PlatformUtilities::RestoreTimerResolution();
847 				}
848 
849 				_systemActionManager->ProcessSystemActions();
850 
851 				lastFrameNumber = _ppu->GetFrameCount();
852 
853 				if(_stop) {
854 					_stop = false;
855 					break;
856 				}
857 			}
858 		}
859 	} catch(const std::runtime_error &ex) {
860 		crashed = true;
861 		_stopCode = -1;
862 		MessageManager::DisplayMessage("Error", "GameCrash", ex.what());
863 	}
864 
865 	_paused = false;
866 	_running = false;
867 
868 	_notificationManager->SendNotification(ConsoleNotificationType::BeforeEmulationStop);
869 
870 	if(!crashed) {
871 		_saveStateManager->SaveRecentGame(GetRomInfo().RomName, _romFilepath, _patchFilename);
872 	}
873 
874 	_videoDecoder->StopThread();
875 	StopRecordingHdPack();
876 
877 	_soundMixer->StopAudio();
878 	MovieManager::Stop();
879 	_soundMixer->StopRecording();
880 
881 	PlatformUtilities::EnableScreensaver();
882 	PlatformUtilities::RestoreTimerResolution();
883 
884 	_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
885 
886 	_initialized = false;
887 
888 	if(!_romFilepath.empty() && _mapper) {
889 		//Ensure we save any battery file before unloading anything
890 		SaveBatteries();
891 	}
892 
893 	_romFilepath = "";
894 
895 	Release(false);
896 
897 	_stopLock.Release();
898 	_runLock.Release();
899 
900 	_emulationThreadId = std::thread::id();
901 
902 	_notificationManager->SendNotification(ConsoleNotificationType::GameStopped);
903 	_notificationManager->SendNotification(ConsoleNotificationType::EmulationStopped);
904 }
905 
ResetRunTimers()906 void Console::ResetRunTimers()
907 {
908 	_resetRunTimers = true;
909 }
910 
IsRunning()911 bool Console::IsRunning()
912 {
913 	if(_master) {
914 		//For slave CPU, return the master's state
915 		return _master->IsRunning();
916 	} else {
917 		return !_stopLock.IsFree() && _running;
918 	}
919 }
920 
IsExecutionStopped()921 bool Console::IsExecutionStopped()
922 {
923 	if(_master) {
924 		//For slave CPU, return the master's state
925 		return _master->IsPaused();
926 	} else {
927 		return _runLock.IsFree() || (!_runLock.IsFree() && _pauseCounter > 0) || !_running;
928 	}
929 }
930 
IsPaused()931 bool Console::IsPaused()
932 {
933 	if(_master) {
934 		return _master->_paused;
935 	} else {
936 		return _paused;
937 	}
938 }
939 
PauseOnNextFrame()940 void Console::PauseOnNextFrame()
941 {
942 	_pauseOnNextFrameRequested = true;
943 }
944 
UpdateNesModel(bool sendNotification)945 void Console::UpdateNesModel(bool sendNotification)
946 {
947 	bool configChanged = false;
948 	if(_settings->NeedControllerUpdate()) {
949 		_controlManager->UpdateControlDevices();
950 		configChanged = true;
951 	}
952 
953 	NesModel model = _settings->GetNesModel();
954 	if(model == NesModel::Auto) {
955 		switch(_mapper->GetRomInfo().System) {
956 			case GameSystem::NesPal: model = NesModel::PAL; break;
957 			case GameSystem::Dendy: model = NesModel::Dendy; break;
958 			default: model = NesModel::NTSC; break;
959 		}
960 	}
961 	if(_model != model) {
962 		_model = model;
963 		configChanged = true;
964 
965 		if(sendNotification) {
966 			MessageManager::DisplayMessage("Region", model == NesModel::PAL ? "PAL" : (model == NesModel::Dendy ? "Dendy" : "NTSC"));
967 		}
968 	}
969 
970 	_mapper->SetNesModel(model);
971 	_ppu->SetNesModel(model);
972 	_apu->SetNesModel(model);
973 
974 	if(configChanged && sendNotification) {
975 		_notificationManager->SendNotification(ConsoleNotificationType::ConfigChanged);
976 	}
977 }
978 
GetFrameDelay()979 double Console::GetFrameDelay()
980 {
981 	uint32_t emulationSpeed = _settings->GetEmulationSpeed();
982 	double frameDelay;
983 	if(emulationSpeed == 0) {
984 		frameDelay = 0;
985 	} else {
986 		//60.1fps (NTSC), 50.01fps (PAL/Dendy)
987 		switch(_model) {
988 			default:
989 			case NesModel::NTSC: frameDelay = _settings->CheckFlag(EmulationFlags::IntegerFpsMode) ? 16.6666666666666666667 : 16.63926405550947; break;
990 			case NesModel::PAL:
991 			case NesModel::Dendy: frameDelay = _settings->CheckFlag(EmulationFlags::IntegerFpsMode) ? 20 : 19.99720920217466; break;
992 		}
993 		frameDelay /= (double)emulationSpeed / 100.0;
994 	}
995 
996 	return frameDelay;
997 }
998 
SaveState(ostream & saveStream)999 void Console::SaveState(ostream &saveStream)
1000 {
1001 	if(_initialized) {
1002 		_cpu->SaveSnapshot(&saveStream);
1003 		_ppu->SaveSnapshot(&saveStream);
1004 		_memoryManager->SaveSnapshot(&saveStream);
1005 		_apu->SaveSnapshot(&saveStream);
1006 		_controlManager->SaveSnapshot(&saveStream);
1007 		_mapper->SaveSnapshot(&saveStream);
1008 		if(_hdAudioDevice) {
1009 			_hdAudioDevice->SaveSnapshot(&saveStream);
1010 		} else {
1011 			Snapshotable::WriteEmptyBlock(&saveStream);
1012 		}
1013 
1014 		if(_slave) {
1015 			//For VS Dualsystem, append the 2nd console's savestate
1016 			_slave->SaveState(saveStream);
1017 		}
1018 	}
1019 }
1020 
LoadState(istream & loadStream)1021 void Console::LoadState(istream &loadStream)
1022 {
1023 	LoadState(loadStream, SaveStateManager::FileFormatVersion);
1024 }
1025 
LoadState(istream & loadStream,uint32_t stateVersion)1026 void Console::LoadState(istream &loadStream, uint32_t stateVersion)
1027 {
1028 	if(_initialized) {
1029 		_cpu->LoadSnapshot(&loadStream, stateVersion);
1030 		_ppu->LoadSnapshot(&loadStream, stateVersion);
1031 		_memoryManager->LoadSnapshot(&loadStream, stateVersion);
1032 		_apu->LoadSnapshot(&loadStream, stateVersion);
1033 		_controlManager->LoadSnapshot(&loadStream, stateVersion);
1034 		_mapper->LoadSnapshot(&loadStream, stateVersion);
1035 		if(_hdAudioDevice) {
1036 			_hdAudioDevice->LoadSnapshot(&loadStream, stateVersion);
1037 		} else {
1038 			Snapshotable::SkipBlock(&loadStream);
1039 		}
1040 
1041 		if(_slave) {
1042 			//For VS Dualsystem, the slave console's savestate is appended to the end of the file
1043 			_slave->LoadState(loadStream, stateVersion);
1044 		}
1045 
1046 		shared_ptr<Debugger> debugger = _debugger;
1047 		if(debugger) {
1048 			debugger->ResetCounters();
1049 		}
1050 
1051 		_debugHud->ClearScreen();
1052 		_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
1053 		UpdateNesModel(false);
1054 	}
1055 }
1056 
LoadState(uint8_t * buffer,uint32_t bufferSize)1057 void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
1058 {
1059 	//Send any unprocessed sound to the SoundMixer - needed for rewind
1060 	_apu->EndFrame();
1061 
1062 	stringstream stream;
1063 	stream.write((char*)buffer, bufferSize);
1064 	stream.seekg(0, ios::beg);
1065 	LoadState(stream);
1066 }
1067 
BreakIfDebugging()1068 void Console::BreakIfDebugging()
1069 {
1070 	shared_ptr<Debugger> debugger = _debugger;
1071 	if(debugger) {
1072 		debugger->BreakImmediately(BreakSource::BreakOnCpuCrash);
1073 	} else if(_settings->CheckFlag(EmulationFlags::BreakOnCrash)) {
1074 		//When "Break on Crash" is enabled, open the debugger and break immediately if a crash occurs
1075 		debugger = GetDebugger(true);
1076 		debugger->BreakImmediately(BreakSource::BreakOnCpuCrash);
1077 	}
1078 }
1079 
GetDebugger(bool autoStart)1080 std::shared_ptr<Debugger> Console::GetDebugger(bool autoStart)
1081 {
1082 	shared_ptr<Debugger> debugger = _debugger;
1083 	if(!debugger && autoStart) {
1084 		//Lock to make sure we don't try to start debuggers in 2 separate threads at once
1085 		auto lock = _debuggerLock.AcquireSafe();
1086 		debugger = _debugger;
1087 		if(!debugger) {
1088 			debugger.reset(new Debugger(shared_from_this(), _cpu, _ppu, _apu, _memoryManager, _mapper));
1089 			_debugger = debugger;
1090 		}
1091 	}
1092 	return debugger;
1093 }
1094 
StopDebugger()1095 void Console::StopDebugger()
1096 {
1097 	if(_debugger) {
1098 		_debugger->ReleaseDebugger(_running);
1099 	}
1100 	_debugger.reset();
1101 }
1102 
GetEmulationThreadId()1103 std::thread::id Console::GetEmulationThreadId()
1104 {
1105 	return _emulationThreadId;
1106 }
1107 
GetLagCounter()1108 uint32_t Console::GetLagCounter()
1109 {
1110 	return _controlManager->GetLagCounter();
1111 }
1112 
ResetLagCounter()1113 void Console::ResetLagCounter()
1114 {
1115 	Pause();
1116 	_controlManager->ResetLagCounter();
1117 	Resume();
1118 }
1119 
IsDebuggerAttached()1120 bool Console::IsDebuggerAttached()
1121 {
1122 	return (bool)_debugger;
1123 }
1124 
SetNextFrameOverclockStatus(bool disabled)1125 void Console::SetNextFrameOverclockStatus(bool disabled)
1126 {
1127 	_disableOcNextFrame = disabled;
1128 }
1129 
GetStopCode()1130 int32_t Console::GetStopCode()
1131 {
1132 	return _stopCode;
1133 }
1134 
InitializeRam(void * data,uint32_t length)1135 void Console::InitializeRam(void* data, uint32_t length)
1136 {
1137 	InitializeRam(_settings->GetRamPowerOnState(), data, length);
1138 }
1139 
InitializeRam(RamPowerOnState powerOnState,void * data,uint32_t length)1140 void Console::InitializeRam(RamPowerOnState powerOnState, void* data, uint32_t length)
1141 {
1142 	switch(powerOnState) {
1143 		default:
1144 		case RamPowerOnState::AllZeros: memset(data, 0, length); break;
1145 		case RamPowerOnState::AllOnes: memset(data, 0xFF, length); break;
1146 		case RamPowerOnState::Random:
1147 			std::random_device rd;
1148 			std::mt19937 mt(rd());
1149 			std::uniform_int_distribution<> dist(0, 255);
1150 			for(uint32_t i = 0; i < length; i++) {
1151 				((uint8_t*)data)[i] = dist(mt);
1152 			}
1153 			break;
1154 	}
1155 }
1156 
GetHdData()1157 shared_ptr<HdPackData> Console::GetHdData()
1158 {
1159 	return _hdData;
1160 }
1161 
IsHdPpu()1162 bool Console::IsHdPpu()
1163 {
1164 	return _hdData && std::dynamic_pointer_cast<HdPpu>(_ppu) != nullptr;
1165 }
1166 
LoadHdPack(VirtualFile & romFile,VirtualFile & patchFile)1167 void Console::LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile)
1168 {
1169 	_hdData.reset();
1170 	if(_settings->CheckFlag(EmulationFlags::UseHdPacks)) {
1171 		_hdData.reset(new HdPackData());
1172 		if(!HdPackLoader::LoadHdNesPack(romFile, *_hdData.get())) {
1173 			_hdData.reset();
1174 		} else {
1175 			auto result = _hdData->PatchesByHash.find(romFile.GetSha1Hash());
1176 			if(result != _hdData->PatchesByHash.end()) {
1177 				patchFile = result->second;
1178 			}
1179 		}
1180 	}
1181 }
1182 
StartRecordingHdPack(string saveFolder,ScaleFilterType filterType,uint32_t scale,uint32_t flags,uint32_t chrRamBankSize)1183 void Console::StartRecordingHdPack(string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize)
1184 {
1185 	ConsolePauseHelper helper(this);
1186 
1187 	std::stringstream saveState;
1188 	SaveState(saveState);
1189 
1190 	_hdPackBuilder.reset();
1191 	_hdPackBuilder.reset(new HdPackBuilder(shared_from_this(), saveFolder, filterType, scale, flags, chrRamBankSize, !_mapper->HasChrRom()));
1192 
1193 	_memoryManager->UnregisterIODevice(_ppu.get());
1194 	_ppu.reset();
1195 	_ppu.reset(new HdBuilderPpu(shared_from_this(), _hdPackBuilder.get(), chrRamBankSize, _hdData));
1196 	_memoryManager->RegisterIODevice(_ppu.get());
1197 
1198 	shared_ptr<Debugger> debugger = _debugger;
1199 	if(debugger) {
1200 		debugger->SetPpu(_ppu);
1201 	}
1202 
1203 	LoadState(saveState);
1204 
1205 	_soundMixer->StopAudio();
1206 }
1207 
StopRecordingHdPack()1208 void Console::StopRecordingHdPack()
1209 {
1210 	if(_hdPackBuilder) {
1211 		ConsolePauseHelper helper(this);
1212 
1213 		std::stringstream saveState;
1214 		SaveState(saveState);
1215 
1216 		_memoryManager->UnregisterIODevice(_ppu.get());
1217 		_ppu.reset();
1218 		if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
1219 			_ppu.reset(new HdPpu(shared_from_this(), _hdData.get()));
1220 		} else {
1221 			_ppu.reset(new PPU(shared_from_this()));
1222 		}
1223 		_memoryManager->RegisterIODevice(_ppu.get());
1224 		_hdPackBuilder.reset();
1225 
1226 		shared_ptr<Debugger> debugger = _debugger;
1227 		if(debugger) {
1228 			debugger->SetPpu(_ppu);
1229 		}
1230 
1231 		LoadState(saveState);
1232 
1233 		_soundMixer->StopAudio();
1234 	}
1235 }
1236 
UpdateHdPackMode()1237 bool Console::UpdateHdPackMode()
1238 {
1239 	//Switch back and forth between HD PPU and regular PPU as needed
1240 	Pause();
1241 
1242 	VirtualFile romFile = _romFilepath;
1243 	VirtualFile patchFile = _patchFilename;
1244 	LoadHdPack(romFile, patchFile);
1245 
1246 	bool isHdPackLoaded = std::dynamic_pointer_cast<HdPpu>(_ppu) != nullptr;
1247 	bool hdPackFound = _hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty());
1248 
1249 	bool modeChanged = false;
1250 	if(isHdPackLoaded != hdPackFound) {
1251 		std::stringstream saveState;
1252 		SaveState(saveState);
1253 
1254 		_memoryManager->UnregisterIODevice(_ppu.get());
1255 		_ppu.reset();
1256 		if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
1257 			_ppu.reset(new HdPpu(shared_from_this(), _hdData.get()));
1258 		} else if(std::dynamic_pointer_cast<NsfMapper>(_mapper)) {
1259 			//Disable most of the PPU for NSFs
1260 			_ppu.reset(new NsfPpu(shared_from_this()));
1261 		} else {
1262 			_ppu.reset(new PPU(shared_from_this()));
1263 		}
1264 		_memoryManager->RegisterIODevice(_ppu.get());
1265 
1266 		LoadState(saveState);
1267 		modeChanged = true;
1268 	}
1269 
1270 	Resume();
1271 
1272 	return modeChanged;
1273 }
1274 
GetDipSwitchCount()1275 uint32_t Console::GetDipSwitchCount()
1276 {
1277 	shared_ptr<ControlManager> controlManager = _controlManager;
1278 	shared_ptr<BaseMapper> mapper = _mapper;
1279 
1280 	if(std::dynamic_pointer_cast<VsControlManager>(controlManager)) {
1281 		return IsDualSystem() ? 16 : 8;
1282 	} else if(mapper) {
1283 		return mapper->GetMapperDipSwitchCount();
1284 	}
1285 
1286 	return 0;
1287 }
1288 
GetAvailableFeatures()1289 ConsoleFeatures Console::GetAvailableFeatures()
1290 {
1291 	ConsoleFeatures features = ConsoleFeatures::None;
1292 	shared_ptr<BaseMapper> mapper = _mapper;
1293 	shared_ptr<ControlManager> controlManager = _controlManager;
1294 	if(mapper && controlManager) {
1295 		features = (ConsoleFeatures)((int)features | (int)mapper->GetAvailableFeatures());
1296 
1297 		if(dynamic_cast<VsControlManager*>(controlManager.get())) {
1298 			features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::VsSystem);
1299 		}
1300 
1301 		if(std::dynamic_pointer_cast<IBarcodeReader>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort))) {
1302 			features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::BarcodeReader);
1303 		}
1304 
1305 		if(std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2))) {
1306 			features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::TapeRecorder);
1307 		}
1308 	}
1309 	return features;
1310 }
1311 
InputBarcode(uint64_t barcode,uint32_t digitCount)1312 void Console::InputBarcode(uint64_t barcode, uint32_t digitCount)
1313 {
1314 	shared_ptr<BaseMapper> mapper = _mapper;
1315 	shared_ptr<ControlManager> controlManager = _controlManager;
1316 
1317 	if(mapper) {
1318 		shared_ptr<IBarcodeReader> barcodeReader = std::dynamic_pointer_cast<IBarcodeReader>(mapper->GetMapperControlDevice());
1319 		if(barcodeReader) {
1320 			barcodeReader->InputBarcode(barcode, digitCount);
1321 		}
1322 	}
1323 
1324 	if(controlManager) {
1325 		shared_ptr<IBarcodeReader> barcodeReader = std::dynamic_pointer_cast<IBarcodeReader>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort));
1326 		if(barcodeReader) {
1327 			barcodeReader->InputBarcode(barcode, digitCount);
1328 		}
1329 	}
1330 }
1331 
LoadTapeFile(string filepath)1332 void Console::LoadTapeFile(string filepath)
1333 {
1334 	shared_ptr<ControlManager> controlManager = _controlManager;
1335 	if(controlManager) {
1336 		shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
1337 		if(dataRecorder) {
1338 			Pause();
1339 			dataRecorder->LoadFromFile(filepath);
1340 			Resume();
1341 		}
1342 	}
1343 }
1344 
StartRecordingTapeFile(string filepath)1345 void Console::StartRecordingTapeFile(string filepath)
1346 {
1347 	shared_ptr<ControlManager> controlManager = _controlManager;
1348 	if(controlManager) {
1349 		shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
1350 		if(dataRecorder) {
1351 			Pause();
1352 			dataRecorder->StartRecording(filepath);
1353 			Resume();
1354 		}
1355 	}
1356 }
1357 
StopRecordingTapeFile()1358 void Console::StopRecordingTapeFile()
1359 {
1360 	shared_ptr<ControlManager> controlManager = _controlManager;
1361 	if(controlManager) {
1362 		shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
1363 		if(dataRecorder) {
1364 			Pause();
1365 			dataRecorder->StopRecording();
1366 			Resume();
1367 		}
1368 	}
1369 }
1370 
IsRecordingTapeFile()1371 bool Console::IsRecordingTapeFile()
1372 {
1373 	shared_ptr<ControlManager> controlManager = _controlManager;
1374 	if(controlManager) {
1375 		shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
1376 		if(dataRecorder) {
1377 			return dataRecorder->IsRecording();
1378 		}
1379 	}
1380 
1381 	return false;
1382 }
1383 
IsNsf()1384 bool Console::IsNsf()
1385 {
1386 	return std::dynamic_pointer_cast<NsfMapper>(_mapper) != nullptr;
1387 }
1388 
CopyRewindData(shared_ptr<Console> sourceConsole)1389 void Console::CopyRewindData(shared_ptr<Console> sourceConsole)
1390 {
1391 	sourceConsole->Pause();
1392 	Pause();
1393 
1394 	//Disable battery saving for this instance
1395 	_batteryManager->SetSaveEnabled(false);
1396 	_historyViewer.reset(new HistoryViewer(shared_from_this()));
1397 	sourceConsole->_rewindManager->CopyHistory(_historyViewer);
1398 
1399 	Resume();
1400 	sourceConsole->Resume();
1401 }
1402 
GetRamBuffer(DebugMemoryType memoryType,uint32_t & size,int32_t & startAddr)1403 uint8_t* Console::GetRamBuffer(DebugMemoryType memoryType, uint32_t &size, int32_t &startAddr)
1404 {
1405 	//Only used by libretro port for achievements - should not be used by anything else.
1406 	switch(memoryType) {
1407 		default: break;
1408 
1409 		case DebugMemoryType::InternalRam:
1410 			size = MemoryManager::InternalRAMSize;
1411 			startAddr = 0;
1412 			return _memoryManager->GetInternalRAM();
1413 
1414 		case DebugMemoryType::SaveRam:
1415 			size = _mapper->GetMemorySize(DebugMemoryType::SaveRam);
1416 			startAddr = _mapper->FromAbsoluteAddress(0, AddressType::SaveRam);
1417 			return _mapper->GetSaveRam();
1418 
1419 		case DebugMemoryType::WorkRam:
1420 			size = _mapper->GetMemorySize(DebugMemoryType::WorkRam);
1421 			startAddr = _mapper->FromAbsoluteAddress(0, AddressType::WorkRam);
1422 			return _mapper->GetWorkRam();
1423 	}
1424 
1425 	throw std::runtime_error("unsupported memory type");
1426 }
1427 
DebugAddTrace(const char * log)1428 void Console::DebugAddTrace(const char * log)
1429 {
1430 #ifndef LIBRETRO
1431 	if(_debugger) {
1432 		_debugger->AddTrace(log);
1433 	}
1434 #endif
1435 }
1436 
DebugProcessPpuCycle()1437 void Console::DebugProcessPpuCycle()
1438 {
1439 #ifndef LIBRETRO
1440 	if(_debugger && _debugger->IsPpuCycleToProcess()) {
1441 		_debugger->ProcessPpuCycle();
1442 	}
1443 #endif
1444 }
1445 
DebugProcessEvent(EventType type)1446 void Console::DebugProcessEvent(EventType type)
1447 {
1448 #ifndef LIBRETRO
1449 	if(_debugger) {
1450 		_debugger->ProcessEvent(type);
1451 	}
1452 #endif
1453 }
1454 
DebugProcessInterrupt(uint16_t cpuAddr,uint16_t destCpuAddr,bool forNmi)1455 void Console::DebugProcessInterrupt(uint16_t cpuAddr, uint16_t destCpuAddr, bool forNmi)
1456 {
1457 #ifndef LIBRETRO
1458 	if(_debugger) {
1459 		_debugger->ProcessInterrupt(cpuAddr, destCpuAddr, forNmi);
1460 	}
1461 #endif
1462 }
1463 
DebugSetLastFramePpuScroll(uint16_t addr,uint8_t xScroll,bool updateHorizontalScrollOnly)1464 void Console::DebugSetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool updateHorizontalScrollOnly)
1465 {
1466 #ifndef LIBRETRO
1467 	if(_debugger) {
1468 		_debugger->SetLastFramePpuScroll(addr, xScroll, updateHorizontalScrollOnly);
1469 	}
1470 #endif
1471 }
1472 
DebugProcessRamOperation(MemoryOperationType type,uint16_t & addr,uint8_t & value)1473 bool Console::DebugProcessRamOperation(MemoryOperationType type, uint16_t & addr, uint8_t & value)
1474 {
1475 #ifndef LIBRETRO
1476 	if(_debugger) {
1477 		return _debugger->ProcessRamOperation(type, addr, value);
1478 	}
1479 	return true;
1480 #else
1481 	return true;
1482 #endif
1483 }
1484 
DebugProcessVramReadOperation(MemoryOperationType type,uint16_t addr,uint8_t & value)1485 void Console::DebugProcessVramReadOperation(MemoryOperationType type, uint16_t addr, uint8_t & value)
1486 {
1487 #ifndef LIBRETRO
1488 	if(_debugger) {
1489 		_debugger->ProcessVramReadOperation(type, addr, value);
1490 	}
1491 #endif
1492 }
1493 
DebugProcessVramWriteOperation(uint16_t addr,uint8_t & value)1494 void Console::DebugProcessVramWriteOperation(uint16_t addr, uint8_t & value)
1495 {
1496 #ifndef LIBRETRO
1497 	if(_debugger) {
1498 		_debugger->ProcessVramWriteOperation(addr, value);
1499 	}
1500 #endif
1501 }
1502 
DisplayDebugInformation(double lastFrame,double & lastFrameMin,double & lastFrameMax,double frameDurations[60])1503 void Console::DisplayDebugInformation(double lastFrame, double &lastFrameMin, double &lastFrameMax, double frameDurations[60])
1504 {
1505 	AudioStatistics stats = _soundMixer->GetStatistics();
1506 
1507 	int startFrame = _ppu->GetFrameCount();
1508 
1509 	_debugHud->DrawRectangle(8, 8, 115, 49, 0x40000000, true, 1, startFrame);
1510 	_debugHud->DrawRectangle(8, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
1511 
1512 	_debugHud->DrawString(10, 10, "Audio Stats", 0xFFFFFF, 0xFF000000, 1, startFrame);
1513 	_debugHud->DrawString(10, 21, "Latency: ", 0xFFFFFF, 0xFF000000, 1, startFrame);
1514 
1515 	int color = (stats.AverageLatency > 0 && std::abs(stats.AverageLatency - _settings->GetAudioLatency()) > 3) ? 0xFF0000 : 0xFFFFFF;
1516 	std::stringstream ss;
1517 	ss << std::fixed << std::setprecision(2) << stats.AverageLatency << " ms";
1518 	_debugHud->DrawString(54, 21, ss.str(), color, 0xFF000000, 1, startFrame);
1519 
1520 	_debugHud->DrawString(10, 30, "Underruns: " + std::to_string(stats.BufferUnderrunEventCount), 0xFFFFFF, 0xFF000000, 1, startFrame);
1521 	_debugHud->DrawString(10, 39, "Buffer Size: " + std::to_string(stats.BufferSize / 1024) + "kb", 0xFFFFFF, 0xFF000000, 1, startFrame);
1522 	_debugHud->DrawString(10, 48, "Rate: " + std::to_string((uint32_t)(_settings->GetSampleRate() * _soundMixer->GetRateAdjustment())) + "Hz", 0xFFFFFF, 0xFF000000, 1, startFrame);
1523 
1524 	_debugHud->DrawRectangle(132, 8, 115, 49, 0x40000000, true, 1, startFrame);
1525 	_debugHud->DrawRectangle(132, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
1526 	_debugHud->DrawString(134, 10, "Video Stats", 0xFFFFFF, 0xFF000000, 1, startFrame);
1527 
1528 	double totalDuration = 0;
1529 	for(int i = 0; i < 60; i++) {
1530 		totalDuration += frameDurations[i];
1531 	}
1532 
1533 	ss = std::stringstream();
1534 	ss << "FPS: " << std::fixed << std::setprecision(4) << (1000 / (totalDuration/60));
1535 	_debugHud->DrawString(134, 21, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
1536 
1537 	ss = std::stringstream();
1538 	ss << "Last Frame: " << std::fixed << std::setprecision(2) << lastFrame << " ms";
1539 	_debugHud->DrawString(134, 30, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
1540 
1541 	if(_ppu->GetFrameCount() > 60) {
1542 		lastFrameMin = (std::min)(lastFrame, lastFrameMin);
1543 		lastFrameMax = (std::max)(lastFrame, lastFrameMax);
1544 	} else {
1545 		lastFrameMin = 9999;
1546 		lastFrameMax = 0;
1547 	}
1548 
1549 	ss = std::stringstream();
1550 	ss << "Min Delay: " << std::fixed << std::setprecision(2) << ((lastFrameMin < 9999) ? lastFrameMin : 0.0) << " ms";
1551 	_debugHud->DrawString(134, 39, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
1552 
1553 	ss = std::stringstream();
1554 	ss << "Max Delay: " << std::fixed << std::setprecision(2) << lastFrameMax << " ms";
1555 	_debugHud->DrawString(134, 48, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
1556 }
1557 
ExportStub()1558 void Console::ExportStub()
1559 {
1560 	//Force the compiler to export the PgoRunTest function - otherwise it seems to be ignored since it is unused
1561 	vector<string> testRoms;
1562 	PgoRunTest(testRoms, true);
1563 }