Debugger: Added debug log window

This currently logs uninitialized memory reads (main CPU, SA1 & GB) + SGB packets
This commit is contained in:
Sour 2020-06-23 18:34:03 -04:00
parent 73afbc3623
commit 4ac7b21878
28 changed files with 508 additions and 54 deletions

View file

@ -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<BaseCartridge> 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();

View file

@ -225,6 +225,13 @@ public:
}
}
__forceinline void DebugLog(string log)
{
if(_debugger) {
_debugger->Log(log);
}
}
template<CpuType type> void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
void ProcessEvent(EventType type);
void BreakImmediately(BreakSource source);

View file

@ -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);

View file

@ -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;
}
}
}
}

View file

@ -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;

View file

@ -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<uint8_t> output;

View file

@ -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> _labelManager;
unique_ptr<ExpressionEvaluator> _watchExpEval[(int)DebugUtilities::GetLastCpuType() + 1];
SimpleLock _logLock;
std::list<string> _debuggerLog;
atomic<bool> _executionStopped;
atomic<uint32_t> _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<TraceLogger> GetTraceLogger();

View file

@ -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;
}
}
}
}

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -19,6 +19,7 @@
#include "../Core/BaseEventManager.h"
extern shared_ptr<Console> _console;
static string _logString;
shared_ptr<Debugger> 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); }

View file

@ -31,13 +31,13 @@ unique_ptr<IRenderingDevice> _renderer;
unique_ptr<IAudioDevice> _soundManager;
unique_ptr<IKeyManager> _keyManager;
unique_ptr<ShortcutKeyHandler> _shortcutKeyHandler;
void* _windowHandle = nullptr;
void* _viewerHandle = nullptr;
string _returnString;
string _logString;
shared_ptr<Console> _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()

View file

@ -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()
{

View file

@ -0,0 +1,10 @@
using System.Drawing;
namespace Mesen.GUI.Config
{
public class DebugLogConfig
{
public Size WindowSize = new Size(0, 0);
public Point WindowLocation;
}
}

View file

@ -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;

View file

@ -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,

View file

@ -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)),

111
UI/Debugger/frmDebugLog.Designer.cs generated Normal file
View file

@ -0,0 +1,111 @@
namespace Mesen.GUI.Forms
{
partial class frmDebugLog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

@ -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();
}
}
}

View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="tmrRefresh.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>107, 17</value>
</metadata>
</root>

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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)
{

View file

@ -280,6 +280,7 @@
<Compile Include="Debugger\Config\DebuggerShortcutsConfig.cs" />
<Compile Include="Debugger\Config\DebuggerInfo.cs" />
<Compile Include="Debugger\Config\AssemblerConfig.cs" />
<Compile Include="Debugger\Config\DebugLogConfig.cs" />
<Compile Include="Debugger\Config\ProfilerConfig.cs" />
<Compile Include="Debugger\Config\ScriptWindowConfig.cs" />
<Compile Include="Debugger\Config\SpriteViewerConfig.cs" />
@ -310,6 +311,12 @@
<Compile Include="Debugger\Controls\ctrlGameboyStatus.Designer.cs">
<DependentUpon>ctrlGameboyStatus.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\frmDebugLog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\frmDebugLog.Designer.cs">
<DependentUpon>frmDebugLog.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Integration\RgbdsSymbolFile.cs" />
<Compile Include="Debugger\Integration\BassLabelFile.cs" />
<Compile Include="Debugger\MemoryTools\ctrlMemoryAccessCounters.cs">
@ -1090,6 +1097,9 @@
<EmbeddedResource Include="Debugger\Controls\ctrlGameboyStatus.resx">
<DependentUpon>ctrlGameboyStatus.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\frmDebugLog.resx">
<DependentUpon>frmDebugLog.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\MemoryTools\ctrlMemoryAccessCounters.resx">
<DependentUpon>ctrlMemoryAccessCounters.cs</DependentUpon>
</EmbeddedResource>