diff --git a/Core/Console.cpp b/Core/Console.cpp index e63a01c..e31e363 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -390,6 +390,7 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, EmulationConfig orgConfig = _settings->GetEmulationConfig(); //backup emulation config (can be temporarily overriden to control the power on RAM state) shared_ptr cart = forPowerCycle ? _cart : BaseCartridge::CreateCartridge(this, romFile, patchFile); if(cart) { + bool debuggerActive = _debugger != nullptr; if(stopRom) { KeyManager::UpdateDevices(); Stop(false); @@ -400,8 +401,6 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, _cart = cart; auto lock = _debuggerLock.AcquireSafe(); - bool debuggerActive = _debugger != nullptr; - if(_debugger) { //Reset debugger if it was running before _debugger->Release(); diff --git a/Core/Console.h b/Core/Console.h index a168527..9c54cfe 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -225,6 +225,13 @@ public: } } + __forceinline void DebugLog(string log) + { + if(_debugger) { + _debugger->Log(log); + } + } + template void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi); void ProcessEvent(EventType type); void BreakImmediately(BreakSource source); diff --git a/Core/Cpu.h b/Core/Cpu.h index 92f0868..22f999d 100644 --- a/Core/Cpu.h +++ b/Core/Cpu.h @@ -30,13 +30,13 @@ private: typedef void(Cpu::*Func)(); - MemoryManager *_memoryManager; - DmaController *_dmaController; - Console *_console; + MemoryManager *_memoryManager = nullptr; + DmaController *_dmaController = nullptr; + Console *_console = nullptr; bool _immediateMode = false; - CpuState _state; + CpuState _state = {}; uint32_t _operand = -1; uint32_t GetProgramAddress(uint16_t addr); diff --git a/Core/CpuDebugger.cpp b/Core/CpuDebugger.cpp index 926f0fa..37b6415 100644 --- a/Core/CpuDebugger.cpp +++ b/Core/CpuDebugger.cpp @@ -21,6 +21,7 @@ #include "MemoryAccessCounter.h" #include "ExpressionEvaluator.h" #include "Assembler.h" +#include "../Utilities/HexUtilities.h" CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType) { @@ -140,9 +141,15 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock())) { //Memory access was a read on an uninitialized memory address - if(_enableBreakOnUninitRead && _settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled) && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnUninitRead)) { - breakSource = BreakSource::BreakOnUninitMemoryRead; - _step->StepCount = 0; + if(_enableBreakOnUninitRead) { + if(_memoryAccessCounter->GetReadCount(addressInfo) == 1) { + //Only warn the first time + _debugger->Log(string(_cpuType == CpuType::Sa1 ? "[SA1]" : "[CPU]") + " Uninitialized memory read: $" + HexUtilities::ToHex24(addr)); + } + if(_settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled) && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnUninitRead)) { + breakSource = BreakSource::BreakOnUninitMemoryRead; + _step->StepCount = 0; + } } } } diff --git a/Core/DebugUtilities.h b/Core/DebugUtilities.h index de715be..969181b 100644 --- a/Core/DebugUtilities.h +++ b/Core/DebugUtilities.h @@ -79,6 +79,17 @@ public: } } + static bool IsRomMemory(SnesMemoryType memType) + { + switch(memType) { + case SnesMemoryType::PrgRom: + case SnesMemoryType::GbPrgRom: + case SnesMemoryType::GbBootRom: + case SnesMemoryType::SaveRam: //Include save ram here to avoid uninit memory read warnings on save ram + return true; + } + } + static constexpr CpuType GetLastCpuType() { return CpuType::Gameboy; diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 938bff1..cb0b218 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -666,6 +666,25 @@ void Debugger::SetBreakpoints(Breakpoint breakpoints[], uint32_t length) } } +void Debugger::Log(string message) +{ + auto lock = _logLock.AcquireSafe(); + if(_debuggerLog.size() >= 1000) { + _debuggerLog.pop_front(); + } + _debuggerLog.push_back(message); +} + +string Debugger::GetLog() +{ + auto lock = _logLock.AcquireSafe(); + stringstream ss; + for(string& msg : _debuggerLog) { + ss << msg << "\n"; + } + return ss.str(); +} + void Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption) { vector output; diff --git a/Core/Debugger.h b/Core/Debugger.h index cd1c66c..5a62b00 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -4,6 +4,7 @@ #include "PpuTypes.h" #include "DebugTypes.h" #include "DebugUtilities.h" +#include "../Utilities/SimpleLock.h" class Console; class Cpu; @@ -74,6 +75,9 @@ private: shared_ptr _labelManager; unique_ptr _watchExpEval[(int)DebugUtilities::GetLastCpuType() + 1]; + + SimpleLock _logLock; + std::list _debuggerLog; atomic _executionStopped; atomic _breakRequestCount; @@ -139,6 +143,9 @@ public: void SetBreakpoints(Breakpoint breakpoints[], uint32_t length); + void Log(string message); + string GetLog(); + void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption); shared_ptr GetTraceLogger(); diff --git a/Core/GbDebugger.cpp b/Core/GbDebugger.cpp index 28eba6c..4c826f0 100644 --- a/Core/GbDebugger.cpp +++ b/Core/GbDebugger.cpp @@ -18,6 +18,7 @@ #include "GbEventManager.h" #include "BaseEventManager.h" #include "GbAssembler.h" +#include "../Utilities/HexUtilities.h" GbDebugger::GbDebugger(Debugger* debugger) { @@ -135,9 +136,15 @@ void GbDebugger::ProcessRead(uint16_t addr, uint8_t value, MemoryOperationType t if(addr < 0xFE00 || addr >= 0xFF80) { if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock())) { //Memory access was a read on an uninitialized memory address - if(_enableBreakOnUninitRead && _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled) && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnUninitRead)) { - breakSource = BreakSource::BreakOnUninitMemoryRead; - _step->StepCount = 0; + if(_enableBreakOnUninitRead) { + if(_memoryAccessCounter->GetReadCount(addressInfo) == 1) { + //Only warn the first time + _debugger->Log("[GB] Uninitialized memory read: $" + HexUtilities::ToHex(addr)); + } + if(_settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled) && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnUninitRead)) { + breakSource = BreakSource::BreakOnUninitMemoryRead; + _step->StepCount = 0; + } } } } diff --git a/Core/MemoryAccessCounter.cpp b/Core/MemoryAccessCounter.cpp index 82d6bfe..9a3369d 100644 --- a/Core/MemoryAccessCounter.cpp +++ b/Core/MemoryAccessCounter.cpp @@ -30,14 +30,19 @@ MemoryAccessCounter::MemoryAccessCounter(Debugger* debugger, Console *console) } } -bool MemoryAccessCounter::IsAddressUninitialized(AddressInfo &addressInfo) +bool MemoryAccessCounter::IsAddressUninitialized(AddressInfo& addressInfo) { - if(addressInfo.Type != SnesMemoryType::PrgRom && addressInfo.Type != SnesMemoryType::SaveRam && addressInfo.Type != SnesMemoryType::GbPrgRom && addressInfo.Type != SnesMemoryType::GbBootRom) { + if(!DebugUtilities::IsRomMemory(addressInfo.Type)) { return _counters[(int)addressInfo.Type][addressInfo.Address].WriteCount == 0; } return false; } +uint64_t MemoryAccessCounter::GetReadCount(AddressInfo& addressInfo) +{ + return _counters[(int)addressInfo.Type][addressInfo.Address].ReadCount; +} + bool MemoryAccessCounter::ProcessMemoryRead(AddressInfo &addressInfo, uint64_t masterClock) { if(addressInfo.Address < 0) { diff --git a/Core/MemoryAccessCounter.h b/Core/MemoryAccessCounter.h index 30c3453..ee45494 100644 --- a/Core/MemoryAccessCounter.h +++ b/Core/MemoryAccessCounter.h @@ -43,6 +43,8 @@ private: public: MemoryAccessCounter(Debugger *debugger, Console *console); + uint64_t GetReadCount(AddressInfo& addressInfo); + bool ProcessMemoryRead(AddressInfo& addressInfo, uint64_t masterClock); void ProcessMemoryWrite(AddressInfo& addressInfo, uint64_t masterClock); void ProcessMemoryExec(AddressInfo& addressInfo, uint64_t masterClock); diff --git a/Core/Sa1Cpu.h b/Core/Sa1Cpu.h index d441b11..9b86467 100644 --- a/Core/Sa1Cpu.h +++ b/Core/Sa1Cpu.h @@ -22,12 +22,12 @@ private: static constexpr uint32_t LegacyIrqVector = 0xFFFE; static constexpr uint32_t LegacyCoprocessorVector = 0x00FFF4; - Sa1* _sa1; - Console* _console; + Sa1* _sa1 = nullptr; + Console* _console = nullptr; bool _immediateMode = false; - CpuState _state; + CpuState _state = {}; uint32_t _operand = -1; uint32_t GetProgramAddress(uint16_t addr); diff --git a/Core/SuperGameboy.cpp b/Core/SuperGameboy.cpp index 853b439..9f8979b 100644 --- a/Core/SuperGameboy.cpp +++ b/Core/SuperGameboy.cpp @@ -154,12 +154,9 @@ void SuperGameboy::ProcessInputPortWrite(uint8_t value) _packetReady = true; _listeningForPacket = false; - /*string log = HexUtilities::ToHex(_packetData[0] >> 3); - log += " Size: " + std::to_string(_packetData[0] & 0x07) + " - "; - for(int i = 0; i < 16; i++) { - log += HexUtilities::ToHex(_packetData[i]) + " "; + if(_console->IsDebugging()) { + LogPacket(); } - MessageManager::Log(log);*/ } else { _packetData[_packetByte] &= ~(1 << _packetBit); } @@ -191,6 +188,50 @@ void SuperGameboy::ProcessInputPortWrite(uint8_t value) _inputWriteClock = _memoryManager->GetMasterClock(); } +void SuperGameboy::LogPacket() +{ + uint8_t commandId = _packetData[0] >> 3; + string name; + switch(commandId) { + case 0: name = "PAL01"; break; //Set SGB Palette 0, 1 Data + case 1: name = "PAL23"; break; //Set SGB Palette 2, 3 Data + case 2: name = "PAL03"; break; //Set SGB Palette 0, 3 Data + case 3: name = "PAL12"; break; //Set SGB Palette 1, 2 Data + case 4: name = "ATTR_BLK"; break; //"Block" Area Designation Mode + case 5: name = "ATTR_LIN"; break; //"Line" Area Designation Mode + case 6: name = "ATTR_DIV"; break; //"Divide" Area Designation Mode + case 7: name = "ATTR_CHR"; break; //"1CHR" Area Designation Mode + case 8: name = "SOUND"; break; //Sound On / Off + case 9: name = "SOU_TRN"; break; //Transfer Sound PRG / DATA + case 0xA: name = "PAL_SET"; break; //Set SGB Palette Indirect + case 0xB: name = "PAL_TRN"; break; //Set System Color Palette Data + case 0xC: name = "ATRC_EN"; break; //Enable / disable Attraction Mode + case 0xD: name = "TEST_EN"; break; //Speed Function + case 0xE: name = "ICON_EN"; break; //SGB Function + case 0xF: name = "DATA_SND"; break; //SUPER NES WRAM Transfer 1 + case 0x10: name = "DATA_TRN"; break; //SUPER NES WRAM Transfer 2 + case 0x11: name = "MLT_REG"; break; //Controller 2 Request + case 0x12: name = "JUMP"; break; //Set SNES Program Counter + case 0x13: name = "CHR_TRN"; break; //Transfer Character Font Data + case 0x14: name = "PCT_TRN"; break; //Set Screen Data Color Data + case 0x15: name = "ATTR_TRN"; break; //Set Attribute from ATF + case 0x16: name = "ATTR_SET"; break; //Set Data to ATF + case 0x17: name = "MASK_EN"; break; //Game Boy Window Mask + case 0x18: name = "OBJ_TRN"; break; //Super NES OBJ Mode + + case 0x1E: name = "Header Data"; break; + case 0x1F: name = "Header Data"; break; + + default: name = "Unknown"; break; + } + + string log = "SGB Command: " + HexUtilities::ToHex(commandId) + " - " + name + " (Len: " + std::to_string(_packetData[0] & 0x07) + ") - "; + for(int i = 0; i < 16; i++) { + log += HexUtilities::ToHex(_packetData[i]) + " "; + } + _console->DebugLog(log); +} + void SuperGameboy::WriteLcdColor(uint8_t scanline, uint8_t pixel, uint8_t color) { _lcdBuffer[GetLcdBufferRow()][(scanline & 0x07) * 160 + pixel] = color; diff --git a/Core/SuperGameboy.h b/Core/SuperGameboy.h index b003fb0..609dc0c 100644 --- a/Core/SuperGameboy.h +++ b/Core/SuperGameboy.h @@ -59,6 +59,8 @@ public: void ProcessInputPortWrite(uint8_t value); + void LogPacket(); + void WriteLcdColor(uint8_t scanline, uint8_t pixel, uint8_t color); void MixAudio(uint32_t targetRate, int16_t* soundSamples, uint32_t sampleCount); diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index d1a88f3..45d2179 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -19,6 +19,7 @@ #include "../Core/BaseEventManager.h" extern shared_ptr _console; +static string _logString; shared_ptr GetDebugger() { @@ -65,7 +66,13 @@ extern "C" DllExport void __stdcall GetProfilerData(CpuType cpuType, ProfiledFunction* profilerData, uint32_t& functionCount) { GetDebugger()->GetCallstackManager(cpuType)->GetProfiler()->GetProfilerData(profilerData, functionCount); } DllExport void __stdcall ResetProfiler(CpuType cpuType) { GetDebugger()->GetCallstackManager(cpuType)->GetProfiler()->Reset(); } - DllExport void __stdcall GetState(DebugState &state) { GetDebugger()->GetState(state, false); } + DllExport void __stdcall GetState(DebugState& state) { GetDebugger()->GetState(state, false); } + + DllExport const char* __stdcall GetDebuggerLog() + { + _logString = GetDebugger()->GetLog(); + return _logString.c_str(); + } DllExport void __stdcall SetMemoryState(SnesMemoryType type, uint8_t *buffer, int32_t length) { GetDebugger()->GetMemoryDumper()->SetMemoryState(type, buffer, length); } DllExport uint32_t __stdcall GetMemorySize(SnesMemoryType type) { return GetDebugger()->GetMemoryDumper()->GetMemorySize(type); } diff --git a/InteropDLL/EmuApiWrapper.cpp b/InteropDLL/EmuApiWrapper.cpp index df70145..4479c89 100644 --- a/InteropDLL/EmuApiWrapper.cpp +++ b/InteropDLL/EmuApiWrapper.cpp @@ -31,13 +31,13 @@ unique_ptr _renderer; unique_ptr _soundManager; unique_ptr _keyManager; unique_ptr _shortcutKeyHandler; - -void* _windowHandle = nullptr; -void* _viewerHandle = nullptr; -string _returnString; -string _logString; shared_ptr _console; -InteropNotificationListeners _listeners; + +static void* _windowHandle = nullptr; +static void* _viewerHandle = nullptr; +static string _returnString; +static string _logString; +static InteropNotificationListeners _listeners; struct InteropRomInfo { @@ -48,8 +48,8 @@ struct InteropRomInfo char Sha1[40]; }; -string _romPath; -string _patchPath; +static string _romPath; +static string _patchPath; extern "C" { DllExport bool __stdcall TestDll() diff --git a/UI/Debugger/Config/DebugInfo.cs b/UI/Debugger/Config/DebugInfo.cs index 476498f..57a2f60 100644 --- a/UI/Debugger/Config/DebugInfo.cs +++ b/UI/Debugger/Config/DebugInfo.cs @@ -29,6 +29,7 @@ namespace Mesen.GUI.Config public ScriptWindowConfig ScriptWindow = new ScriptWindowConfig(); public ProfilerConfig Profiler = new ProfilerConfig(); public AssemblerConfig Assembler = new AssemblerConfig(); + public DebugLogConfig DebugLog = new DebugLogConfig(); public DebugInfo() { diff --git a/UI/Debugger/Config/DebugLogConfig.cs b/UI/Debugger/Config/DebugLogConfig.cs new file mode 100644 index 0000000..3ac0359 --- /dev/null +++ b/UI/Debugger/Config/DebugLogConfig.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace Mesen.GUI.Config +{ + public class DebugLogConfig + { + public Size WindowSize = new Size(0, 0); + public Point WindowLocation; + } +} \ No newline at end of file diff --git a/UI/Debugger/Config/DebuggerShortcutsConfig.cs b/UI/Debugger/Config/DebuggerShortcutsConfig.cs index 7466ce2..aa1aff2 100644 --- a/UI/Debugger/Config/DebuggerShortcutsConfig.cs +++ b/UI/Debugger/Config/DebuggerShortcutsConfig.cs @@ -95,10 +95,8 @@ namespace Mesen.GUI.Config public XmlKeys OpenTraceLogger = Keys.Control | Keys.J; [ShortcutName("Open Register Viewer")] public XmlKeys OpenRegisterViewer = Keys.Control | Keys.K; - [ShortcutName("Open Text Hooker")] - public XmlKeys OpenTextHooker = Keys.Control | Keys.H; - [ShortcutName("Open Watch Window")] - public XmlKeys OpenWatchWindow = Keys.Control | Keys.W; + [ShortcutName("Open Debug Log")] + public XmlKeys OpenDebugLog = Keys.Control | Keys.B; [ShortcutName("Open Tilemap Viewer")] public XmlKeys OpenTilemapViewer = Keys.Control | Keys.D1; diff --git a/UI/Debugger/DebugWindowManager.cs b/UI/Debugger/DebugWindowManager.cs index 3b084e2..36b7983 100644 --- a/UI/Debugger/DebugWindowManager.cs +++ b/UI/Debugger/DebugWindowManager.cs @@ -45,6 +45,7 @@ namespace Mesen.GUI.Debugger case DebugWindow.RegisterViewer: frm = new frmRegisterViewer(); frm.Icon = Properties.Resources.RegisterIcon; break; case DebugWindow.Profiler: frm = new frmProfiler(); frm.Icon = Properties.Resources.PerfTracker; break; case DebugWindow.Assembler: frm = new frmAssembler(); frm.Icon = Properties.Resources.Chip; break; + case DebugWindow.DebugLog: frm = new frmDebugLog(); frm.Icon = Properties.Resources.LogWindow; break; case DebugWindow.GbTileViewer: frm = new frmTileViewer(CpuType.Gameboy); frm.Icon = Properties.Resources.VerticalLayout; break; case DebugWindow.GbTilemapViewer: frm = new frmTilemapViewer(CpuType.Gameboy); frm.Icon = Properties.Resources.VideoOptions; break; case DebugWindow.GbPaletteViewer: frm = new frmPaletteViewer(CpuType.Gameboy); frm.Icon = Properties.Resources.VideoFilter; break; @@ -176,6 +177,7 @@ namespace Mesen.GUI.Debugger case DebugWindow.EventViewer: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmEventViewer) && ((frmEventViewer)form).CpuType == CpuType.Cpu); case DebugWindow.GbEventViewer: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmEventViewer) && ((frmEventViewer)form).CpuType == CpuType.Gameboy); case DebugWindow.Profiler: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmProfiler)); + case DebugWindow.DebugLog: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmDebugLog)); } return null; @@ -233,6 +235,8 @@ namespace Mesen.GUI.Debugger RegisterViewer, Profiler, Assembler, + DebugLog, + GbTileViewer, GbTilemapViewer, GbPaletteViewer, diff --git a/UI/Debugger/frmDbgPreferences.cs b/UI/Debugger/frmDbgPreferences.cs index 4ecb2b0..4e250bc 100644 --- a/UI/Debugger/frmDbgPreferences.cs +++ b/UI/Debugger/frmDbgPreferences.cs @@ -51,7 +51,7 @@ namespace Mesen.GUI.Debugger GetMember(nameof(DebuggerShortcutsConfig.OpenProfiler)), GetMember(nameof(DebuggerShortcutsConfig.OpenScriptWindow)), GetMember(nameof(DebuggerShortcutsConfig.OpenTraceLogger)), - //GetMember(nameof(DebuggerShortcutsConfig.OpenWatchWindow)), + GetMember(nameof(DebuggerShortcutsConfig.OpenDebugLog)), GetMember(nameof(DebuggerShortcutsConfig.OpenTilemapViewer)), GetMember(nameof(DebuggerShortcutsConfig.OpenTileViewer)), diff --git a/UI/Debugger/frmDebugLog.Designer.cs b/UI/Debugger/frmDebugLog.Designer.cs new file mode 100644 index 0000000..c72834d --- /dev/null +++ b/UI/Debugger/frmDebugLog.Designer.cs @@ -0,0 +1,111 @@ +namespace Mesen.GUI.Forms +{ + partial class frmDebugLog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.txtLog = new System.Windows.Forms.TextBox(); + this.btnClose = new System.Windows.Forms.Button(); + this.tmrRefresh = new System.Windows.Forms.Timer(this.components); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.txtLog, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.btnClose, 1, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(480, 377); + this.tableLayoutPanel1.TabIndex = 0; + // + // txtLog + // + this.txtLog.BackColor = System.Drawing.Color.White; + this.tableLayoutPanel1.SetColumnSpan(this.txtLog, 2); + this.txtLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtLog.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtLog.Location = new System.Drawing.Point(3, 3); + this.txtLog.Multiline = true; + this.txtLog.Name = "txtLog"; + this.txtLog.ReadOnly = true; + this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtLog.Size = new System.Drawing.Size(474, 340); + this.txtLog.TabIndex = 1; + this.txtLog.WordWrap = false; + // + // btnClose + // + this.btnClose.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnClose.Location = new System.Drawing.Point(407, 349); + this.btnClose.Name = "btnClose"; + this.btnClose.Size = new System.Drawing.Size(70, 25); + this.btnClose.TabIndex = 0; + this.btnClose.Text = "Close"; + this.btnClose.UseVisualStyleBackColor = true; + this.btnClose.Click += new System.EventHandler(this.btnClose_Click); + // + // tmrRefresh + // + this.tmrRefresh.Enabled = true; + this.tmrRefresh.Tick += new System.EventHandler(this.tmrRefresh_Tick); + // + // frmDebugLog + // + this.AcceptButton = this.btnClose; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnClose; + this.ClientSize = new System.Drawing.Size(480, 377); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "frmDebugLog"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Debug Log"; + this.Controls.SetChildIndex(this.tableLayoutPanel1, 0); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TextBox txtLog; + private System.Windows.Forms.Button btnClose; + private System.Windows.Forms.Timer tmrRefresh; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmDebugLog.cs b/UI/Debugger/frmDebugLog.cs new file mode 100644 index 0000000..23bdf74 --- /dev/null +++ b/UI/Debugger/frmDebugLog.cs @@ -0,0 +1,69 @@ +using Mesen.GUI.Config; +using Mesen.GUI.Controls; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public partial class frmDebugLog : BaseForm + { + private string _currentLog; + public frmDebugLog() + { + InitializeComponent(); + txtLog.Font = new Font(BaseControl.MonospaceFontFamily, 10); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + DebugLogConfig config = ConfigManager.Config.Debug.DebugLog; + RestoreLocation(config.WindowLocation, config.WindowSize); + UpdateLog(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + + DebugLogConfig config = ConfigManager.Config.Debug.DebugLog; + config.WindowSize = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Size : this.Size; + config.WindowLocation = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Location : this.Location; + ConfigManager.ApplyChanges(); + } + + private void UpdateLog() + { + Task.Run(() => { + string log = DebugApi.GetLog(); + if(_currentLog != log) { + _currentLog = log; + this.BeginInvoke((Action)(() => { + txtLog.Text = _currentLog; + txtLog.SelectionLength = 0; + txtLog.SelectionStart = txtLog.Text.Length; + txtLog.ScrollToCaret(); + })); + } + }); + } + + private void btnClose_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void tmrRefresh_Tick(object sender, EventArgs e) + { + UpdateLog(); + } + } +} diff --git a/UI/Debugger/frmDebugLog.resx b/UI/Debugger/frmDebugLog.resx new file mode 100644 index 0000000..b8422f0 --- /dev/null +++ b/UI/Debugger/frmDebugLog.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 107, 17 + + \ No newline at end of file diff --git a/UI/Forms/Tools/frmLogWindow.cs b/UI/Forms/Tools/frmLogWindow.cs index 5d62992..6f88f7e 100644 --- a/UI/Forms/Tools/frmLogWindow.cs +++ b/UI/Forms/Tools/frmLogWindow.cs @@ -18,11 +18,6 @@ namespace Mesen.GUI.Forms InitializeComponent(); } - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - } - protected override void OnShown(EventArgs e) { base.OnShown(e); diff --git a/UI/Forms/frmMain.Designer.cs b/UI/Forms/frmMain.Designer.cs index c56178f..1151cf7 100644 --- a/UI/Forms/frmMain.Designer.cs +++ b/UI/Forms/frmMain.Designer.cs @@ -161,6 +161,7 @@ this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuEventViewer = new System.Windows.Forms.ToolStripMenuItem(); this.mnuMemoryTools = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDebugLog = new System.Windows.Forms.ToolStripMenuItem(); this.mnuRegisterViewer = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem26 = new System.Windows.Forms.ToolStripSeparator(); @@ -376,7 +377,7 @@ this.mnuShowFPS}); this.mnuEmulationSpeed.Image = global::Mesen.GUI.Properties.Resources.Speed; this.mnuEmulationSpeed.Name = "mnuEmulationSpeed"; - this.mnuEmulationSpeed.Size = new System.Drawing.Size(180, 22); + this.mnuEmulationSpeed.Size = new System.Drawing.Size(135, 22); this.mnuEmulationSpeed.Text = "Speed"; this.mnuEmulationSpeed.DropDownOpening += new System.EventHandler(this.mnuEmulationSpeed_DropDownOpening); // @@ -463,7 +464,7 @@ this.mnuFullscreen}); this.mnuVideoScale.Image = global::Mesen.GUI.Properties.Resources.Fullscreen; this.mnuVideoScale.Name = "mnuVideoScale"; - this.mnuVideoScale.Size = new System.Drawing.Size(180, 22); + this.mnuVideoScale.Size = new System.Drawing.Size(135, 22); this.mnuVideoScale.Text = "Video Size"; this.mnuVideoScale.DropDownOpening += new System.EventHandler(this.mnuVideoScale_DropDownOpening); // @@ -550,7 +551,7 @@ this.mnuBlendHighResolutionModes}); this.mnuVideoFilter.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; this.mnuVideoFilter.Name = "mnuVideoFilter"; - this.mnuVideoFilter.Size = new System.Drawing.Size(180, 22); + this.mnuVideoFilter.Size = new System.Drawing.Size(135, 22); this.mnuVideoFilter.Text = "Video Filter"; this.mnuVideoFilter.DropDownOpening += new System.EventHandler(this.mnuVideoFilter_DropDownOpening); // @@ -744,7 +745,7 @@ this.mnuRegionPal}); this.mnuRegion.Image = global::Mesen.GUI.Properties.Resources.WebBrowser; this.mnuRegion.Name = "mnuRegion"; - this.mnuRegion.Size = new System.Drawing.Size(180, 22); + this.mnuRegion.Size = new System.Drawing.Size(135, 22); this.mnuRegion.Text = "Region"; this.mnuRegion.DropDownOpening += new System.EventHandler(this.mnuRegion_DropDownOpening); // @@ -774,13 +775,13 @@ // toolStripMenuItem4 // this.toolStripMenuItem4.Name = "toolStripMenuItem4"; - this.toolStripMenuItem4.Size = new System.Drawing.Size(177, 6); + this.toolStripMenuItem4.Size = new System.Drawing.Size(132, 6); // // mnuAudioConfig // this.mnuAudioConfig.Image = global::Mesen.GUI.Properties.Resources.Audio; this.mnuAudioConfig.Name = "mnuAudioConfig"; - this.mnuAudioConfig.Size = new System.Drawing.Size(180, 22); + this.mnuAudioConfig.Size = new System.Drawing.Size(135, 22); this.mnuAudioConfig.Text = "Audio"; this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click); // @@ -788,7 +789,7 @@ // this.mnuInputConfig.Image = global::Mesen.GUI.Properties.Resources.Controller; this.mnuInputConfig.Name = "mnuInputConfig"; - this.mnuInputConfig.Size = new System.Drawing.Size(180, 22); + this.mnuInputConfig.Size = new System.Drawing.Size(135, 22); this.mnuInputConfig.Text = "Input"; this.mnuInputConfig.Click += new System.EventHandler(this.mnuInputConfig_Click); // @@ -796,7 +797,7 @@ // this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.VideoOptions; this.mnuVideoConfig.Name = "mnuVideoConfig"; - this.mnuVideoConfig.Size = new System.Drawing.Size(180, 22); + this.mnuVideoConfig.Size = new System.Drawing.Size(135, 22); this.mnuVideoConfig.Text = "Video"; this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); // @@ -804,33 +805,33 @@ // this.mnuEmulationConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches; this.mnuEmulationConfig.Name = "mnuEmulationConfig"; - this.mnuEmulationConfig.Size = new System.Drawing.Size(180, 22); + this.mnuEmulationConfig.Size = new System.Drawing.Size(135, 22); this.mnuEmulationConfig.Text = "Emulation"; this.mnuEmulationConfig.Click += new System.EventHandler(this.mnuEmulationConfig_Click); // // toolStripMenuItem22 // this.toolStripMenuItem22.Name = "toolStripMenuItem22"; - this.toolStripMenuItem22.Size = new System.Drawing.Size(177, 6); + this.toolStripMenuItem22.Size = new System.Drawing.Size(132, 6); // // mnuGameboyConfig // this.mnuGameboyConfig.Image = global::Mesen.GUI.Properties.Resources.GameboyIcon; this.mnuGameboyConfig.Name = "mnuGameboyConfig"; - this.mnuGameboyConfig.Size = new System.Drawing.Size(180, 22); + this.mnuGameboyConfig.Size = new System.Drawing.Size(135, 22); this.mnuGameboyConfig.Text = "Game Boy"; this.mnuGameboyConfig.Click += new System.EventHandler(this.mnuGameboyConfig_Click); // // toolStripMenuItem3 // this.toolStripMenuItem3.Name = "toolStripMenuItem3"; - this.toolStripMenuItem3.Size = new System.Drawing.Size(177, 6); + this.toolStripMenuItem3.Size = new System.Drawing.Size(132, 6); // // mnuPreferences // this.mnuPreferences.Image = global::Mesen.GUI.Properties.Resources.Settings; this.mnuPreferences.Name = "mnuPreferences"; - this.mnuPreferences.Size = new System.Drawing.Size(180, 22); + this.mnuPreferences.Size = new System.Drawing.Size(135, 22); this.mnuPreferences.Text = "Preferences"; this.mnuPreferences.Click += new System.EventHandler(this.mnuPreferences_Click); // @@ -1130,6 +1131,7 @@ this.mnuTraceLogger, this.toolStripMenuItem26, this.mnuAssembler, + this.mnuDebugLog, this.mnuProfiler, this.mnuScriptWindow, this.toolStripMenuItem12, @@ -1217,6 +1219,13 @@ this.mnuMemoryTools.Size = new System.Drawing.Size(183, 22); this.mnuMemoryTools.Text = "Memory Tools"; // + // mnuDebugLog + // + this.mnuDebugLog.Image = global::Mesen.GUI.Properties.Resources.LogWindow; + this.mnuDebugLog.Name = "mnuDebugLog"; + this.mnuDebugLog.Size = new System.Drawing.Size(183, 22); + this.mnuDebugLog.Text = "Debug Log"; + // // mnuRegisterViewer // this.mnuRegisterViewer.Image = global::Mesen.GUI.Properties.Resources.RegisterIcon; @@ -1582,5 +1591,6 @@ private System.Windows.Forms.ToolStripMenuItem mnuGbEventViewer; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem22; private System.Windows.Forms.ToolStripMenuItem mnuGameboyConfig; + private System.Windows.Forms.ToolStripMenuItem mnuDebugLog; } } \ No newline at end of file diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index eda03c8..fc047b9 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -302,6 +302,7 @@ namespace Mesen.GUI.Forms mnuRegisterViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenRegisterViewer)); mnuProfiler.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenProfiler)); mnuAssembler.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenAssembler)); + mnuDebugLog.InitShortcut(this, nameof(DebuggerShortcutsConfig.OpenDebugLog)); mnuNoneFilter.Click += (s, e) => { _shortcuts.SetVideoFilter(VideoFilterType.None); }; mnuNtscFilter.Click += (s, e) => { _shortcuts.SetVideoFilter(VideoFilterType.NTSC); }; @@ -368,6 +369,7 @@ namespace Mesen.GUI.Forms mnuRegisterViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.RegisterViewer); }; mnuProfiler.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.Profiler); }; mnuAssembler.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.Assembler); }; + mnuDebugLog.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.DebugLog); }; mnuGbTilemapViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.GbTilemapViewer); }; mnuGbTileViewer.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.GbTileViewer); }; @@ -484,6 +486,7 @@ namespace Mesen.GUI.Forms mnuRegisterViewer.Enabled = running; mnuProfiler.Enabled = running; mnuAssembler.Enabled = running; + mnuDebugLog.Enabled = running; bool isGameboyMode = coprocessor == CoprocessorType.Gameboy; bool isSuperGameboy = coprocessor == CoprocessorType.SGB; diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index 6db6c6e..e7e4133 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -27,6 +27,9 @@ namespace Mesen.GUI [DllImport(DllPath)] public static extern void SetTraceOptions(InteropTraceLoggerOptions options); [DllImport(DllPath)] public static extern void ClearTraceLog(); + [DllImport(DllPath, EntryPoint = "GetDebuggerLog")] private static extern IntPtr GetDebuggerLogWrapper(); + public static string GetLog() { return Utf8Marshaler.PtrToStringUtf8(DebugApi.GetDebuggerLogWrapper()).Replace("\n", Environment.NewLine); } + [DllImport(DllPath, EntryPoint = "GetDisassemblyLineData")] private static extern void GetDisassemblyLineDataWrapper(CpuType type, UInt32 lineIndex, ref InteropCodeLineData lineData); public static CodeLineData GetDisassemblyLineData(CpuType type, UInt32 lineIndex) { diff --git a/UI/UI.csproj b/UI/UI.csproj index 4f14cf7..7a34da9 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -280,6 +280,7 @@ + @@ -310,6 +311,12 @@ ctrlGameboyStatus.cs + + Form + + + frmDebugLog.cs + @@ -1090,6 +1097,9 @@ ctrlGameboyStatus.cs + + frmDebugLog.cs + ctrlMemoryAccessCounters.cs