2014-06-14 11:27:55 -04:00
|
|
|
#include "stdafx.h"
|
2019-06-22 16:33:59 -04:00
|
|
|
#include <random>
|
2019-11-16 22:32:06 -05:00
|
|
|
#include <future>
|
2015-07-01 23:17:14 -04:00
|
|
|
#include <thread>
|
2014-06-14 11:27:55 -04:00
|
|
|
#include "Console.h"
|
2017-04-29 08:29:56 -04:00
|
|
|
#include "CPU.h"
|
|
|
|
#include "PPU.h"
|
|
|
|
#include "APU.h"
|
|
|
|
#include "MemoryManager.h"
|
|
|
|
#include "AutoSaveManager.h"
|
2015-07-01 23:17:14 -04:00
|
|
|
#include "BaseMapper.h"
|
2016-04-30 20:08:53 -04:00
|
|
|
#include "ControlManager.h"
|
|
|
|
#include "VsControlManager.h"
|
2014-06-24 02:47:32 -04:00
|
|
|
#include "MapperFactory.h"
|
2015-07-01 23:17:14 -04:00
|
|
|
#include "Debugger.h"
|
2015-07-17 20:58:57 -04:00
|
|
|
#include "MessageManager.h"
|
|
|
|
#include "EmulationSettings.h"
|
2017-06-28 19:00:08 -04:00
|
|
|
#include "../Utilities/sha1.h"
|
2014-06-23 19:02:09 -04:00
|
|
|
#include "../Utilities/Timer.h"
|
2014-07-09 18:29:07 -04:00
|
|
|
#include "../Utilities/FolderUtilities.h"
|
2016-08-29 21:07:52 -04:00
|
|
|
#include "../Utilities/PlatformUtilities.h"
|
2017-09-30 14:07:07 -04:00
|
|
|
#include "VirtualFile.h"
|
2017-06-28 19:00:08 -04:00
|
|
|
#include "HdBuilderPpu.h"
|
2015-08-14 21:50:14 -04:00
|
|
|
#include "HdPpu.h"
|
2016-06-25 20:46:54 -04:00
|
|
|
#include "NsfPpu.h"
|
2016-01-14 19:33:16 -05:00
|
|
|
#include "SoundMixer.h"
|
2016-06-25 20:46:54 -04:00
|
|
|
#include "NsfMapper.h"
|
2017-04-18 22:39:45 -04:00
|
|
|
#include "MovieManager.h"
|
2017-04-28 19:54:58 -04:00
|
|
|
#include "RewindManager.h"
|
2017-05-06 15:27:48 -04:00
|
|
|
#include "SaveStateManager.h"
|
2017-06-28 19:00:08 -04:00
|
|
|
#include "HdPackBuilder.h"
|
2017-08-19 16:46:57 -04:00
|
|
|
#include "HdAudioDevice.h"
|
2017-11-19 23:08:23 -05:00
|
|
|
#include "FDS.h"
|
|
|
|
#include "SystemActionManager.h"
|
|
|
|
#include "FdsSystemActionManager.h"
|
|
|
|
#include "VsSystemActionManager.h"
|
2017-11-24 21:38:12 -05:00
|
|
|
#include "FamilyBasicDataRecorder.h"
|
2017-11-19 23:08:23 -05:00
|
|
|
#include "IBarcodeReader.h"
|
|
|
|
#include "IBattery.h"
|
|
|
|
#include "KeyManager.h"
|
|
|
|
#include "BatteryManager.h"
|
2018-06-09 14:03:53 -04:00
|
|
|
#include "DebugHud.h"
|
2018-07-01 15:21:05 -04:00
|
|
|
#include "RomLoader.h"
|
|
|
|
#include "CheatManager.h"
|
|
|
|
#include "VideoDecoder.h"
|
|
|
|
#include "VideoRenderer.h"
|
|
|
|
#include "DebugHud.h"
|
2018-07-02 14:49:19 -04:00
|
|
|
#include "NotificationManager.h"
|
2018-07-11 18:07:13 -04:00
|
|
|
#include "HistoryViewer.h"
|
2018-09-14 22:09:51 -04:00
|
|
|
#include "ConsolePauseHelper.h"
|
2018-12-31 14:59:00 -05:00
|
|
|
#include "PgoUtilities.h"
|
2014-06-21 19:03:13 -04:00
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
Console::Console(shared_ptr<Console> master, EmulationSettings* initialSettings)
|
2014-06-14 11:27:55 -04:00
|
|
|
{
|
2018-07-02 21:32:59 -04:00
|
|
|
_master = master;
|
2018-07-13 22:19:26 -04:00
|
|
|
|
|
|
|
if(_master) {
|
|
|
|
//Slave console should use the same settings as the master
|
|
|
|
_settings = _master->_settings;
|
|
|
|
} else {
|
|
|
|
if(initialSettings) {
|
|
|
|
_settings.reset(new EmulationSettings(*initialSettings));
|
|
|
|
} else {
|
|
|
|
_settings.reset(new EmulationSettings());
|
|
|
|
}
|
|
|
|
KeyManager::SetSettings(_settings.get());
|
|
|
|
}
|
|
|
|
|
2018-07-22 16:17:15 -04:00
|
|
|
_pauseCounter = 0;
|
2018-01-07 10:05:58 -05:00
|
|
|
_model = NesModel::NTSC;
|
2014-07-09 18:29:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Console::~Console()
|
|
|
|
{
|
2017-04-18 22:39:45 -04:00
|
|
|
MovieManager::Stop();
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::Init()
|
|
|
|
{
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager.reset(new NotificationManager());
|
2018-07-22 17:31:50 -04:00
|
|
|
_batteryManager.reset(new BatteryManager());
|
2018-07-02 21:32:59 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_videoRenderer.reset(new VideoRenderer(shared_from_this()));
|
|
|
|
_videoDecoder.reset(new VideoDecoder(shared_from_this()));
|
2018-07-02 21:32:59 -04:00
|
|
|
|
|
|
|
_saveStateManager.reset(new SaveStateManager(shared_from_this()));
|
2018-07-01 15:21:05 -04:00
|
|
|
_cheatManager.reset(new CheatManager(shared_from_this()));
|
|
|
|
_debugHud.reset(new DebugHud());
|
2018-07-02 21:32:59 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_soundMixer.reset(new SoundMixer(shared_from_this()));
|
2018-07-02 21:32:59 -04:00
|
|
|
_soundMixer->SetNesModel(_model);
|
2018-07-06 00:10:10 -04:00
|
|
|
|
|
|
|
if(_master) {
|
|
|
|
_emulationThreadId = _master->_emulationThreadId;
|
|
|
|
}
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::Release(bool forShutdown)
|
|
|
|
{
|
2018-07-03 23:32:26 -04:00
|
|
|
if(_slave) {
|
|
|
|
_slave->Release(true);
|
|
|
|
_slave.reset();
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
if(forShutdown) {
|
2018-07-02 21:32:59 -04:00
|
|
|
_videoDecoder->StopThread();
|
|
|
|
_videoRenderer->StopThread();
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_videoDecoder.reset();
|
2018-07-02 21:32:59 -04:00
|
|
|
_videoRenderer.reset();
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_debugHud.reset();
|
2018-07-02 21:32:59 -04:00
|
|
|
_saveStateManager.reset();
|
2018-07-01 15:21:05 -04:00
|
|
|
_cheatManager.reset();
|
2018-07-02 21:32:59 -04:00
|
|
|
|
|
|
|
_soundMixer.reset();
|
|
|
|
_notificationManager.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(_master) {
|
|
|
|
_master->_notificationManager->SendNotification(ConsoleNotificationType::VsDualSystemStopped);
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
_rewindManager.reset();
|
|
|
|
_autoSaveManager.reset();
|
|
|
|
|
|
|
|
_hdPackBuilder.reset();
|
|
|
|
_hdData.reset();
|
|
|
|
_hdAudioDevice.reset();
|
|
|
|
|
|
|
|
_systemActionManager.reset();
|
2018-07-02 21:32:59 -04:00
|
|
|
|
|
|
|
_master.reset();
|
2018-07-01 15:21:05 -04:00
|
|
|
_cpu.reset();
|
|
|
|
_ppu.reset();
|
|
|
|
_apu.reset();
|
|
|
|
_debugger.reset();
|
|
|
|
_mapper.reset();
|
|
|
|
_memoryManager.reset();
|
|
|
|
_controlManager.reset();
|
2014-07-09 18:29:07 -04:00
|
|
|
}
|
|
|
|
|
2018-07-22 17:31:50 -04:00
|
|
|
shared_ptr<BatteryManager> Console::GetBatteryManager()
|
|
|
|
{
|
|
|
|
return _batteryManager;
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
shared_ptr<SaveStateManager> Console::GetSaveStateManager()
|
2014-07-09 18:29:07 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _saveStateManager;
|
2014-07-09 18:29:07 -04:00
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
shared_ptr<VideoDecoder> Console::GetVideoDecoder()
|
2015-07-20 23:20:41 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _videoDecoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr<VideoRenderer> Console::GetVideoRenderer()
|
|
|
|
{
|
|
|
|
return _videoRenderer;
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr<DebugHud> Console::GetDebugHud()
|
|
|
|
{
|
|
|
|
return _debugHud;
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::SaveBatteries()
|
|
|
|
{
|
2018-06-16 10:03:49 -04:00
|
|
|
shared_ptr<BaseMapper> mapper = _mapper;
|
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
|
|
|
|
|
|
|
if(mapper) {
|
|
|
|
mapper->SaveBattery();
|
2018-01-04 19:03:47 -05:00
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
|
2018-06-16 10:03:49 -04:00
|
|
|
if(controlManager) {
|
|
|
|
shared_ptr<IBattery> device = std::dynamic_pointer_cast<IBattery>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort));
|
2018-01-04 19:03:47 -05:00
|
|
|
if(device) {
|
|
|
|
device->SaveBattery();
|
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
2015-07-20 23:20:41 -04:00
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
bool Console::LoadMatchingRom(string romName, HashInfo hashInfo)
|
|
|
|
{
|
|
|
|
if(_initialized) {
|
|
|
|
string currentRomFilepath = GetRomPath().GetFilePath();
|
|
|
|
if(!currentRomFilepath.empty()) {
|
2018-07-07 14:52:51 -04:00
|
|
|
HashInfo gameHashInfo = GetRomInfo().Hash;
|
|
|
|
if(gameHashInfo.Crc32 == hashInfo.Crc32 || gameHashInfo.Sha1.compare(hashInfo.Sha1) == 0 || gameHashInfo.PrgChrMd5.compare(hashInfo.PrgChrMd5) == 0) {
|
2018-07-01 15:21:05 -04:00
|
|
|
//Current game matches, power cycle game and return
|
|
|
|
PowerCycle();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string match = FindMatchingRom(romName, hashInfo);
|
|
|
|
if(!match.empty()) {
|
|
|
|
return Initialize(match);
|
|
|
|
}
|
2018-07-02 19:01:10 -04:00
|
|
|
return false;
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
string Console::FindMatchingRom(string romName, HashInfo hashInfo)
|
|
|
|
{
|
|
|
|
if(_initialized) {
|
|
|
|
VirtualFile currentRom = GetRomPath();
|
|
|
|
if(currentRom.IsValid() && !GetPatchFile().IsValid()) {
|
2018-07-07 14:52:51 -04:00
|
|
|
HashInfo gameHashInfo = GetRomInfo().Hash;
|
|
|
|
if(gameHashInfo.Crc32 == hashInfo.Crc32 || gameHashInfo.Sha1.compare(hashInfo.Sha1) == 0 || gameHashInfo.PrgChrMd5.compare(hashInfo.PrgChrMd5) == 0) {
|
2018-07-01 15:21:05 -04:00
|
|
|
//Current game matches
|
|
|
|
return currentRom;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string lcRomname = romName;
|
|
|
|
std::transform(lcRomname.begin(), lcRomname.end(), lcRomname.begin(), ::tolower);
|
|
|
|
std::unordered_set<string> validExtensions = { { ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe", "*.7z", "*.zip" } };
|
|
|
|
vector<string> romFiles;
|
|
|
|
for(string folder : FolderUtilities::GetKnownGameFolders()) {
|
|
|
|
vector<string> files = FolderUtilities::GetFilesInFolder(folder, validExtensions, true);
|
|
|
|
romFiles.insert(romFiles.end(), files.begin(), files.end());
|
|
|
|
}
|
|
|
|
|
2019-01-20 16:33:23 -05:00
|
|
|
if(!romName.empty()) {
|
|
|
|
//Perform quick search based on file name
|
|
|
|
string match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, true);
|
|
|
|
if(!match.empty()) {
|
|
|
|
return match;
|
|
|
|
}
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//Perform slow CRC32 search for ROM
|
2019-01-20 16:33:23 -05:00
|
|
|
string match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, false);
|
2018-07-01 15:21:05 -04:00
|
|
|
if(!match.empty()) {
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Console::Initialize(string romFile, string patchFile)
|
|
|
|
{
|
|
|
|
VirtualFile rom = romFile;
|
|
|
|
VirtualFile patch = patchFile;
|
|
|
|
return Initialize(rom, patch);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Console::Initialize(VirtualFile &romFile)
|
|
|
|
{
|
|
|
|
VirtualFile patchFile;
|
|
|
|
return Initialize(romFile, patchFile);
|
|
|
|
}
|
|
|
|
|
2019-11-17 18:44:36 -05:00
|
|
|
bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile, bool forPowerCycle)
|
2014-07-09 18:29:07 -04:00
|
|
|
{
|
2017-07-30 09:03:54 -04:00
|
|
|
if(romFile.IsValid()) {
|
2019-11-16 22:56:01 -05:00
|
|
|
Pause();
|
|
|
|
if(!_romFilepath.empty() && _mapper) {
|
|
|
|
//Ensure we save any battery file before loading a new game
|
|
|
|
SaveBatteries();
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_videoDecoder->StopThread();
|
2017-12-18 20:02:25 -05:00
|
|
|
|
2018-08-06 17:58:15 -04:00
|
|
|
shared_ptr<HdPackData> originalHdPackData = _hdData;
|
2017-07-30 09:03:54 -04:00
|
|
|
LoadHdPack(romFile, patchFile);
|
|
|
|
if(patchFile.IsValid()) {
|
|
|
|
if(romFile.ApplyPatch(patchFile)) {
|
2017-11-19 23:08:23 -05:00
|
|
|
MessageManager::DisplayMessage("Patch", "ApplyingPatch", patchFile.GetFileName());
|
2017-07-30 09:03:54 -04:00
|
|
|
} else {
|
|
|
|
//Patch failed
|
|
|
|
}
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
2017-07-30 09:03:54 -04:00
|
|
|
vector<uint8_t> fileData;
|
|
|
|
romFile.ReadFile(fileData);
|
2016-07-26 19:19:28 -04:00
|
|
|
|
2018-07-22 17:31:50 -04:00
|
|
|
_batteryManager->Initialize(FolderUtilities::GetFilename(romFile.GetFileName(), false));
|
2019-06-22 16:33:59 -04:00
|
|
|
|
|
|
|
RomData romData;
|
|
|
|
shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(shared_from_this(), romFile.GetFileName(), fileData, romData);
|
2017-06-28 19:00:08 -04:00
|
|
|
if(mapper) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_soundMixer->StopAudio(true);
|
2017-12-28 20:07:32 -05:00
|
|
|
|
2018-06-16 23:42:22 -04:00
|
|
|
bool isDifferentGame = _romFilepath != (string)romFile || _patchFilename != (string)patchFile;
|
2017-06-28 19:00:08 -04:00
|
|
|
if(_mapper) {
|
2018-06-16 23:42:22 -04:00
|
|
|
if(isDifferentGame) {
|
|
|
|
//Save current game state before loading another one
|
2018-07-07 14:52:51 -04:00
|
|
|
_saveStateManager->SaveRecentGame(GetRomInfo().RomName, _romFilepath, _patchFilename);
|
2018-06-16 23:42:22 -04:00
|
|
|
}
|
|
|
|
|
2017-06-28 19:00:08 -04:00
|
|
|
//Send notification only if a game was already running and we successfully loaded the new one
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::GameStopped, (void*)1);
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
2015-12-26 17:11:00 -05:00
|
|
|
|
2018-06-16 23:42:22 -04:00
|
|
|
if(isDifferentGame) {
|
2017-11-19 23:08:23 -05:00
|
|
|
_romFilepath = romFile;
|
|
|
|
_patchFilename = patchFile;
|
|
|
|
|
|
|
|
//Changed game, stop all recordings
|
|
|
|
MovieManager::Stop();
|
2018-07-02 21:32:59 -04:00
|
|
|
_soundMixer->StopRecording();
|
2017-11-19 23:08:23 -05:00
|
|
|
StopRecordingHdPack();
|
|
|
|
}
|
2017-05-25 20:04:49 -04:00
|
|
|
|
2019-11-17 18:44:36 -05:00
|
|
|
shared_ptr<BaseMapper> previousMapper = _mapper;
|
2017-06-28 19:00:08 -04:00
|
|
|
_mapper = mapper;
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager.reset(new MemoryManager(shared_from_this()));
|
|
|
|
_cpu.reset(new CPU(shared_from_this()));
|
|
|
|
_apu.reset(new APU(shared_from_this()));
|
2017-11-19 23:08:23 -05:00
|
|
|
|
2019-06-23 17:15:05 -04:00
|
|
|
_mapper->SetConsole(shared_from_this());
|
|
|
|
_mapper->Initialize(romData);
|
2019-11-17 18:44:36 -05:00
|
|
|
if(!isDifferentGame && forPowerCycle) {
|
|
|
|
_mapper->CopyPrgChrRom(previousMapper);
|
|
|
|
}
|
2019-06-23 17:15:05 -04:00
|
|
|
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_slave) {
|
|
|
|
_slave->Release(false);
|
|
|
|
_slave.reset();
|
|
|
|
}
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
RomInfo romInfo = _mapper->GetRomInfo();
|
2018-07-07 17:30:13 -04:00
|
|
|
if(!_master && romInfo.VsType == VsSystemType::VsDualSystem) {
|
2018-07-02 21:32:59 -04:00
|
|
|
_slave.reset(new Console(shared_from_this()));
|
|
|
|
_slave->Init();
|
|
|
|
_slave->Initialize(romFile, patchFile);
|
|
|
|
}
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
switch(romInfo.System) {
|
2018-01-04 19:03:47 -05:00
|
|
|
case GameSystem::FDS:
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->SetPpuModel(PpuModel::Ppu2C02);
|
2018-07-01 15:21:05 -04:00
|
|
|
_systemActionManager.reset(new FdsSystemActionManager(shared_from_this(), _mapper));
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
case GameSystem::VsSystem:
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->SetPpuModel(romInfo.VsPpuModel);
|
2018-07-01 15:21:05 -04:00
|
|
|
_systemActionManager.reset(new VsSystemActionManager(shared_from_this()));
|
2018-01-04 19:03:47 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->SetPpuModel(PpuModel::Ppu2C02);
|
2018-07-01 15:21:05 -04:00
|
|
|
_systemActionManager.reset(new SystemActionManager(shared_from_this())); break;
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
2014-06-21 15:43:41 -04:00
|
|
|
|
2017-12-27 12:32:10 -05:00
|
|
|
//Temporarely disable battery saves to prevent battery files from being created for the wrong game (for Battle Box & Turbo File)
|
2018-07-22 17:31:50 -04:00
|
|
|
_batteryManager->SetSaveEnabled(false);
|
2018-07-02 18:46:02 -04:00
|
|
|
|
|
|
|
uint32_t pollCounter = 0;
|
|
|
|
if(_controlManager && !isDifferentGame) {
|
|
|
|
//When power cycling, poll counter must be preserved to allow movies to playback properly
|
|
|
|
pollCounter = _controlManager->GetPollCounter();
|
|
|
|
}
|
2018-07-02 21:32:59 -04:00
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
if(romInfo.System == GameSystem::VsSystem) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_controlManager.reset(new VsControlManager(shared_from_this(), _systemActionManager, _mapper->GetMapperControlDevice()));
|
2017-11-19 23:08:23 -05:00
|
|
|
} else {
|
2018-07-01 15:21:05 -04:00
|
|
|
_controlManager.reset(new ControlManager(shared_from_this(), _systemActionManager, _mapper->GetMapperControlDevice()));
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
2018-07-02 18:46:02 -04:00
|
|
|
_controlManager->SetPollCounter(pollCounter);
|
2017-11-19 23:08:23 -05:00
|
|
|
_controlManager->UpdateControlDevices();
|
2018-07-02 18:46:02 -04:00
|
|
|
|
2017-12-27 12:32:10 -05:00
|
|
|
//Re-enable battery saves
|
2018-07-22 17:31:50 -04:00
|
|
|
_batteryManager->SetSaveEnabled(true);
|
2017-11-19 23:08:23 -05:00
|
|
|
|
2017-08-19 17:51:36 -04:00
|
|
|
if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_ppu.reset(new HdPpu(shared_from_this(), _hdData.get()));
|
2018-01-02 12:18:01 -05:00
|
|
|
} else if(std::dynamic_pointer_cast<NsfMapper>(_mapper)) {
|
2017-06-28 19:00:08 -04:00
|
|
|
//Disable most of the PPU for NSFs
|
2018-07-01 15:21:05 -04:00
|
|
|
_ppu.reset(new NsfPpu(shared_from_this()));
|
2017-06-28 19:00:08 -04:00
|
|
|
} else {
|
2018-07-01 15:21:05 -04:00
|
|
|
_ppu.reset(new PPU(shared_from_this()));
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
2016-01-23 00:52:06 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager->SetMapper(_mapper);
|
2017-06-28 19:00:08 -04:00
|
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
|
|
|
_memoryManager->RegisterIODevice(_apu.get());
|
|
|
|
_memoryManager->RegisterIODevice(_controlManager.get());
|
|
|
|
_memoryManager->RegisterIODevice(_mapper.get());
|
2018-07-01 15:21:05 -04:00
|
|
|
|
2017-08-19 16:46:57 -04:00
|
|
|
if(_hdData && (!_hdData->BgmFilesById.empty() || !_hdData->SfxFilesById.empty())) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_hdAudioDevice.reset(new HdAudioDevice(shared_from_this(), _hdData.get()));
|
2017-08-19 16:46:57 -04:00
|
|
|
_memoryManager->RegisterIODevice(_hdAudioDevice.get());
|
2018-08-06 17:58:15 -04:00
|
|
|
} else {
|
|
|
|
_hdAudioDevice.reset();
|
2017-08-19 16:46:57 -04:00
|
|
|
}
|
2016-01-28 22:34:23 -05:00
|
|
|
|
2017-06-28 19:00:08 -04:00
|
|
|
_model = NesModel::Auto;
|
|
|
|
UpdateNesModel(false);
|
2016-06-04 08:55:52 -04:00
|
|
|
|
2017-06-28 19:00:08 -04:00
|
|
|
_initialized = true;
|
2017-04-28 19:54:58 -04:00
|
|
|
|
2017-06-28 19:00:08 -04:00
|
|
|
if(_debugger) {
|
2017-12-28 16:30:37 -05:00
|
|
|
//Reset debugger if it was running before
|
|
|
|
auto lock = _debuggerLock.AcquireSafe();
|
2017-06-28 19:00:08 -04:00
|
|
|
StopDebugger();
|
|
|
|
GetDebugger();
|
|
|
|
}
|
|
|
|
|
|
|
|
ResetComponents(false);
|
2016-07-10 19:15:00 -04:00
|
|
|
|
2018-07-15 18:26:08 -04:00
|
|
|
//Reset components before creating rewindmanager, otherwise the first save state it takes will be invalid
|
2019-11-17 18:44:36 -05:00
|
|
|
if(!forPowerCycle) {
|
|
|
|
_rewindManager.reset(new RewindManager(shared_from_this()));
|
|
|
|
_notificationManager->RegisterNotificationListener(_rewindManager);
|
|
|
|
}
|
2018-07-15 18:26:08 -04:00
|
|
|
|
|
|
|
//Poll controller input after creating rewind manager, to make sure it catches the first frame's input
|
|
|
|
_controlManager->UpdateInputState();
|
|
|
|
|
2018-07-02 21:32:59 -04:00
|
|
|
#ifndef LIBRETRO
|
|
|
|
//Don't use auto-save manager for libretro
|
|
|
|
//Only enable auto-save for the master console (VS Dualsystem)
|
|
|
|
if(IsMaster()) {
|
|
|
|
_autoSaveManager.reset(new AutoSaveManager(shared_from_this()));
|
|
|
|
}
|
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
_videoDecoder->StartThread();
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2017-07-30 09:03:54 -04:00
|
|
|
FolderUtilities::AddKnownGameFolder(romFile.GetFolderPath());
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2018-07-02 21:32:59 -04:00
|
|
|
if(IsMaster()) {
|
2019-11-17 18:44:36 -05:00
|
|
|
if(!forPowerCycle) {
|
|
|
|
string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC");
|
|
|
|
string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
|
|
|
|
MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomName, false));
|
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
|
2018-07-06 00:10:10 -04:00
|
|
|
|
|
|
|
if(_slave) {
|
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::VsDualSystemStarted);
|
|
|
|
}
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
2018-07-01 15:21:05 -04:00
|
|
|
Resume();
|
2017-06-28 19:00:08 -04:00
|
|
|
return true;
|
2018-08-06 17:58:15 -04:00
|
|
|
} else {
|
|
|
|
_hdData = originalHdPackData;
|
2019-11-16 22:56:01 -05:00
|
|
|
|
|
|
|
//Reset battery source to current game if new game failed to load
|
|
|
|
_batteryManager->Initialize(FolderUtilities::GetFilename(GetRomInfo().RomName, false));
|
|
|
|
if(_mapper) {
|
|
|
|
_videoDecoder->StartThread();
|
|
|
|
}
|
|
|
|
Resume();
|
2016-06-12 18:11:31 -04:00
|
|
|
}
|
2014-07-10 19:25:35 -04:00
|
|
|
}
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2019-11-16 22:56:01 -05:00
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
|
|
|
if(debugger) {
|
|
|
|
debugger->Resume();
|
2017-12-18 20:02:25 -05:00
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
|
2017-07-30 09:03:54 -04:00
|
|
|
MessageManager::DisplayMessage("Error", "CouldNotLoadFile", romFile.GetFileName());
|
2017-06-28 19:00:08 -04:00
|
|
|
return false;
|
2015-07-05 22:23:44 -04:00
|
|
|
}
|
2014-07-01 12:44:01 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
void Console::ProcessCpuClock()
|
2015-12-27 18:41:38 -05:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
_mapper->ProcessCpuClock();
|
|
|
|
_apu->ProcessCpuClock();
|
2014-06-14 11:27:55 -04:00
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
CPU* Console::GetCpu()
|
2014-06-14 11:27:55 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _cpu.get();
|
|
|
|
}
|
2015-07-05 22:23:44 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
PPU* Console::GetPpu()
|
|
|
|
{
|
|
|
|
return _ppu.get();
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
APU* Console::GetApu()
|
2017-11-19 23:08:23 -05:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _apu.get();
|
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
shared_ptr<SoundMixer> Console::GetSoundMixer()
|
|
|
|
{
|
|
|
|
return _soundMixer;
|
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
|
2018-07-02 14:49:19 -04:00
|
|
|
shared_ptr<NotificationManager> Console::GetNotificationManager()
|
|
|
|
{
|
|
|
|
return _notificationManager;
|
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
EmulationSettings* Console::GetSettings()
|
|
|
|
{
|
|
|
|
return _settings.get();
|
|
|
|
}
|
|
|
|
|
2018-07-02 21:32:59 -04:00
|
|
|
bool Console::IsDualSystem()
|
|
|
|
{
|
|
|
|
return _slave != nullptr || _master != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_ptr<Console> Console::GetDualConsole()
|
|
|
|
{
|
|
|
|
//When called from the master, returns the slave.
|
|
|
|
//When called from the slave, returns the master.
|
|
|
|
//Returns a null pointer when not running a dualsystem game
|
|
|
|
return _slave ? _slave : _master;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Console::IsMaster()
|
|
|
|
{
|
|
|
|
return !_master;
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
BaseMapper* Console::GetMapper()
|
|
|
|
{
|
|
|
|
return _mapper.get();
|
|
|
|
}
|
2015-07-05 22:23:44 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
ControlManager* Console::GetControlManager()
|
|
|
|
{
|
|
|
|
return _controlManager.get();
|
|
|
|
}
|
2016-02-08 23:34:48 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
MemoryManager* Console::GetMemoryManager()
|
|
|
|
{
|
|
|
|
return _memoryManager.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
CheatManager* Console::GetCheatManager()
|
|
|
|
{
|
|
|
|
return _cheatManager.get();
|
|
|
|
}
|
|
|
|
|
2018-08-06 18:32:22 -04:00
|
|
|
shared_ptr<RewindManager> Console::GetRewindManager()
|
2018-07-01 15:21:05 -04:00
|
|
|
{
|
2018-08-06 18:32:22 -04:00
|
|
|
return _rewindManager;
|
2014-07-09 18:29:07 -04:00
|
|
|
}
|
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
HistoryViewer* Console::GetHistoryViewer()
|
|
|
|
{
|
|
|
|
return _historyViewer.get();
|
|
|
|
}
|
|
|
|
|
2017-08-19 19:40:02 -04:00
|
|
|
VirtualFile Console::GetRomPath()
|
2014-07-09 18:29:07 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return static_cast<VirtualFile>(_romFilepath);
|
2014-06-14 11:27:55 -04:00
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
VirtualFile Console::GetPatchFile()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return (VirtualFile)_patchFilename;
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
|
2018-07-07 14:52:51 -04:00
|
|
|
RomInfo Console::GetRomInfo()
|
2017-06-28 19:00:08 -04:00
|
|
|
{
|
2018-07-07 14:52:51 -04:00
|
|
|
return _mapper ? _mapper->GetRomInfo() : (RomInfo {});
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Console::GetFrameCount()
|
|
|
|
{
|
|
|
|
return _ppu ? _ppu->GetFrameCount() : 0;
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
|
|
|
|
2016-07-01 23:54:31 -04:00
|
|
|
NesModel Console::GetModel()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _model;
|
2016-07-01 23:54:31 -04:00
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
shared_ptr<SystemActionManager> Console::GetSystemActionManager()
|
|
|
|
{
|
|
|
|
return _systemActionManager;
|
|
|
|
}
|
|
|
|
|
2017-05-02 23:31:06 -04:00
|
|
|
void Console::PowerCycle()
|
2019-11-17 18:44:36 -05:00
|
|
|
{
|
|
|
|
ReloadRom(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::ReloadRom(bool forPowerCycle)
|
2017-05-02 23:31:06 -04:00
|
|
|
{
|
2017-11-19 23:08:23 -05:00
|
|
|
if(_initialized && !_romFilepath.empty()) {
|
2018-07-01 15:21:05 -04:00
|
|
|
VirtualFile romFile = _romFilepath;
|
|
|
|
VirtualFile patchFile = _patchFilename;
|
2019-11-17 18:44:36 -05:00
|
|
|
Initialize(romFile, patchFile, forPowerCycle);
|
2017-05-02 23:31:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-05 19:35:38 -04:00
|
|
|
void Console::Reset(bool softReset)
|
2014-06-14 11:27:55 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_initialized) {
|
2018-07-07 18:06:34 -04:00
|
|
|
bool needSuspend = softReset ? _systemActionManager->Reset() : _systemActionManager->PowerCycle();
|
|
|
|
|
|
|
|
if(needSuspend) {
|
|
|
|
//Only do this if a reset/power cycle is not already pending - otherwise we'll end up calling Suspend() too many times
|
|
|
|
//Resume from code break if needed (otherwise reset doesn't happen right away)
|
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
|
|
|
if(debugger) {
|
|
|
|
debugger->Suspend();
|
|
|
|
debugger->Run();
|
|
|
|
}
|
2017-12-27 12:39:19 -05:00
|
|
|
}
|
2014-07-01 12:44:01 -04:00
|
|
|
}
|
2014-06-14 11:27:55 -04:00
|
|
|
}
|
|
|
|
|
2014-06-25 13:30:02 -04:00
|
|
|
void Console::ResetComponents(bool softReset)
|
2014-06-24 02:47:32 -04:00
|
|
|
{
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_slave) {
|
|
|
|
//Always reset/power cycle the slave alongside the master CPU
|
|
|
|
_slave->ResetComponents(softReset);
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_soundMixer->StopAudio(true);
|
2018-07-30 21:05:13 -04:00
|
|
|
_debugHud->ClearScreen();
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2016-07-17 14:07:22 -04:00
|
|
|
_memoryManager->Reset(softReset);
|
2018-12-26 13:28:17 -05:00
|
|
|
if(!_settings->CheckFlag(EmulationFlags::DisablePpuReset) || !softReset || IsNsf()) {
|
2017-02-24 23:06:13 -05:00
|
|
|
_ppu->Reset();
|
|
|
|
}
|
2015-07-19 01:30:13 -04:00
|
|
|
_apu->Reset(softReset);
|
2017-04-20 21:58:35 -04:00
|
|
|
_cpu->Reset(softReset, _model);
|
2016-06-21 22:13:26 -04:00
|
|
|
_controlManager->Reset(softReset);
|
2016-07-10 18:22:37 -04:00
|
|
|
|
2018-08-26 18:53:15 -04:00
|
|
|
_resetRunTimers = true;
|
|
|
|
|
2019-11-16 22:32:06 -05:00
|
|
|
std::async(std::launch::async, KeyManager::UpdateDevices);
|
2015-08-24 20:27:07 -04:00
|
|
|
|
2018-07-02 14:49:19 -04:00
|
|
|
//This notification MUST be sent before the UpdateInputState() below to allow MovieRecorder to grab the first frame's worth of inputs
|
|
|
|
_notificationManager->SendNotification(softReset ? ConsoleNotificationType::GameReset : ConsoleNotificationType::GameLoaded);
|
2017-10-03 18:40:26 -04:00
|
|
|
|
|
|
|
if(softReset) {
|
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
|
|
|
if(debugger) {
|
2017-10-05 20:48:18 -04:00
|
|
|
debugger->ResetCounters();
|
2017-10-03 18:40:26 -04:00
|
|
|
debugger->ProcessEvent(EventType::Reset);
|
2017-12-29 10:00:37 -05:00
|
|
|
debugger->Resume();
|
2017-10-03 18:40:26 -04:00
|
|
|
}
|
|
|
|
}
|
2014-06-24 02:47:32 -04:00
|
|
|
}
|
|
|
|
|
2018-04-14 21:53:52 -04:00
|
|
|
void Console::Stop(int stopCode)
|
2014-06-20 21:48:55 -04:00
|
|
|
{
|
|
|
|
_stop = true;
|
2018-04-14 21:53:52 -04:00
|
|
|
_stopCode = stopCode;
|
2016-07-31 14:31:44 -04:00
|
|
|
|
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
|
|
|
if(debugger) {
|
2017-08-21 23:11:14 -04:00
|
|
|
debugger->Suspend();
|
2015-08-21 22:42:44 -04:00
|
|
|
}
|
2015-07-05 19:12:41 -04:00
|
|
|
_stopLock.Acquire();
|
|
|
|
_stopLock.Release();
|
2014-06-21 19:03:13 -04:00
|
|
|
}
|
|
|
|
|
2014-07-01 12:44:01 -04:00
|
|
|
void Console::Pause()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
2016-07-31 14:31:44 -04:00
|
|
|
if(debugger) {
|
2015-08-21 22:42:44 -04:00
|
|
|
//Make sure debugger resumes if we try to pause the emu, otherwise we will get deadlocked.
|
2016-07-31 14:31:44 -04:00
|
|
|
debugger->Suspend();
|
2015-08-21 22:42:44 -04:00
|
|
|
}
|
2018-07-02 21:32:59 -04:00
|
|
|
|
|
|
|
if(_master) {
|
|
|
|
//When trying to pause/resume the slave, we need to pause/resume the master instead
|
|
|
|
_master->Pause();
|
|
|
|
} else {
|
2018-07-22 16:17:15 -04:00
|
|
|
_pauseCounter++;
|
2018-07-02 21:32:59 -04:00
|
|
|
_runLock.Acquire();
|
|
|
|
}
|
2014-07-01 12:44:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::Resume()
|
|
|
|
{
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_master) {
|
|
|
|
//When trying to pause/resume the slave, we need to pause/resume the master instead
|
|
|
|
_master->Resume();
|
|
|
|
} else {
|
|
|
|
_runLock.Release();
|
2018-07-22 16:17:15 -04:00
|
|
|
_pauseCounter--;
|
2018-07-02 21:32:59 -04:00
|
|
|
}
|
2016-06-04 08:55:52 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
2016-07-31 14:31:44 -04:00
|
|
|
if(debugger) {
|
2016-06-04 08:55:52 -04:00
|
|
|
//Make sure debugger resumes if we try to pause the emu, otherwise we will get deadlocked.
|
2016-07-31 14:31:44 -04:00
|
|
|
debugger->Resume();
|
2016-06-04 08:55:52 -04:00
|
|
|
}
|
2014-07-01 12:44:01 -04:00
|
|
|
}
|
|
|
|
|
2018-01-04 19:03:47 -05:00
|
|
|
void Console::RunSingleFrame()
|
|
|
|
{
|
|
|
|
//Used by Libretro
|
2018-07-01 15:21:05 -04:00
|
|
|
uint32_t lastFrameNumber = _ppu->GetFrameCount();
|
2018-01-04 19:03:47 -05:00
|
|
|
_emulationThreadId = std::this_thread::get_id();
|
2018-01-08 20:11:11 -05:00
|
|
|
UpdateNesModel(true);
|
2018-01-04 19:03:47 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
while(_ppu->GetFrameCount() == lastFrameNumber) {
|
2018-01-04 19:03:47 -05:00
|
|
|
_cpu->Exec();
|
2018-07-03 23:32:26 -04:00
|
|
|
if(_slave) {
|
|
|
|
RunSlaveCpu();
|
|
|
|
}
|
2018-01-04 19:03:47 -05:00
|
|
|
}
|
|
|
|
|
2018-12-26 13:28:17 -05:00
|
|
|
_settings->DisableOverclocking(_disableOcNextFrame || IsNsf());
|
2018-01-04 19:03:47 -05:00
|
|
|
_disableOcNextFrame = false;
|
|
|
|
|
|
|
|
_systemActionManager->ProcessSystemActions();
|
|
|
|
_apu->EndFrame();
|
|
|
|
}
|
|
|
|
|
2018-07-03 23:32:26 -04:00
|
|
|
void Console::RunSlaveCpu()
|
|
|
|
{
|
2019-05-12 12:28:01 -04:00
|
|
|
int64_t cycleGap;
|
2018-07-03 23:32:26 -04:00
|
|
|
while(true) {
|
|
|
|
//Run the slave until it catches up to the master CPU (and take into account the CPU count overflow that occurs every ~20mins)
|
2019-05-12 12:28:01 -04:00
|
|
|
cycleGap = (int64_t)(_cpu->GetCycleCount() - _slave->_cpu->GetCycleCount());
|
|
|
|
if(cycleGap > 5 || _ppu->GetFrameCount() > _slave->_ppu->GetFrameCount()) {
|
2018-07-03 23:32:26 -04:00
|
|
|
_slave->_cpu->Exec();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-14 11:27:55 -04:00
|
|
|
void Console::Run()
|
|
|
|
{
|
2014-06-21 15:43:41 -04:00
|
|
|
Timer clockTimer;
|
2018-06-09 14:03:53 -04:00
|
|
|
Timer lastFrameTimer;
|
2019-02-01 14:15:27 -05:00
|
|
|
double frameDurations[60] = {};
|
|
|
|
uint32_t frameDurationIndex = 0;
|
2015-07-21 23:05:27 -04:00
|
|
|
double targetTime;
|
2018-06-09 14:03:53 -04:00
|
|
|
double lastFrameMin = 9999;
|
|
|
|
double lastFrameMax = 0;
|
2015-07-21 23:05:27 -04:00
|
|
|
uint32_t lastFrameNumber = -1;
|
2018-08-26 18:53:15 -04:00
|
|
|
double lastDelay = GetFrameDelay();
|
2015-07-14 21:51:39 -04:00
|
|
|
|
2015-07-05 19:05:33 -04:00
|
|
|
_runLock.Acquire();
|
|
|
|
_stopLock.Acquire();
|
2014-07-06 19:54:47 -04:00
|
|
|
|
2017-10-07 13:01:42 -04:00
|
|
|
_emulationThreadId = std::this_thread::get_id();
|
2018-07-06 00:10:10 -04:00
|
|
|
if(_slave) {
|
|
|
|
_slave->_emulationThreadId = std::this_thread::get_id();
|
|
|
|
}
|
2017-10-07 13:01:42 -04:00
|
|
|
|
2018-08-26 18:53:15 -04:00
|
|
|
targetTime = lastDelay;
|
2015-07-21 23:05:27 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_videoDecoder->StartThread();
|
2016-08-29 21:07:52 -04:00
|
|
|
|
|
|
|
PlatformUtilities::DisableScreensaver();
|
2017-05-06 15:27:48 -04:00
|
|
|
|
2017-05-22 11:50:21 -04:00
|
|
|
UpdateNesModel(true);
|
|
|
|
|
2018-04-10 17:05:54 -04:00
|
|
|
_running = true;
|
|
|
|
|
2017-05-06 15:27:48 -04:00
|
|
|
bool crashed = false;
|
2017-12-04 00:09:11 -05:00
|
|
|
try {
|
|
|
|
while(true) {
|
2016-02-11 22:59:31 -05:00
|
|
|
_cpu->Exec();
|
2017-02-24 23:06:13 -05:00
|
|
|
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_slave) {
|
2018-07-03 23:32:26 -04:00
|
|
|
RunSlaveCpu();
|
2018-07-02 21:32:59 -04:00
|
|
|
}
|
|
|
|
|
2018-07-03 23:32:26 -04:00
|
|
|
if(_ppu->GetFrameCount() != lastFrameNumber) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_soundMixer->ProcessEndOfFrame();
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_slave) {
|
|
|
|
_slave->_soundMixer->ProcessEndOfFrame();
|
|
|
|
}
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
if(_historyViewer) {
|
|
|
|
_historyViewer->ProcessEndOfFrame();
|
|
|
|
}
|
2017-12-04 00:09:11 -05:00
|
|
|
_rewindManager->ProcessEndOfFrame();
|
2018-12-26 13:28:17 -05:00
|
|
|
_settings->DisableOverclocking(_disableOcNextFrame || IsNsf());
|
2017-12-04 00:09:11 -05:00
|
|
|
_disableOcNextFrame = false;
|
2014-07-06 19:54:47 -04:00
|
|
|
|
2018-08-26 18:53:15 -04:00
|
|
|
//Update model (ntsc/pal) and get delay for next frame
|
|
|
|
UpdateNesModel(true);
|
|
|
|
double delay = GetFrameDelay();
|
|
|
|
|
2019-01-21 09:48:54 -05:00
|
|
|
if(_resetRunTimers || delay != lastDelay || (clockTimer.GetElapsedMS() - targetTime) > 300) {
|
|
|
|
//Reset the timers, this can happen in 3 scenarios:
|
|
|
|
//1) Target frame rate changed
|
|
|
|
//2) The console was reset/power cycled or the emulation was paused (with or without the debugger)
|
|
|
|
//3) As a satefy net, if we overshoot our target by over 300 milliseconds, the timer is reset, too.
|
|
|
|
// This can happen when something slows the emulator down severely (or when breaking execution in VS when debugging Mesen itself, etc.)
|
2018-08-26 18:53:15 -04:00
|
|
|
clockTimer.Reset();
|
|
|
|
targetTime = 0;
|
|
|
|
|
|
|
|
_resetRunTimers = false;
|
|
|
|
lastDelay = delay;
|
|
|
|
}
|
|
|
|
|
|
|
|
targetTime += delay;
|
|
|
|
|
|
|
|
bool displayDebugInfo = _settings->CheckFlag(EmulationFlags::DisplayDebugInfo);
|
|
|
|
if(displayDebugInfo) {
|
2019-02-01 14:15:27 -05:00
|
|
|
double lastFrameTime = lastFrameTimer.GetElapsedMS();
|
|
|
|
lastFrameTimer.Reset();
|
|
|
|
frameDurations[frameDurationIndex] = lastFrameTime;
|
|
|
|
frameDurationIndex = (frameDurationIndex + 1) % 60;
|
|
|
|
|
|
|
|
DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
|
2018-08-26 18:53:15 -04:00
|
|
|
if(_slave) {
|
2019-02-01 14:15:27 -05:00
|
|
|
_slave->DisplayDebugInformation(lastFrameTime, lastFrameMin, lastFrameMax, frameDurations);
|
2018-08-26 18:53:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-13 20:52:12 -05:00
|
|
|
//When sleeping for a long time (e.g <= 25% speed), sleep in small chunks and check to see if we need to stop sleeping between each sleep call
|
|
|
|
while(targetTime - clockTimer.GetElapsedMS() > 50) {
|
|
|
|
clockTimer.WaitUntil(clockTimer.GetElapsedMS() + 40);
|
|
|
|
if(delay != GetFrameDelay() || _stop || _settings->NeedsPause() || _pauseCounter > 0) {
|
|
|
|
targetTime = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 00:09:11 -05:00
|
|
|
//Sleep until we're ready to start the next frame
|
|
|
|
clockTimer.WaitUntil(targetTime);
|
2014-07-01 12:44:01 -04:00
|
|
|
|
2018-07-22 16:17:15 -04:00
|
|
|
if(_pauseCounter > 0) {
|
2017-12-04 00:09:11 -05:00
|
|
|
//Need to temporarely pause the emu (to save/load a state, etc.)
|
|
|
|
_runLock.Release();
|
2014-07-06 19:54:47 -04:00
|
|
|
|
2017-12-04 00:09:11 -05:00
|
|
|
//Spin wait until we are allowed to start again
|
2018-07-22 16:17:15 -04:00
|
|
|
while(_pauseCounter > 0) { }
|
2016-09-04 19:04:52 -04:00
|
|
|
|
2017-12-04 00:09:11 -05:00
|
|
|
_runLock.Acquire();
|
2014-07-09 21:48:54 -04:00
|
|
|
}
|
2019-01-13 14:10:46 -05:00
|
|
|
|
|
|
|
if(_pauseOnNextFrameRequested) {
|
|
|
|
//Used by "Run Single Frame" option
|
|
|
|
_settings->SetFlags(EmulationFlags::Paused);
|
|
|
|
_pauseOnNextFrameRequested = false;
|
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
bool pausedRequired = _settings->NeedsPause();
|
|
|
|
if(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::GamePaused);
|
2017-12-04 00:09:11 -05:00
|
|
|
|
|
|
|
//Prevent audio from looping endlessly while game is paused
|
2018-07-01 15:21:05 -04:00
|
|
|
_soundMixer->StopAudio();
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_slave) {
|
|
|
|
_slave->_soundMixer->StopAudio();
|
|
|
|
}
|
2017-12-04 00:09:11 -05:00
|
|
|
|
|
|
|
_runLock.Release();
|
|
|
|
|
|
|
|
PlatformUtilities::EnableScreensaver();
|
2018-06-07 21:47:03 -04:00
|
|
|
PlatformUtilities::RestoreTimerResolution();
|
2018-07-13 22:19:26 -04:00
|
|
|
while(pausedRequired && !_stop && !_settings->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
|
2017-12-04 00:09:11 -05:00
|
|
|
//Sleep until emulation is resumed
|
|
|
|
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(30));
|
2018-07-13 22:19:26 -04:00
|
|
|
pausedRequired = _settings->NeedsPause();
|
|
|
|
_paused = true;
|
2017-12-04 00:09:11 -05:00
|
|
|
}
|
2018-07-13 22:19:26 -04:00
|
|
|
_paused = false;
|
|
|
|
|
2017-12-04 00:09:11 -05:00
|
|
|
PlatformUtilities::DisableScreensaver();
|
|
|
|
_runLock.Acquire();
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::GameResumed);
|
2018-06-09 14:03:53 -04:00
|
|
|
lastFrameTimer.Reset();
|
2018-08-26 18:53:15 -04:00
|
|
|
|
|
|
|
//Reset the timer to avoid speed up after a pause
|
|
|
|
_resetRunTimers = true;
|
2016-12-14 20:48:47 -05:00
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_settings->CheckFlag(EmulationFlags::UseHighResolutionTimer)) {
|
2018-06-07 21:47:03 -04:00
|
|
|
PlatformUtilities::EnableHighResolutionTimer();
|
|
|
|
} else {
|
|
|
|
PlatformUtilities::RestoreTimerResolution();
|
|
|
|
}
|
|
|
|
|
2017-12-04 00:09:11 -05:00
|
|
|
_systemActionManager->ProcessSystemActions();
|
2015-07-21 23:05:27 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
lastFrameNumber = _ppu->GetFrameCount();
|
2017-12-04 00:09:11 -05:00
|
|
|
|
|
|
|
if(_stop) {
|
|
|
|
_stop = false;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-06 19:54:47 -04:00
|
|
|
}
|
|
|
|
}
|
2017-12-04 00:09:11 -05:00
|
|
|
} catch(const std::runtime_error &ex) {
|
|
|
|
crashed = true;
|
2018-04-14 21:53:52 -04:00
|
|
|
_stopCode = -1;
|
2017-12-04 00:09:11 -05:00
|
|
|
MessageManager::DisplayMessage("Error", "GameCrash", ex.what());
|
2014-06-14 18:20:56 -04:00
|
|
|
}
|
2017-05-06 15:27:48 -04:00
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
_paused = false;
|
2018-04-10 17:05:54 -04:00
|
|
|
_running = false;
|
|
|
|
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::BeforeEmulationStop);
|
2018-04-10 17:05:54 -04:00
|
|
|
|
2017-05-06 15:27:48 -04:00
|
|
|
if(!crashed) {
|
2018-07-07 14:52:51 -04:00
|
|
|
_saveStateManager->SaveRecentGame(GetRomInfo().RomName, _romFilepath, _patchFilename);
|
2017-05-06 15:27:48 -04:00
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_videoDecoder->StopThread();
|
2017-08-09 17:44:52 -04:00
|
|
|
StopRecordingHdPack();
|
2018-07-01 15:21:05 -04:00
|
|
|
|
|
|
|
_soundMixer->StopAudio();
|
2017-04-18 22:39:45 -04:00
|
|
|
MovieManager::Stop();
|
2018-07-02 21:32:59 -04:00
|
|
|
_soundMixer->StopRecording();
|
2018-07-01 15:21:05 -04:00
|
|
|
|
2016-08-29 21:07:52 -04:00
|
|
|
PlatformUtilities::EnableScreensaver();
|
2018-06-07 21:47:03 -04:00
|
|
|
PlatformUtilities::RestoreTimerResolution();
|
2015-08-30 21:04:21 -04:00
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
|
2017-05-30 07:36:55 -04:00
|
|
|
|
2016-01-31 00:41:33 -05:00
|
|
|
_initialized = false;
|
2017-07-15 22:52:37 -04:00
|
|
|
|
|
|
|
if(!_romFilepath.empty() && _mapper) {
|
|
|
|
//Ensure we save any battery file before unloading anything
|
2017-11-19 23:08:23 -05:00
|
|
|
SaveBatteries();
|
2017-07-15 22:52:37 -04:00
|
|
|
}
|
|
|
|
|
2016-01-28 22:34:23 -05:00
|
|
|
_romFilepath = "";
|
2018-04-04 23:35:41 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
Release(false);
|
|
|
|
|
2015-07-05 19:05:33 -04:00
|
|
|
_stopLock.Release();
|
|
|
|
_runLock.Release();
|
2017-08-05 14:24:44 -04:00
|
|
|
|
2017-10-07 13:01:42 -04:00
|
|
|
_emulationThreadId = std::thread::id();
|
|
|
|
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::GameStopped);
|
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::EmulationStopped);
|
2014-06-25 21:52:37 -04:00
|
|
|
}
|
|
|
|
|
2018-08-26 18:53:15 -04:00
|
|
|
void Console::ResetRunTimers()
|
|
|
|
{
|
|
|
|
_resetRunTimers = true;
|
|
|
|
}
|
|
|
|
|
2016-02-14 12:58:35 -05:00
|
|
|
bool Console::IsRunning()
|
|
|
|
{
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_master) {
|
|
|
|
//For slave CPU, return the master's state
|
|
|
|
return _master->IsRunning();
|
|
|
|
} else {
|
|
|
|
return !_stopLock.IsFree() && _running;
|
|
|
|
}
|
2017-10-03 18:18:29 -04:00
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
bool Console::IsExecutionStopped()
|
2017-10-03 18:18:29 -04:00
|
|
|
{
|
2018-07-02 21:32:59 -04:00
|
|
|
if(_master) {
|
|
|
|
//For slave CPU, return the master's state
|
|
|
|
return _master->IsPaused();
|
|
|
|
} else {
|
2018-07-22 16:17:15 -04:00
|
|
|
return _runLock.IsFree() || (!_runLock.IsFree() && _pauseCounter > 0) || !_running;
|
2018-07-02 21:32:59 -04:00
|
|
|
}
|
2016-02-14 12:58:35 -05:00
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
bool Console::IsPaused()
|
2018-07-11 18:07:13 -04:00
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_master) {
|
|
|
|
return _master->_paused;
|
|
|
|
} else {
|
|
|
|
return _paused;
|
|
|
|
}
|
2018-07-11 18:07:13 -04:00
|
|
|
}
|
|
|
|
|
2019-01-13 14:10:46 -05:00
|
|
|
void Console::PauseOnNextFrame()
|
|
|
|
{
|
|
|
|
_pauseOnNextFrameRequested = true;
|
|
|
|
}
|
|
|
|
|
2016-07-10 19:15:00 -04:00
|
|
|
void Console::UpdateNesModel(bool sendNotification)
|
2015-07-21 23:05:27 -04:00
|
|
|
{
|
2016-02-05 23:14:27 -05:00
|
|
|
bool configChanged = false;
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_settings->NeedControllerUpdate()) {
|
2016-02-05 23:14:27 -05:00
|
|
|
_controlManager->UpdateControlDevices();
|
|
|
|
configChanged = true;
|
|
|
|
}
|
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
NesModel model = _settings->GetNesModel();
|
2015-07-21 23:05:27 -04:00
|
|
|
if(model == NesModel::Auto) {
|
2018-07-07 14:52:51 -04:00
|
|
|
switch(_mapper->GetRomInfo().System) {
|
2016-06-15 21:59:34 -04:00
|
|
|
case GameSystem::NesPal: model = NesModel::PAL; break;
|
|
|
|
case GameSystem::Dendy: model = NesModel::Dendy; break;
|
|
|
|
default: model = NesModel::NTSC; break;
|
|
|
|
}
|
2015-07-21 23:05:27 -04:00
|
|
|
}
|
2016-02-05 23:14:27 -05:00
|
|
|
if(_model != model) {
|
|
|
|
_model = model;
|
|
|
|
configChanged = true;
|
2016-07-10 19:15:00 -04:00
|
|
|
|
|
|
|
if(sendNotification) {
|
|
|
|
MessageManager::DisplayMessage("Region", model == NesModel::PAL ? "PAL" : (model == NesModel::Dendy ? "Dendy" : "NTSC"));
|
|
|
|
}
|
2016-02-05 23:14:27 -05:00
|
|
|
}
|
2016-07-10 19:15:00 -04:00
|
|
|
|
2019-11-10 17:35:29 -05:00
|
|
|
_cpu->SetMasterClockDivider(model);
|
2016-07-10 19:15:00 -04:00
|
|
|
_mapper->SetNesModel(model);
|
|
|
|
_ppu->SetNesModel(model);
|
|
|
|
_apu->SetNesModel(model);
|
|
|
|
|
|
|
|
if(configChanged && sendNotification) {
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::ConfigChanged);
|
2016-07-10 19:15:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
double Console::GetFrameDelay()
|
|
|
|
{
|
2018-07-13 22:19:26 -04:00
|
|
|
uint32_t emulationSpeed = _settings->GetEmulationSpeed();
|
2016-01-23 00:52:06 -05:00
|
|
|
double frameDelay;
|
2015-08-24 20:27:07 -04:00
|
|
|
if(emulationSpeed == 0) {
|
2015-08-23 20:24:24 -04:00
|
|
|
frameDelay = 0;
|
|
|
|
} else {
|
2016-01-30 19:33:32 -05:00
|
|
|
//60.1fps (NTSC), 50.01fps (PAL/Dendy)
|
2016-07-10 19:15:00 -04:00
|
|
|
switch(_model) {
|
2016-06-02 23:56:11 -04:00
|
|
|
default:
|
2018-07-13 22:19:26 -04:00
|
|
|
case NesModel::NTSC: frameDelay = _settings->CheckFlag(EmulationFlags::IntegerFpsMode) ? 16.6666666666666666667 : 16.63926405550947; break;
|
2016-01-30 19:33:32 -05:00
|
|
|
case NesModel::PAL:
|
2018-07-13 22:19:26 -04:00
|
|
|
case NesModel::Dendy: frameDelay = _settings->CheckFlag(EmulationFlags::IntegerFpsMode) ? 20 : 19.99720920217466; break;
|
2016-01-30 19:33:32 -05:00
|
|
|
}
|
2015-08-24 20:27:07 -04:00
|
|
|
frameDelay /= (double)emulationSpeed / 100.0;
|
2015-08-23 20:24:24 -04:00
|
|
|
}
|
2015-08-24 20:27:07 -04:00
|
|
|
|
2016-01-23 00:52:06 -05:00
|
|
|
return frameDelay;
|
2015-07-21 23:05:27 -04:00
|
|
|
}
|
|
|
|
|
2014-07-01 12:44:01 -04:00
|
|
|
void Console::SaveState(ostream &saveStream)
|
2014-06-25 21:52:37 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_initialized) {
|
|
|
|
_cpu->SaveSnapshot(&saveStream);
|
|
|
|
_ppu->SaveSnapshot(&saveStream);
|
|
|
|
_memoryManager->SaveSnapshot(&saveStream);
|
|
|
|
_apu->SaveSnapshot(&saveStream);
|
|
|
|
_controlManager->SaveSnapshot(&saveStream);
|
|
|
|
_mapper->SaveSnapshot(&saveStream);
|
|
|
|
if(_hdAudioDevice) {
|
|
|
|
_hdAudioDevice->SaveSnapshot(&saveStream);
|
2017-08-20 19:03:16 -04:00
|
|
|
} else {
|
|
|
|
Snapshotable::WriteEmptyBlock(&saveStream);
|
|
|
|
}
|
2018-07-02 21:32:59 -04:00
|
|
|
|
|
|
|
if(_slave) {
|
|
|
|
//For VS Dualsystem, append the 2nd console's savestate
|
|
|
|
_slave->SaveState(saveStream);
|
|
|
|
}
|
2014-07-06 19:54:47 -04:00
|
|
|
}
|
2014-07-01 12:44:01 -04:00
|
|
|
}
|
2014-06-25 21:52:37 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
void Console::LoadState(istream &loadStream)
|
|
|
|
{
|
|
|
|
LoadState(loadStream, SaveStateManager::FileFormatVersion);
|
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
void Console::LoadState(istream &loadStream, uint32_t stateVersion)
|
2014-07-01 12:44:01 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_initialized) {
|
|
|
|
_cpu->LoadSnapshot(&loadStream, stateVersion);
|
|
|
|
_ppu->LoadSnapshot(&loadStream, stateVersion);
|
|
|
|
_memoryManager->LoadSnapshot(&loadStream, stateVersion);
|
|
|
|
_apu->LoadSnapshot(&loadStream, stateVersion);
|
|
|
|
_controlManager->LoadSnapshot(&loadStream, stateVersion);
|
|
|
|
_mapper->LoadSnapshot(&loadStream, stateVersion);
|
|
|
|
if(_hdAudioDevice) {
|
|
|
|
_hdAudioDevice->LoadSnapshot(&loadStream, stateVersion);
|
2017-08-20 19:03:16 -04:00
|
|
|
} else {
|
|
|
|
Snapshotable::SkipBlock(&loadStream);
|
|
|
|
}
|
2018-07-02 21:32:59 -04:00
|
|
|
|
|
|
|
if(_slave) {
|
|
|
|
//For VS Dualsystem, the slave console's savestate is appended to the end of the file
|
|
|
|
_slave->LoadState(loadStream, stateVersion);
|
|
|
|
}
|
2014-07-09 21:11:02 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
2017-10-05 20:48:18 -04:00
|
|
|
if(debugger) {
|
|
|
|
debugger->ResetCounters();
|
|
|
|
}
|
|
|
|
|
2018-07-30 21:05:13 -04:00
|
|
|
_debugHud->ClearScreen();
|
2018-07-02 14:49:19 -04:00
|
|
|
_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
|
2018-08-26 18:53:15 -04:00
|
|
|
UpdateNesModel(false);
|
2014-07-06 19:54:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
|
|
|
|
{
|
2017-04-28 19:54:58 -04:00
|
|
|
//Send any unprocessed sound to the SoundMixer - needed for rewind
|
2018-07-01 15:21:05 -04:00
|
|
|
_apu->EndFrame();
|
2017-04-28 19:54:58 -04:00
|
|
|
|
2014-07-06 19:54:47 -04:00
|
|
|
stringstream stream;
|
|
|
|
stream.write((char*)buffer, bufferSize);
|
|
|
|
stream.seekg(0, ios::beg);
|
|
|
|
LoadState(stream);
|
2014-06-25 21:52:37 -04:00
|
|
|
}
|
|
|
|
|
2016-08-25 19:02:33 -04:00
|
|
|
std::shared_ptr<Debugger> Console::GetDebugger(bool autoStart)
|
2015-06-24 19:26:19 -04:00
|
|
|
{
|
2017-11-19 23:08:23 -05:00
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
|
|
|
if(!debugger && autoStart) {
|
2017-12-28 16:30:37 -05:00
|
|
|
//Lock to make sure we don't try to start debuggers in 2 separate threads at once
|
|
|
|
auto lock = _debuggerLock.AcquireSafe();
|
|
|
|
debugger = _debugger;
|
|
|
|
if(!debugger) {
|
2018-07-01 15:21:05 -04:00
|
|
|
debugger.reset(new Debugger(shared_from_this(), _cpu, _ppu, _apu, _memoryManager, _mapper));
|
2017-12-28 16:30:37 -05:00
|
|
|
_debugger = debugger;
|
|
|
|
}
|
2015-08-21 22:42:44 -04:00
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
return debugger;
|
2015-06-24 19:26:19 -04:00
|
|
|
}
|
2015-08-21 22:42:44 -04:00
|
|
|
|
|
|
|
void Console::StopDebugger()
|
|
|
|
{
|
2019-01-27 14:31:05 -05:00
|
|
|
if(_debugger) {
|
|
|
|
_debugger->ReleaseDebugger(_running);
|
|
|
|
}
|
2015-08-21 22:42:44 -04:00
|
|
|
_debugger.reset();
|
2016-02-05 23:14:27 -05:00
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
std::thread::id Console::GetEmulationThreadId()
|
2016-07-10 18:22:37 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _emulationThreadId;
|
2016-07-10 18:22:37 -04:00
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
uint32_t Console::GetLagCounter()
|
2017-10-07 13:01:42 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _controlManager->GetLagCounter();
|
2017-10-07 13:01:42 -04:00
|
|
|
}
|
|
|
|
|
2016-07-10 18:22:37 -04:00
|
|
|
void Console::ResetLagCounter()
|
|
|
|
{
|
2018-08-01 20:20:49 -04:00
|
|
|
Pause();
|
2018-07-01 15:21:05 -04:00
|
|
|
_controlManager->ResetLagCounter();
|
2018-08-01 20:20:49 -04:00
|
|
|
Resume();
|
2016-11-26 18:04:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Console::IsDebuggerAttached()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return (bool)_debugger;
|
2016-12-23 13:56:45 -05:00
|
|
|
}
|
|
|
|
|
2017-04-29 16:11:22 -04:00
|
|
|
void Console::SetNextFrameOverclockStatus(bool disabled)
|
2016-12-23 13:56:45 -05:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
_disableOcNextFrame = disabled;
|
2017-04-29 16:11:22 -04:00
|
|
|
}
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2018-04-14 21:53:52 -04:00
|
|
|
int32_t Console::GetStopCode()
|
|
|
|
{
|
|
|
|
return _stopCode;
|
|
|
|
}
|
|
|
|
|
2019-06-22 16:33:59 -04:00
|
|
|
void Console::InitializeRam(void* data, uint32_t length)
|
|
|
|
{
|
|
|
|
InitializeRam(_settings->GetRamPowerOnState(), data, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::InitializeRam(RamPowerOnState powerOnState, void* data, uint32_t length)
|
|
|
|
{
|
|
|
|
switch(powerOnState) {
|
|
|
|
default:
|
|
|
|
case RamPowerOnState::AllZeros: memset(data, 0, length); break;
|
|
|
|
case RamPowerOnState::AllOnes: memset(data, 0xFF, length); break;
|
|
|
|
case RamPowerOnState::Random:
|
|
|
|
std::random_device rd;
|
|
|
|
std::mt19937 mt(rd());
|
|
|
|
std::uniform_int_distribution<> dist(0, 255);
|
|
|
|
for(uint32_t i = 0; i < length; i++) {
|
|
|
|
((uint8_t*)data)[i] = dist(mt);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
shared_ptr<HdPackData> Console::GetHdData()
|
2017-06-28 19:00:08 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _hdData;
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
|
|
|
|
2017-08-19 17:51:36 -04:00
|
|
|
bool Console::IsHdPpu()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
return _hdData && std::dynamic_pointer_cast<HdPpu>(_ppu) != nullptr;
|
2017-08-19 17:51:36 -04:00
|
|
|
}
|
|
|
|
|
2017-07-30 09:03:54 -04:00
|
|
|
void Console::LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile)
|
2017-06-28 19:00:08 -04:00
|
|
|
{
|
|
|
|
_hdData.reset();
|
2018-07-13 22:19:26 -04:00
|
|
|
if(_settings->CheckFlag(EmulationFlags::UseHdPacks)) {
|
2017-06-28 19:00:08 -04:00
|
|
|
_hdData.reset(new HdPackData());
|
2017-07-30 09:03:54 -04:00
|
|
|
if(!HdPackLoader::LoadHdNesPack(romFile, *_hdData.get())) {
|
2017-06-28 19:00:08 -04:00
|
|
|
_hdData.reset();
|
|
|
|
} else {
|
2017-07-30 09:03:54 -04:00
|
|
|
auto result = _hdData->PatchesByHash.find(romFile.GetSha1Hash());
|
2017-06-28 19:00:08 -04:00
|
|
|
if(result != _hdData->PatchesByHash.end()) {
|
2017-07-30 09:03:54 -04:00
|
|
|
patchFile = result->second;
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::StartRecordingHdPack(string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize)
|
|
|
|
{
|
2018-09-14 22:09:51 -04:00
|
|
|
ConsolePauseHelper helper(this);
|
|
|
|
|
2017-06-28 19:00:08 -04:00
|
|
|
std::stringstream saveState;
|
2018-07-01 15:21:05 -04:00
|
|
|
SaveState(saveState);
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_hdPackBuilder.reset();
|
|
|
|
_hdPackBuilder.reset(new HdPackBuilder(shared_from_this(), saveFolder, filterType, scale, flags, chrRamBankSize, !_mapper->HasChrRom()));
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager->UnregisterIODevice(_ppu.get());
|
|
|
|
_ppu.reset();
|
2018-09-13 20:58:35 -04:00
|
|
|
_ppu.reset(new HdBuilderPpu(shared_from_this(), _hdPackBuilder.get(), chrRamBankSize, _hdData));
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2018-09-07 21:12:30 -04:00
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
|
|
|
if(debugger) {
|
|
|
|
debugger->SetPpu(_ppu);
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
LoadState(saveState);
|
2018-09-14 22:09:51 -04:00
|
|
|
|
|
|
|
_soundMixer->StopAudio();
|
2017-06-28 19:00:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::StopRecordingHdPack()
|
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_hdPackBuilder) {
|
2018-09-14 22:09:51 -04:00
|
|
|
ConsolePauseHelper helper(this);
|
|
|
|
|
2017-08-09 17:44:52 -04:00
|
|
|
std::stringstream saveState;
|
2018-07-01 15:21:05 -04:00
|
|
|
SaveState(saveState);
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager->UnregisterIODevice(_ppu.get());
|
|
|
|
_ppu.reset();
|
2018-09-13 20:58:35 -04:00
|
|
|
if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
|
|
|
|
_ppu.reset(new HdPpu(shared_from_this(), _hdData.get()));
|
|
|
|
} else {
|
|
|
|
_ppu.reset(new PPU(shared_from_this()));
|
|
|
|
}
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
|
|
|
_hdPackBuilder.reset();
|
2017-06-28 19:00:08 -04:00
|
|
|
|
2018-09-07 21:12:30 -04:00
|
|
|
shared_ptr<Debugger> debugger = _debugger;
|
|
|
|
if(debugger) {
|
|
|
|
debugger->SetPpu(_ppu);
|
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
LoadState(saveState);
|
2018-09-14 22:09:51 -04:00
|
|
|
|
|
|
|
_soundMixer->StopAudio();
|
2017-08-09 17:44:52 -04:00
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
|
2018-01-04 19:03:47 -05:00
|
|
|
bool Console::UpdateHdPackMode()
|
|
|
|
{
|
|
|
|
//Switch back and forth between HD PPU and regular PPU as needed
|
2018-08-01 20:20:49 -04:00
|
|
|
Pause();
|
2018-01-04 19:03:47 -05:00
|
|
|
|
|
|
|
VirtualFile romFile = _romFilepath;
|
|
|
|
VirtualFile patchFile = _patchFilename;
|
|
|
|
LoadHdPack(romFile, patchFile);
|
|
|
|
|
|
|
|
bool isHdPackLoaded = std::dynamic_pointer_cast<HdPpu>(_ppu) != nullptr;
|
|
|
|
bool hdPackFound = _hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty());
|
|
|
|
|
|
|
|
bool modeChanged = false;
|
|
|
|
if(isHdPackLoaded != hdPackFound) {
|
|
|
|
std::stringstream saveState;
|
2018-07-01 15:21:05 -04:00
|
|
|
SaveState(saveState);
|
2018-01-04 19:03:47 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager->UnregisterIODevice(_ppu.get());
|
|
|
|
_ppu.reset();
|
2018-01-04 19:03:47 -05:00
|
|
|
if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_ppu.reset(new HdPpu(shared_from_this(), _hdData.get()));
|
2018-01-04 19:03:47 -05:00
|
|
|
} else if(std::dynamic_pointer_cast<NsfMapper>(_mapper)) {
|
|
|
|
//Disable most of the PPU for NSFs
|
2018-07-01 15:21:05 -04:00
|
|
|
_ppu.reset(new NsfPpu(shared_from_this()));
|
2018-01-04 19:03:47 -05:00
|
|
|
} else {
|
2018-07-01 15:21:05 -04:00
|
|
|
_ppu.reset(new PPU(shared_from_this()));
|
2018-01-04 19:03:47 -05:00
|
|
|
}
|
2018-07-01 15:21:05 -04:00
|
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
2018-01-04 19:03:47 -05:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
LoadState(saveState);
|
2018-01-04 19:03:47 -05:00
|
|
|
modeChanged = true;
|
|
|
|
}
|
|
|
|
|
2018-08-01 20:20:49 -04:00
|
|
|
Resume();
|
2018-01-04 19:03:47 -05:00
|
|
|
|
|
|
|
return modeChanged;
|
|
|
|
}
|
|
|
|
|
2019-02-08 19:39:35 -05:00
|
|
|
uint32_t Console::GetDipSwitchCount()
|
|
|
|
{
|
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
|
|
|
shared_ptr<BaseMapper> mapper = _mapper;
|
|
|
|
|
|
|
|
if(std::dynamic_pointer_cast<VsControlManager>(controlManager)) {
|
|
|
|
return IsDualSystem() ? 16 : 8;
|
|
|
|
} else if(mapper) {
|
|
|
|
return mapper->GetMapperDipSwitchCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-19 23:08:23 -05:00
|
|
|
ConsoleFeatures Console::GetAvailableFeatures()
|
|
|
|
{
|
|
|
|
ConsoleFeatures features = ConsoleFeatures::None;
|
2018-06-16 10:03:49 -04:00
|
|
|
shared_ptr<BaseMapper> mapper = _mapper;
|
2018-06-16 15:47:06 -04:00
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
2018-06-16 10:03:49 -04:00
|
|
|
if(mapper && controlManager) {
|
|
|
|
features = (ConsoleFeatures)((int)features | (int)mapper->GetAvailableFeatures());
|
2017-11-19 23:08:23 -05:00
|
|
|
|
2018-06-16 10:03:49 -04:00
|
|
|
if(dynamic_cast<VsControlManager*>(controlManager.get())) {
|
2017-11-19 23:08:23 -05:00
|
|
|
features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::VsSystem);
|
|
|
|
}
|
|
|
|
|
2018-06-16 10:03:49 -04:00
|
|
|
if(std::dynamic_pointer_cast<IBarcodeReader>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort))) {
|
2017-11-19 23:08:23 -05:00
|
|
|
features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::BarcodeReader);
|
|
|
|
}
|
2017-11-24 21:38:12 -05:00
|
|
|
|
2018-06-16 10:03:49 -04:00
|
|
|
if(std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2))) {
|
2017-11-24 21:38:12 -05:00
|
|
|
features = (ConsoleFeatures)((int)features | (int)ConsoleFeatures::TapeRecorder);
|
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
return features;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::InputBarcode(uint64_t barcode, uint32_t digitCount)
|
|
|
|
{
|
2018-06-16 10:03:49 -04:00
|
|
|
shared_ptr<BaseMapper> mapper = _mapper;
|
2018-06-16 15:47:06 -04:00
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
2018-06-16 10:03:49 -04:00
|
|
|
|
|
|
|
if(mapper) {
|
|
|
|
shared_ptr<IBarcodeReader> barcodeReader = std::dynamic_pointer_cast<IBarcodeReader>(mapper->GetMapperControlDevice());
|
|
|
|
if(barcodeReader) {
|
|
|
|
barcodeReader->InputBarcode(barcode, digitCount);
|
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
|
|
|
|
2018-06-16 10:03:49 -04:00
|
|
|
if(controlManager) {
|
|
|
|
shared_ptr<IBarcodeReader> barcodeReader = std::dynamic_pointer_cast<IBarcodeReader>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort));
|
|
|
|
if(barcodeReader) {
|
|
|
|
barcodeReader->InputBarcode(barcode, digitCount);
|
|
|
|
}
|
2017-11-19 23:08:23 -05:00
|
|
|
}
|
2017-11-24 21:38:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::LoadTapeFile(string filepath)
|
|
|
|
{
|
2018-06-16 10:03:49 -04:00
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
|
|
|
if(controlManager) {
|
|
|
|
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
|
|
|
|
if(dataRecorder) {
|
2018-08-01 20:20:49 -04:00
|
|
|
Pause();
|
2018-06-16 10:03:49 -04:00
|
|
|
dataRecorder->LoadFromFile(filepath);
|
2018-08-01 20:20:49 -04:00
|
|
|
Resume();
|
2018-06-16 10:03:49 -04:00
|
|
|
}
|
2017-11-24 21:38:12 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::StartRecordingTapeFile(string filepath)
|
|
|
|
{
|
2018-06-16 10:03:49 -04:00
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
|
|
|
if(controlManager) {
|
|
|
|
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
|
|
|
|
if(dataRecorder) {
|
2018-08-01 20:20:49 -04:00
|
|
|
Pause();
|
2018-06-16 10:03:49 -04:00
|
|
|
dataRecorder->StartRecording(filepath);
|
2018-08-01 20:20:49 -04:00
|
|
|
Resume();
|
2018-06-16 10:03:49 -04:00
|
|
|
}
|
2017-11-24 21:38:12 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::StopRecordingTapeFile()
|
|
|
|
{
|
2018-06-16 10:03:49 -04:00
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
|
|
|
if(controlManager) {
|
|
|
|
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
|
|
|
|
if(dataRecorder) {
|
2018-08-01 20:20:49 -04:00
|
|
|
Pause();
|
2018-06-16 10:03:49 -04:00
|
|
|
dataRecorder->StopRecording();
|
2018-08-01 20:20:49 -04:00
|
|
|
Resume();
|
2018-06-16 10:03:49 -04:00
|
|
|
}
|
2017-11-24 21:38:12 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Console::IsRecordingTapeFile()
|
|
|
|
{
|
2018-06-16 10:03:49 -04:00
|
|
|
shared_ptr<ControlManager> controlManager = _controlManager;
|
|
|
|
if(controlManager) {
|
|
|
|
shared_ptr<FamilyBasicDataRecorder> dataRecorder = std::dynamic_pointer_cast<FamilyBasicDataRecorder>(controlManager->GetControlDevice(BaseControlDevice::ExpDevicePort2));
|
|
|
|
if(dataRecorder) {
|
|
|
|
return dataRecorder->IsRecording();
|
|
|
|
}
|
2017-11-24 21:38:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2018-01-07 01:22:28 -05:00
|
|
|
}
|
|
|
|
|
2018-12-26 13:28:17 -05:00
|
|
|
bool Console::IsNsf()
|
|
|
|
{
|
|
|
|
return std::dynamic_pointer_cast<NsfMapper>(_mapper) != nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-11 18:07:13 -04:00
|
|
|
void Console::CopyRewindData(shared_ptr<Console> sourceConsole)
|
|
|
|
{
|
|
|
|
sourceConsole->Pause();
|
|
|
|
Pause();
|
|
|
|
|
2018-07-22 17:31:50 -04:00
|
|
|
//Disable battery saving for this instance
|
|
|
|
_batteryManager->SetSaveEnabled(false);
|
2018-07-11 18:07:13 -04:00
|
|
|
_historyViewer.reset(new HistoryViewer(shared_from_this()));
|
|
|
|
sourceConsole->_rewindManager->CopyHistory(_historyViewer);
|
|
|
|
|
|
|
|
Resume();
|
|
|
|
sourceConsole->Resume();
|
|
|
|
}
|
|
|
|
|
2018-03-29 20:50:52 -04:00
|
|
|
uint8_t* Console::GetRamBuffer(DebugMemoryType memoryType, uint32_t &size, int32_t &startAddr)
|
2018-01-07 01:22:28 -05:00
|
|
|
{
|
|
|
|
//Only used by libretro port for achievements - should not be used by anything else.
|
|
|
|
switch(memoryType) {
|
2018-06-25 15:56:05 -04:00
|
|
|
default: break;
|
|
|
|
|
2018-01-07 01:22:28 -05:00
|
|
|
case DebugMemoryType::InternalRam:
|
|
|
|
size = MemoryManager::InternalRAMSize;
|
2018-03-29 20:50:52 -04:00
|
|
|
startAddr = 0;
|
2018-01-07 01:22:28 -05:00
|
|
|
return _memoryManager->GetInternalRAM();
|
|
|
|
|
|
|
|
case DebugMemoryType::SaveRam:
|
|
|
|
size = _mapper->GetMemorySize(DebugMemoryType::SaveRam);
|
2018-03-29 20:50:52 -04:00
|
|
|
startAddr = _mapper->FromAbsoluteAddress(0, AddressType::SaveRam);
|
2018-01-07 01:22:28 -05:00
|
|
|
return _mapper->GetSaveRam();
|
2018-03-29 20:50:52 -04:00
|
|
|
|
|
|
|
case DebugMemoryType::WorkRam:
|
|
|
|
size = _mapper->GetMemorySize(DebugMemoryType::WorkRam);
|
|
|
|
startAddr = _mapper->FromAbsoluteAddress(0, AddressType::WorkRam);
|
|
|
|
return _mapper->GetWorkRam();
|
2018-01-07 01:22:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
throw std::runtime_error("unsupported memory type");
|
2018-06-09 14:03:53 -04:00
|
|
|
}
|
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
void Console::DebugAddTrace(const char * log)
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_debugger) {
|
|
|
|
_debugger->AddTrace(log);
|
|
|
|
}
|
2018-07-08 00:27:46 -04:00
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::DebugProcessPpuCycle()
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2019-02-09 15:32:35 -05:00
|
|
|
if(_debugger && _debugger->IsPpuCycleToProcess()) {
|
2018-07-01 15:21:05 -04:00
|
|
|
_debugger->ProcessPpuCycle();
|
|
|
|
}
|
2018-07-08 00:27:46 -04:00
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::DebugProcessEvent(EventType type)
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_debugger) {
|
|
|
|
_debugger->ProcessEvent(type);
|
|
|
|
}
|
2018-07-08 00:27:46 -04:00
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::DebugProcessInterrupt(uint16_t cpuAddr, uint16_t destCpuAddr, bool forNmi)
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_debugger) {
|
|
|
|
_debugger->ProcessInterrupt(cpuAddr, destCpuAddr, forNmi);
|
|
|
|
}
|
2018-07-08 00:27:46 -04:00
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::DebugSetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool updateHorizontalScrollOnly)
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_debugger) {
|
|
|
|
_debugger->SetLastFramePpuScroll(addr, xScroll, updateHorizontalScrollOnly);
|
|
|
|
}
|
2018-07-08 00:27:46 -04:00
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Console::DebugProcessRamOperation(MemoryOperationType type, uint16_t & addr, uint8_t & value)
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_debugger) {
|
|
|
|
return _debugger->ProcessRamOperation(type, addr, value);
|
|
|
|
}
|
|
|
|
return true;
|
2018-07-08 00:27:46 -04:00
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::DebugProcessVramReadOperation(MemoryOperationType type, uint16_t addr, uint8_t & value)
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_debugger) {
|
|
|
|
_debugger->ProcessVramReadOperation(type, addr, value);
|
|
|
|
}
|
2018-07-08 00:27:46 -04:00
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::DebugProcessVramWriteOperation(uint16_t addr, uint8_t & value)
|
|
|
|
{
|
2018-07-08 00:27:46 -04:00
|
|
|
#ifndef LIBRETRO
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_debugger) {
|
|
|
|
_debugger->ProcessVramWriteOperation(addr, value);
|
|
|
|
}
|
2018-07-08 00:27:46 -04:00
|
|
|
#endif
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
2019-02-01 14:15:27 -05:00
|
|
|
void Console::DisplayDebugInformation(double lastFrame, double &lastFrameMin, double &lastFrameMax, double frameDurations[60])
|
2018-06-09 14:03:53 -04:00
|
|
|
{
|
2018-07-01 15:21:05 -04:00
|
|
|
AudioStatistics stats = _soundMixer->GetStatistics();
|
|
|
|
|
|
|
|
int startFrame = _ppu->GetFrameCount();
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2018-08-02 22:09:55 -04:00
|
|
|
_debugHud->DrawRectangle(8, 8, 115, 49, 0x40000000, true, 1, startFrame);
|
|
|
|
_debugHud->DrawRectangle(8, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
|
2018-07-01 15:21:05 -04:00
|
|
|
|
|
|
|
_debugHud->DrawString(10, 10, "Audio Stats", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
|
|
|
_debugHud->DrawString(10, 21, "Latency: ", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2018-07-13 22:19:26 -04:00
|
|
|
int color = (stats.AverageLatency > 0 && std::abs(stats.AverageLatency - _settings->GetAudioLatency()) > 3) ? 0xFF0000 : 0xFFFFFF;
|
2018-06-09 14:03:53 -04:00
|
|
|
std::stringstream ss;
|
|
|
|
ss << std::fixed << std::setprecision(2) << stats.AverageLatency << " ms";
|
2018-07-01 15:21:05 -04:00
|
|
|
_debugHud->DrawString(54, 21, ss.str(), color, 0xFF000000, 1, startFrame);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
_debugHud->DrawString(10, 30, "Underruns: " + std::to_string(stats.BufferUnderrunEventCount), 0xFFFFFF, 0xFF000000, 1, startFrame);
|
|
|
|
_debugHud->DrawString(10, 39, "Buffer Size: " + std::to_string(stats.BufferSize / 1024) + "kb", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-08-02 22:09:55 -04:00
|
|
|
_debugHud->DrawString(10, 48, "Rate: " + std::to_string((uint32_t)(_settings->GetSampleRate() * _soundMixer->GetRateAdjustment())) + "Hz", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2018-08-26 18:53:15 -04:00
|
|
|
_debugHud->DrawRectangle(132, 8, 115, 49, 0x40000000, true, 1, startFrame);
|
|
|
|
_debugHud->DrawRectangle(132, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
|
|
|
|
_debugHud->DrawString(134, 10, "Video Stats", 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2019-02-01 14:15:27 -05:00
|
|
|
double totalDuration = 0;
|
|
|
|
for(int i = 0; i < 60; i++) {
|
|
|
|
totalDuration += frameDurations[i];
|
|
|
|
}
|
|
|
|
|
2018-06-09 14:03:53 -04:00
|
|
|
ss = std::stringstream();
|
2019-02-01 14:15:27 -05:00
|
|
|
ss << "FPS: " << std::fixed << std::setprecision(4) << (1000 / (totalDuration/60));
|
2018-08-26 18:53:15 -04:00
|
|
|
_debugHud->DrawString(134, 21, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
|
|
|
ss = std::stringstream();
|
|
|
|
ss << "Last Frame: " << std::fixed << std::setprecision(2) << lastFrame << " ms";
|
2018-08-26 18:53:15 -04:00
|
|
|
_debugHud->DrawString(134, 30, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
2018-07-01 15:21:05 -04:00
|
|
|
if(_ppu->GetFrameCount() > 60) {
|
2018-06-09 14:03:53 -04:00
|
|
|
lastFrameMin = (std::min)(lastFrame, lastFrameMin);
|
|
|
|
lastFrameMax = (std::max)(lastFrame, lastFrameMax);
|
|
|
|
} else {
|
|
|
|
lastFrameMin = 9999;
|
|
|
|
lastFrameMax = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ss = std::stringstream();
|
|
|
|
ss << "Min Delay: " << std::fixed << std::setprecision(2) << ((lastFrameMin < 9999) ? lastFrameMin : 0.0) << " ms";
|
2018-08-26 18:53:15 -04:00
|
|
|
_debugHud->DrawString(134, 39, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-06-09 14:03:53 -04:00
|
|
|
|
|
|
|
ss = std::stringstream();
|
|
|
|
ss << "Max Delay: " << std::fixed << std::setprecision(2) << lastFrameMax << " ms";
|
2018-08-26 18:53:15 -04:00
|
|
|
_debugHud->DrawString(134, 48, ss.str(), 0xFFFFFF, 0xFF000000, 1, startFrame);
|
2018-07-01 15:21:05 -04:00
|
|
|
}
|
|
|
|
|
2018-12-31 14:59:00 -05:00
|
|
|
void Console::ExportStub()
|
|
|
|
{
|
|
|
|
//Force the compiler to export the PgoRunTest function - otherwise it seems to be ignored since it is unused
|
|
|
|
vector<string> testRoms;
|
|
|
|
PgoRunTest(testRoms, true);
|
|
|
|
}
|