diff --git a/Core/Console.cpp b/Core/Console.cpp index 9a9f3c1d..34e60547 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -435,6 +435,8 @@ void Console::Run() UpdateNesModel(true); + _running = true; + bool crashed = false; try { while(true) { @@ -511,6 +513,10 @@ void Console::Run() MessageManager::DisplayMessage("Error", "GameCrash", ex.what()); } + _running = false; + + MessageManager::SendNotification(ConsoleNotificationType::BeforeEmulationStop); + if(!crashed) { SaveStateManager::SaveRecentGame(GetMapperInfo().RomName, _romFilepath, _patchFilename); } @@ -559,12 +565,12 @@ void Console::Run() bool Console::IsRunning() { - return !Instance->_stopLock.IsFree(); + return !Instance->_stopLock.IsFree() && Instance->_running; } bool Console::IsPaused() { - return _runLock.IsFree() || !_pauseLock.IsFree(); + return _runLock.IsFree() || !_pauseLock.IsFree() || !_running; } void Console::UpdateNesModel(bool sendNotification) diff --git a/Core/Console.h b/Core/Console.h index bb1b9e71..d6b9f868 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -57,6 +57,7 @@ class Console string _patchFilename; bool _stop = false; + bool _running = false; bool _disableOcNextFrame = false; diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 72c8dad1..660339b6 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -79,9 +79,6 @@ Debugger::Debugger(shared_ptr console, shared_ptr cpu, shared_ptr< _ppuScrollX = 0; _ppuScrollY = 0; - _ppuViewerScanline = 241; - _ppuViewerCycle = 0; - _flags = 0; _runToCycle = 0; @@ -104,6 +101,8 @@ Debugger::Debugger(shared_ptr console, shared_ptr cpu, shared_ptr< _hasScript = false; _nextScriptId = 0; + UpdatePpuCyclesToProcess(); + Debugger::Instance = this; } @@ -468,21 +467,27 @@ void Debugger::ProcessStepConditions(uint16_t addr) void Debugger::PrivateProcessPpuCycle() { - if(PPU::GetCurrentCycle() == (uint32_t)_ppuViewerCycle && PPU::GetCurrentScanline() == _ppuViewerScanline) { - MessageManager::SendNotification(ConsoleNotificationType::PpuViewerDisplayFrame); - } - if(PPU::GetCurrentCycle() == 0) { - if(_breakOnScanline == PPU::GetCurrentScanline()) { - Step(1); - SleepUntilResume(BreakSource::Pause); + if(_proccessPpuCycle[PPU::GetCurrentCycle()]) { + int32_t currentCycle = (PPU::GetCurrentCycle() << 9) + PPU::GetCurrentScanline(); + for(auto updateCycle : _ppuViewerUpdateCycle) { + if(updateCycle.second == currentCycle) { + MessageManager::SendNotification(ConsoleNotificationType::PpuViewerDisplayFrame, (void*)(uint64_t)updateCycle.first); + } } - if(PPU::GetCurrentScanline() == 241) { - ProcessEvent(EventType::EndFrame); - } else if(PPU::GetCurrentScanline() == -1) { - ProcessEvent(EventType::StartFrame); + + if(PPU::GetCurrentCycle() == 0) { + if(_breakOnScanline == PPU::GetCurrentScanline()) { + Step(1); + SleepUntilResume(BreakSource::Pause); + } + if(PPU::GetCurrentScanline() == 241) { + ProcessEvent(EventType::EndFrame); + } else if(PPU::GetCurrentScanline() == -1) { + ProcessEvent(EventType::StartFrame); + } } } - + OperationInfo operationInfo { 0, 0, MemoryOperationType::DummyRead }; if(_hasBreakpoint[BreakpointType::Global]) { @@ -1128,10 +1133,27 @@ void Debugger::GetPpuAbsoluteAddressAndType(uint32_t relativeAddr, PpuAddressTyp info->Type = PpuAddressType::None; } -void Debugger::SetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) +void Debugger::UpdatePpuCyclesToProcess() { - _ppuViewerScanline = scanline; - _ppuViewerCycle = cycle; + memset(_proccessPpuCycle, 0, sizeof(_proccessPpuCycle)); + for(auto updateCycle : _ppuViewerUpdateCycle) { + _proccessPpuCycle[updateCycle.second] = true; + } + _proccessPpuCycle[0] = true; +} + +void Debugger::SetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) +{ + DebugBreakHelper helper(this); + _ppuViewerUpdateCycle[ppuViewerId] = (cycle << 9) + scanline; + UpdatePpuCyclesToProcess(); +} + +void Debugger::ClearPpuViewerSettings(int32_t ppuViewer) +{ + DebugBreakHelper helper(this); + _ppuViewerUpdateCycle.erase(ppuViewer); + UpdatePpuCyclesToProcess(); } void Debugger::SetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool updateHorizontalScrollOnly) diff --git a/Core/Debugger.h b/Core/Debugger.h index d2efeb90..b4c88c32 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -107,8 +107,8 @@ private: atomic _breakOnScanline; - int32_t _ppuViewerScanline; - int32_t _ppuViewerCycle; + bool _proccessPpuCycle[341]; + std::unordered_map _ppuViewerUpdateCycle; uint16_t _ppuScrollX; uint16_t _ppuScrollY; @@ -142,6 +142,8 @@ private: void AddDebugEvent(DebugEventType type, uint16_t address = -1, uint8_t value = 0, int16_t breakpointId = -1, int8_t ppuLatch = -1); + void UpdatePpuCyclesToProcess(); + public: Debugger(shared_ptr console, shared_ptr cpu, shared_ptr ppu, shared_ptr apu, shared_ptr memoryManager, shared_ptr mapper); ~Debugger(); @@ -186,7 +188,9 @@ public: shared_ptr GetCodeDataLogger(); void SetNextStatement(uint16_t addr); - void SetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle); + + void SetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle); + void ClearPpuViewerSettings(int32_t ppuViewer); bool IsExecutionStopped(); diff --git a/Core/INotificationListener.h b/Core/INotificationListener.h index 57f38569..c4af7a57 100644 --- a/Core/INotificationListener.h +++ b/Core/INotificationListener.h @@ -22,6 +22,7 @@ enum class ConsoleNotificationType ExecuteShortcut = 16, EmulationStopped = 17, EventViewerDisplayFrame = 18, + BeforeEmulationStop = 19, }; class INotificationListener diff --git a/Core/MessageManager.cpp b/Core/MessageManager.cpp index 52f7d57f..bda862b4 100644 --- a/Core/MessageManager.cpp +++ b/Core/MessageManager.cpp @@ -711,24 +711,7 @@ void MessageManager::UnregisterNotificationListener(INotificationListener* notif void MessageManager::SendNotification(ConsoleNotificationType type, void* parameter) { auto lock = _notificationLock.AcquireSafe(); - - //Iterate on a copy to prevent issues if a notification causes a listener to unregister itself - vector listeners = MessageManager::_notificationListeners; - vector processedListeners; - - for(size_t i = 0, len = listeners.size(); i < len; i++) { - INotificationListener* notificationListener = listeners[i]; - if(std::find(processedListeners.begin(), processedListeners.end(), notificationListener) == processedListeners.end()) { - //Only send notification if it hasn't been processed already - notificationListener->ProcessNotification(type, parameter); - } - processedListeners.push_back(notificationListener); - - if(len != MessageManager::_notificationListeners.size()) { - //Vector changed, start from the beginning again (can occur when sending a notification caused listeners to unregister themselves) - i = 0; - len = MessageManager::_notificationListeners.size(); - listeners = MessageManager::_notificationListeners; - } + for(INotificationListener *notificationListener : MessageManager::_notificationListeners) { + notificationListener->ProcessNotification(type, parameter); } } diff --git a/GUI.NET/Debugger/frmPpuViewer.cs b/GUI.NET/Debugger/frmPpuViewer.cs index b96827e0..27739720 100644 --- a/GUI.NET/Debugger/frmPpuViewer.cs +++ b/GUI.NET/Debugger/frmPpuViewer.cs @@ -21,10 +21,16 @@ namespace Mesen.GUI.Debugger private TabPage _selectedTab; private bool _refreshing = false; + private static int _nextPpuViewerId = 0; + private int _ppuViewerId = 0; + public frmPpuViewer() { InitializeComponent(); + _ppuViewerId = _nextPpuViewerId; + _nextPpuViewerId++; + this._selectedTab = this.tpgNametableViewer; this.mnuAutoRefresh.Checked = ConfigManager.Config.DebugInfo.PpuAutoRefresh; this.mnuRefreshOnBreak.Checked = ConfigManager.Config.DebugInfo.PpuRefreshOnBreak; @@ -49,7 +55,7 @@ namespace Mesen.GUI.Debugger this.nudScanline.Value = ConfigManager.Config.DebugInfo.PpuDisplayScanline; this.nudCycle.Value = ConfigManager.Config.DebugInfo.PpuDisplayCycle; - InteropEmu.DebugSetPpuViewerScanlineCycle((int)this.nudScanline.Value, (int)this.nudCycle.Value); + InteropEmu.DebugSetPpuViewerScanlineCycle(_ppuViewerId, (int)this.nudScanline.Value, (int)this.nudCycle.Value); this.ctrlNametableViewer.GetData(); this.ctrlChrViewer.GetData(); @@ -67,6 +73,7 @@ namespace Mesen.GUI.Debugger { base.OnFormClosing(e); this._notifListener.OnNotification -= this._notifListener_OnNotification; + InteropEmu.DebugClearPpuViewerSettings(_ppuViewerId); } private void _notifListener_OnNotification(InteropEmu.NotificationEventArgs e) @@ -81,17 +88,19 @@ namespace Mesen.GUI.Debugger break; case InteropEmu.ConsoleNotificationType.PpuViewerDisplayFrame: - if(ConfigManager.Config.DebugInfo.PpuAutoRefresh && !_refreshing && (DateTime.Now - _lastUpdate).Milliseconds > 66) { - //Update at 15 fps most - this.GetData(); - this.BeginInvoke((MethodInvoker)(() => this.RefreshViewers())); - _lastUpdate = DateTime.Now; + if(e.Parameter.ToInt32() == _ppuViewerId) { + if(ConfigManager.Config.DebugInfo.PpuAutoRefresh && !_refreshing && (DateTime.Now - _lastUpdate).Milliseconds > 66) { + //Update at 15 fps most + this.GetData(); + this.BeginInvoke((MethodInvoker)(() => this.RefreshViewers())); + _lastUpdate = DateTime.Now; + } } break; case InteropEmu.ConsoleNotificationType.GameLoaded: //Configuration is lost when debugger is restarted (when switching game or power cycling) - InteropEmu.DebugSetPpuViewerScanlineCycle(ConfigManager.Config.DebugInfo.PpuDisplayScanline, ConfigManager.Config.DebugInfo.PpuDisplayCycle); + InteropEmu.DebugSetPpuViewerScanlineCycle(_ppuViewerId, ConfigManager.Config.DebugInfo.PpuDisplayScanline, ConfigManager.Config.DebugInfo.PpuDisplayCycle); break; } } @@ -152,7 +161,7 @@ namespace Mesen.GUI.Debugger scanline = Math.Min(260, Math.Max(-1, scanline)); cycle = Math.Min(340, Math.Max(0, cycle)); - InteropEmu.DebugSetPpuViewerScanlineCycle(scanline, cycle); + InteropEmu.DebugSetPpuViewerScanlineCycle(_ppuViewerId, scanline, cycle); ConfigManager.Config.DebugInfo.PpuDisplayScanline = scanline; ConfigManager.Config.DebugInfo.PpuDisplayCycle = cycle; ConfigManager.ApplyChanges(); diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index e9f33a67..b97c8074 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -608,8 +608,8 @@ namespace Mesen.GUI.Forms })); break; - case InteropEmu.ConsoleNotificationType.EmulationStopped: - this.BeginInvoke((Action)(() => { + case InteropEmu.ConsoleNotificationType.BeforeEmulationStop: + this.Invoke((Action)(() => { DebugWindowManager.CloseAll(); })); break; @@ -777,7 +777,7 @@ namespace Mesen.GUI.Forms case EmulatorShortcut.Pause: PauseEmu(); break; case EmulatorShortcut.Reset: this.ResetEmu(); break; case EmulatorShortcut.PowerCycle: this.PowerCycleEmu(); break; - case EmulatorShortcut.PowerOff: InteropEmu.Stop(); break; + case EmulatorShortcut.PowerOff: Task.Run(() => InteropEmu.Stop()); break; case EmulatorShortcut.Exit: this.Close(); break; case EmulatorShortcut.ToggleCheats: ToggleCheats(); break; diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 6e2bafa8..84789a46 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -253,7 +253,8 @@ namespace Mesen.GUI } [DllImport(DLLPath)] public static extern void DebugGetAbsoluteAddressAndType(UInt32 relativeAddr, ref AddressTypeInfo addressTypeInfo); - [DllImport(DLLPath)] public static extern void DebugSetPpuViewerScanlineCycle(Int32 scanline, Int32 cycle); + [DllImport(DLLPath)] public static extern void DebugSetPpuViewerScanlineCycle(Int32 ppuViewerId, Int32 scanline, Int32 cycle); + [DllImport(DLLPath)] public static extern void DebugClearPpuViewerSettings(Int32 ppuViewerId); [DllImport(DLLPath)] public static extern void DebugSetFreezeState(UInt16 address, [MarshalAs(UnmanagedType.I1)]bool frozen); @@ -876,6 +877,7 @@ namespace Mesen.GUI ExecuteShortcut = 16, EmulationStopped = 17, EventViewerDisplayFrame = 18, + BeforeEmulationStop = 19, } public enum ControllerType @@ -1049,7 +1051,10 @@ namespace Mesen.GUI public void Dispose() { - InteropEmu.UnregisterNotificationCallback(_notificationListener); + Task.Run(() => { + //Unregister the callback in another thread, to prevent deadlocks + InteropEmu.UnregisterNotificationCallback(_notificationListener); + }); } public void ProcessNotification(int type, IntPtr parameter) diff --git a/InteropDLL/DebugWrapper.cpp b/InteropDLL/DebugWrapper.cpp index bc48a954..d8a8165b 100644 --- a/InteropDLL/DebugWrapper.cpp +++ b/InteropDLL/DebugWrapper.cpp @@ -51,7 +51,8 @@ extern "C" DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); } DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); } - DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(scanline, cycle); } + DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(ppuViewerId, scanline, cycle); } + DllExport void __stdcall DebugClearPpuViewerSettings(int32_t ppuViewerId) { return GetDebugger()->ClearPpuViewerSettings(ppuViewerId); } DllExport void __stdcall DebugSetNextStatement(uint16_t addr) { GetDebugger()->SetNextStatement(addr); } DllExport void __stdcall DebugSetMemoryState(DebugMemoryType type, uint8_t *buffer) { GetDebugger()->GetMemoryDumper()->SetMemoryState(type, buffer); }