From c1a6453343bf4004d264e01ac3b82bb4523c7420 Mon Sep 17 00:00:00 2001 From: Souryo Date: Thu, 1 Dec 2016 19:38:48 -0500 Subject: [PATCH] Debugger: Memory access counters (read/write/exec/uninitialized read) --- Core/BaseMapper.cpp | 19 +- Core/BaseMapper.h | 5 +- Core/CPU.h | 17 +- Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/Debugger.cpp | 31 ++- Core/Debugger.h | 7 +- Core/IMemoryHandler.h | 1 + Core/MemoryAccessCounter.cpp | 55 +++++ Core/MemoryAccessCounter.h | 25 ++ Core/MemoryDumper.cpp | 4 +- GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs | 2 +- GUI.NET/Debugger/Controls/ctrlHexViewer.cs | 2 +- .../ctrlMemoryAccessCounters.Designer.cs | 222 ++++++++++++++++++ .../Controls/ctrlMemoryAccessCounters.cs | 187 +++++++++++++++ .../Controls/ctrlMemoryAccessCounters.resx | 120 ++++++++++ GUI.NET/Debugger/Controls/ctrlTextbox.cs | 12 +- GUI.NET/Debugger/frmMemoryViewer.Designer.cs | 87 ++++++- GUI.NET/Debugger/frmMemoryViewer.cs | 26 +- GUI.NET/Debugger/frmMemoryViewer.resx | 3 + GUI.NET/GUI.NET.csproj | 9 + GUI.NET/InteropEmu.cs | 39 ++- InteropDLL/DebugWrapper.cpp | 6 + 23 files changed, 830 insertions(+), 57 deletions(-) create mode 100644 Core/MemoryAccessCounter.cpp create mode 100644 Core/MemoryAccessCounter.h create mode 100644 GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.Designer.cs create mode 100644 GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs create mode 100644 GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.resx diff --git a/Core/BaseMapper.cpp b/Core/BaseMapper.cpp index cd5f5c68..6fab5418 100644 --- a/Core/BaseMapper.cpp +++ b/Core/BaseMapper.cpp @@ -5,6 +5,7 @@ #include "../Utilities/FolderUtilities.h" #include "CheatManager.h" #include "Debugger.h" +#include "MemoryDumper.h" void BaseMapper::WriteRegister(uint16_t addr, uint8_t value) { } uint8_t BaseMapper::ReadRegister(uint16_t addr) { return 0; } @@ -570,7 +571,7 @@ void BaseMapper::ProcessNotification(ConsoleNotificationType type, void* paramet void BaseMapper::ApplyCheats() { RestoreOriginalPrgRam(); - CheatManager::ApplyPrgCodes(_prgRom, GetPrgSize()); + CheatManager::ApplyPrgCodes(_prgRom, _prgSize); } void BaseMapper::GetMemoryRanges(MemoryRanges &ranges) @@ -764,14 +765,16 @@ void BaseMapper::WriteMemory(DebugMemoryType type, uint8_t* buffer) } } -uint32_t BaseMapper::GetPrgSize(bool getWorkRamSize) +uint32_t BaseMapper::GetMemorySize(DebugMemoryType type) { - return getWorkRamSize ? _workRamSize : _prgSize; -} - -uint32_t BaseMapper::GetChrSize(bool getRamSize) -{ - return getRamSize ? _chrRamSize : (_onlyChrRam ? 0 : _chrRomSize); + switch(type) { + default: return 0; + case DebugMemoryType::ChrRom: return _chrRomSize; + case DebugMemoryType::ChrRam: return _chrRamSize; + case DebugMemoryType::SaveRam: return _saveRamSize; + case DebugMemoryType::PrgRom: return _prgSize; + case DebugMemoryType::WorkRam: return _workRamSize; + } } int32_t BaseMapper::ToAbsoluteAddress(uint16_t addr) diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h index 245296c8..def69bbf 100644 --- a/Core/BaseMapper.h +++ b/Core/BaseMapper.h @@ -228,8 +228,9 @@ public: CartridgeState GetState(); uint8_t* GetPrgRom(); uint8_t* GetWorkRam(); - uint32_t GetPrgSize(bool getWorkRamSize = false); - uint32_t GetChrSize(bool getRamSize = false); + + uint32_t GetMemorySize(DebugMemoryType type); + uint32_t CopyMemory(DebugMemoryType type, uint8_t* buffer); void WriteMemory(DebugMemoryType type, uint8_t* buffer); int32_t ToAbsoluteAddress(uint16_t addr); diff --git a/Core/CPU.h b/Core/CPU.h index 9bf04db1..77931e75 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -102,7 +102,7 @@ private: void DummyRead() { - MemoryRead(_state.PC, MemoryOperationType::Read); + MemoryRead(_state.PC, MemoryOperationType::DummyRead); } uint8_t ReadByte() @@ -166,7 +166,6 @@ private: IncCycleCount(); } _cpuWrite = false; - } uint8_t MemoryRead(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read) { @@ -252,12 +251,12 @@ private: uint8_t GetZeroAddr() { return ReadByte(); } uint8_t GetZeroXAddr() { uint8_t value = ReadByte(); - MemoryRead(value); //Dummy read + MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read return value + X(); } uint8_t GetZeroYAddr() { uint8_t value = ReadByte(); - MemoryRead(value); //Dummy read + MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read return value + Y(); } uint16_t GetAbsAddr() { return ReadWord(); } @@ -268,7 +267,7 @@ private: if(pageCrossed || dummyRead) { //Dummy read done by the processor (only when page is crossed for READ instructions) - MemoryRead(baseAddr + X() - (pageCrossed ? 0x100 : 0)); + MemoryRead(baseAddr + X() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead); } return baseAddr + X(); } @@ -279,7 +278,7 @@ private: if(pageCrossed || dummyRead) { //Dummy read done by the processor (only when page is crossed for READ instructions) - MemoryRead(baseAddr + Y() - (pageCrossed ? 0x100 : 0)); + MemoryRead(baseAddr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead); } return baseAddr + Y(); @@ -300,7 +299,7 @@ private: uint8_t zero = ReadByte(); //Dummy read - MemoryRead(zero); + MemoryRead(zero, MemoryOperationType::DummyRead); zero += X(); @@ -326,7 +325,7 @@ private: bool pageCrossed = CheckPageCrossed(addr, Y()); if(pageCrossed || dummyRead) { //Dummy read done by the processor (only when page is crossed for READ instructions) - MemoryRead(addr + Y() - (pageCrossed ? 0x100 : 0)); + MemoryRead(addr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead); } return addr + Y(); } @@ -419,7 +418,7 @@ private: uint8_t result = value >> 1; SetZeroNegativeFlags(result); - return value >> 1; + return result; } uint8_t ROL(uint8_t value) { diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c15d9828..ee47ee28 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -437,6 +437,7 @@ + @@ -751,6 +752,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 9b560264..278970ce 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1096,6 +1096,9 @@ Debugger + + Debugger + @@ -1278,5 +1281,8 @@ Debugger + + Debugger + \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 25748fb8..19e2709d 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -11,6 +11,8 @@ #include "CodeDataLogger.h" #include "ExpressionEvaluator.h" #include "LabelManager.h" +#include "MemoryDumper.h" +#include "MemoryAccessCounter.h" Debugger* Debugger::Instance = nullptr; const int Debugger::BreakpointTypeCount; @@ -25,9 +27,10 @@ Debugger::Debugger(shared_ptr console, shared_ptr cpu, shared_ptr< _mapper = mapper; _labelManager.reset(new LabelManager(_mapper)); - _disassembler.reset(new Disassembler(memoryManager->GetInternalRAM(), mapper->GetPrgRom(), mapper->GetPrgSize(), mapper->GetWorkRam(), mapper->GetPrgSize(true), this)); - _codeDataLogger.reset(new CodeDataLogger(mapper->GetPrgSize(), mapper->GetChrSize())); + _disassembler.reset(new Disassembler(memoryManager->GetInternalRAM(), mapper->GetPrgRom(), mapper->GetMemorySize(DebugMemoryType::PrgRom), mapper->GetWorkRam(), mapper->GetMemorySize(DebugMemoryType::WorkRam), this)); + _codeDataLogger.reset(new CodeDataLogger(mapper->GetMemorySize(DebugMemoryType::PrgRom), mapper->GetMemorySize(DebugMemoryType::ChrRom))); _memoryDumper.reset(new MemoryDumper(_ppu, _memoryManager, _mapper, _codeDataLogger)); + _memoryAccessCounter.reset(new MemoryAccessCounter()); _stepOut = false; _stepCount = -1; @@ -100,13 +103,13 @@ void Debugger::BreakIfDebugging() bool Debugger::LoadCdlFile(string cdlFilepath) { if(_codeDataLogger->LoadCdlFile(cdlFilepath)) { - for(int i = 0, len = _mapper->GetPrgSize(); i < len; i++) { + for(int i = 0, len = _mapper->GetMemorySize(DebugMemoryType::PrgRom); i < len; i++) { if(_codeDataLogger->IsCode(i)) { i = _disassembler->BuildCache(i, -1, 0xFFFF, _codeDataLogger->IsSubEntryPoint(i)) - 1; } } - for(int i = 0, len = _mapper->GetPrgSize(); i < len; i++) { + for(int i = 0, len = _mapper->GetMemorySize(DebugMemoryType::PrgRom); i < len; i++) { if(_codeDataLogger->IsSubEntryPoint(i)) { _functionEntryPoints.emplace(i); } @@ -314,8 +317,14 @@ void Debugger::PrivateProcessRamOperation(MemoryOperationType type, uint16_t &ad //Check if a breakpoint has been hit and freeze execution if one has bool breakDone = false; - int32_t absoluteAddr = _mapper->ToAbsoluteAddress(addr); - int32_t absoluteRamAddr = _mapper->ToAbsoluteWorkRamAddress(addr); + AddressTypeInfo addressInfo; + GetAbsoluteAddressAndType(addr, &addressInfo); + int32_t absoluteAddr = addressInfo.Type == AddressType::PrgRom ? addressInfo.Address : -1; + int32_t absoluteRamAddr = addressInfo.Type == AddressType::WorkRam ? addressInfo.Address : -1; + + if(addressInfo.Address >= 0 && type != MemoryOperationType::DummyRead) { + _memoryAccessCounter->ProcessMemoryAccess(addressInfo, type); + } if(absoluteAddr >= 0) { if(type == MemoryOperationType::ExecOperand) { @@ -633,6 +642,11 @@ shared_ptr Debugger::GetMemoryDumper() return _memoryDumper; } +shared_ptr Debugger::GetMemoryAccessCounter() +{ + return _memoryAccessCounter; +} + bool Debugger::IsExecutionStopped() { return _executionStopped; @@ -675,4 +689,9 @@ void Debugger::SetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) { _ppuViewerScanline = scanline; _ppuViewerCycle = cycle; +} + +int Debugger::GetMemorySize(DebugMemoryType memoryType) +{ + return _mapper->GetMemorySize(memoryType); } \ No newline at end of file diff --git a/Core/Debugger.h b/Core/Debugger.h index 293cb756..20cff247 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -13,7 +13,6 @@ using std::unordered_set; #include "TraceLogger.h" #include "../Utilities/SimpleLock.h" #include "CodeDataLogger.h" -#include "MemoryDumper.h" #include "DebuggerTypes.h" class CPU; @@ -22,6 +21,8 @@ class MemoryManager; class Console; class Disassembler; class LabelManager; +class MemoryDumper; +class MemoryAccessCounter; class Debugger { @@ -33,6 +34,7 @@ private: unique_ptr _disassembler; shared_ptr _memoryDumper; shared_ptr _codeDataLogger; + shared_ptr _memoryAccessCounter; shared_ptr _console; shared_ptr _cpu; @@ -145,6 +147,7 @@ public: void StopTraceLogger(); shared_ptr GetMemoryDumper(); + shared_ptr GetMemoryAccessCounter(); int32_t EvaluateExpression(string expression, EvalResultType &resultType); @@ -156,4 +159,6 @@ public: static bool IsEnabled(); static void BreakIfDebugging(); + + int GetMemorySize(DebugMemoryType memoryType); }; \ No newline at end of file diff --git a/Core/IMemoryHandler.h b/Core/IMemoryHandler.h index bf29f147..71a08538 100644 --- a/Core/IMemoryHandler.h +++ b/Core/IMemoryHandler.h @@ -16,6 +16,7 @@ enum class MemoryOperationType ExecOpCode = 2, ExecOperand = 3, PpuRenderingRead = 4, + DummyRead = 5 }; class MemoryRanges diff --git a/Core/MemoryAccessCounter.cpp b/Core/MemoryAccessCounter.cpp new file mode 100644 index 00000000..c949085b --- /dev/null +++ b/Core/MemoryAccessCounter.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "MemoryAccessCounter.h" +#include "Console.h" + +std::unordered_map& MemoryAccessCounter::GetCountMap(MemoryOperationType operationType, AddressType addressType) +{ + switch(operationType) { + case MemoryOperationType::Read: return _readCounts[(int)addressType]; + case MemoryOperationType::Write: return _writeCounts[(int)addressType]; + + default: + case MemoryOperationType::ExecOpCode: + case MemoryOperationType::ExecOperand: return _execCounts[(int)addressType]; + } +} + +void MemoryAccessCounter::ProcessMemoryAccess(AddressTypeInfo &addressInfo, MemoryOperationType operation) +{ + int index = (int)addressInfo.Type; + std::unordered_map &countMap = GetCountMap(operation, addressInfo.Type); + if(operation != MemoryOperationType::Write && + (addressInfo.Type == AddressType::InternalRam || addressInfo.Type == AddressType::WorkRam) && + _initWrites[index].find(addressInfo.Address) == _initWrites[index].end()) { + //Mark address as read before being written to (if trying to read/execute) + _uninitReads[index].emplace(addressInfo.Address); + } else if(operation == MemoryOperationType::Write) { + _initWrites[index].emplace(addressInfo.Address); + } + + countMap[addressInfo.Address]++; +} + +void MemoryAccessCounter::ResetCounts() +{ + Console::Pause(); + for(int i = 0; i < 4; i++) { + _readCounts[i].clear(); + _writeCounts[i].clear(); + _execCounts[i].clear(); + } + Console::Resume(); +} + +void MemoryAccessCounter::GetAccessCounts(AddressType memoryType, MemoryOperationType operationType, uint32_t counts[], bool forUninitReads) +{ + if(forUninitReads) { + for(int address : _uninitReads[(int)memoryType]) { + counts[address] = 1; + } + } else { + for(auto kvp : GetCountMap(operationType, memoryType)) { + counts[kvp.first] = kvp.second; + } + } +} \ No newline at end of file diff --git a/Core/MemoryAccessCounter.h b/Core/MemoryAccessCounter.h new file mode 100644 index 00000000..b283c245 --- /dev/null +++ b/Core/MemoryAccessCounter.h @@ -0,0 +1,25 @@ +#pragma once +#include "stdafx.h" +#include "DebuggerTypes.h" +#include "IMemoryHandler.h" +#include +#include + +class MemoryAccessCounter +{ +private: + std::unordered_map _readCounts[4]; + std::unordered_map _writeCounts[4]; + std::unordered_map _execCounts[4]; + + std::unordered_set _initWrites[4]; + std::unordered_set _uninitReads[4]; + + std::unordered_map& GetCountMap(MemoryOperationType operationType, AddressType addressType); + +public: + void ProcessMemoryAccess(AddressTypeInfo &addressInfo, MemoryOperationType operation); + void ResetCounts(); + + void GetAccessCounts(AddressType memoryType, MemoryOperationType operationType, uint32_t counts[], bool forUninitReads); +}; \ No newline at end of file diff --git a/Core/MemoryDumper.cpp b/Core/MemoryDumper.cpp index 6e1bb63e..ee3b538e 100644 --- a/Core/MemoryDumper.cpp +++ b/Core/MemoryDumper.cpp @@ -132,8 +132,8 @@ void MemoryDumper::GetChrBank(int bankIndex, uint32_t* frameBuffer, uint8_t pale } else { int bank = bankIndex - 2; uint32_t baseAddr = bank * 0x1000; - bool useChrRam = _mapper->GetChrSize(false) == 0; - uint32_t chrSize = _mapper->GetChrSize(useChrRam); + bool useChrRam = _mapper->GetMemorySize(DebugMemoryType::ChrRam) > 0; + uint32_t chrSize = _mapper->GetMemorySize(useChrRam ? DebugMemoryType::ChrRam : DebugMemoryType::ChrRom); vector chrData(chrSize, 0); _mapper->CopyMemory(useChrRam ? DebugMemoryType::ChrRam : DebugMemoryType::ChrRom, chrData.data()); diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs index 1e74a5aa..d7c382a6 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs @@ -131,7 +131,7 @@ namespace Mesen.GUI.Debugger } lineNumbers.Add(relativeAddress); - lineNumberNotes.Add(lineParts[2].Trim('0')); + lineNumberNotes.Add(lineParts[2].TrimStart('0')); codeNotes.Add(lineParts[3]); codeLines.Add(lineParts[4]); } diff --git a/GUI.NET/Debugger/Controls/ctrlHexViewer.cs b/GUI.NET/Debugger/Controls/ctrlHexViewer.cs index 8f8aa44e..6ee3ef22 100644 --- a/GUI.NET/Debugger/Controls/ctrlHexViewer.cs +++ b/GUI.NET/Debugger/Controls/ctrlHexViewer.cs @@ -77,7 +77,7 @@ namespace Mesen.GUI.Debugger.Controls { get { - return 135 + this.ColumnCount * 30; + return 145 + this.ColumnCount * 30; } } diff --git a/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.Designer.cs b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.Designer.cs new file mode 100644 index 00000000..09246e27 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.Designer.cs @@ -0,0 +1,222 @@ +namespace Mesen.GUI.Debugger.Controls +{ + partial class ctrlMemoryAccessCounters + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblViewMemoryType = new System.Windows.Forms.Label(); + this.cboMemoryType = new System.Windows.Forms.ComboBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblSort = new System.Windows.Forms.Label(); + this.cboSort = new System.Windows.Forms.ComboBox(); + this.chkHighlightUninitRead = new System.Windows.Forms.CheckBox(); + this.btnReset = new System.Windows.Forms.Button(); + this.ctrlScrollableTextbox = new Mesen.GUI.Debugger.ctrlScrollableTextbox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.flowLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Controls.Add(this.lblViewMemoryType); + this.flowLayoutPanel1.Controls.Add(this.cboMemoryType); + this.flowLayoutPanel1.Location = new System.Drawing.Point(3, 3); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(167, 26); + this.flowLayoutPanel1.TabIndex = 2; + // + // lblViewMemoryType + // + this.lblViewMemoryType.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblViewMemoryType.AutoSize = true; + this.lblViewMemoryType.Location = new System.Drawing.Point(3, 7); + this.lblViewMemoryType.Name = "lblViewMemoryType"; + this.lblViewMemoryType.Size = new System.Drawing.Size(33, 13); + this.lblViewMemoryType.TabIndex = 0; + this.lblViewMemoryType.Text = "View:"; + // + // cboMemoryType + // + this.cboMemoryType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboMemoryType.FormattingEnabled = true; + this.cboMemoryType.Items.AddRange(new object[] { + "Console RAM", + "PRG ROM", + "Cartridge Save RAM", + "Cartridge Work RAM"}); + this.cboMemoryType.Location = new System.Drawing.Point(42, 3); + this.cboMemoryType.Name = "cboMemoryType"; + this.cboMemoryType.Size = new System.Drawing.Size(121, 21); + this.cboMemoryType.TabIndex = 1; + this.cboMemoryType.SelectedIndexChanged += new System.EventHandler(this.cboMemoryType_SelectedIndexChanged); + // + // 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.ctrlScrollableTextbox, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.chkHighlightUninitRead, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.btnReset, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + 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(514, 307); + this.tableLayoutPanel1.TabIndex = 3; + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel2.Controls.Add(this.lblSort); + this.flowLayoutPanel2.Controls.Add(this.cboSort); + this.flowLayoutPanel2.Location = new System.Drawing.Point(335, 3); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(176, 26); + this.flowLayoutPanel2.TabIndex = 3; + // + // lblSort + // + this.lblSort.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblSort.AutoSize = true; + this.lblSort.Location = new System.Drawing.Point(3, 7); + this.lblSort.Name = "lblSort"; + this.lblSort.Size = new System.Drawing.Size(43, 13); + this.lblSort.TabIndex = 0; + this.lblSort.Text = "Sort by:"; + // + // cboSort + // + this.cboSort.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboSort.FormattingEnabled = true; + this.cboSort.Items.AddRange(new object[] { + "Address", + "Read Count", + "Write Count", + "Execute Count", + "Uninitialized Reads"}); + this.cboSort.Location = new System.Drawing.Point(52, 3); + this.cboSort.Name = "cboSort"; + this.cboSort.Size = new System.Drawing.Size(121, 21); + this.cboSort.TabIndex = 1; + this.cboSort.SelectedIndexChanged += new System.EventHandler(this.cboSort_SelectedIndexChanged); + // + // chkHighlightUninitRead + // + this.chkHighlightUninitRead.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkHighlightUninitRead.AutoSize = true; + this.chkHighlightUninitRead.Location = new System.Drawing.Point(3, 284); + this.chkHighlightUninitRead.Name = "chkHighlightUninitRead"; + this.chkHighlightUninitRead.Size = new System.Drawing.Size(422, 17); + this.chkHighlightUninitRead.TabIndex = 4; + this.chkHighlightUninitRead.Text = "Highlight uninitialized memory reads (only accurate if debugger is active at powe" + + "r on)"; + this.chkHighlightUninitRead.UseVisualStyleBackColor = true; + this.chkHighlightUninitRead.CheckedChanged += new System.EventHandler(this.chkHighlightUninitRead_CheckedChanged); + // + // btnReset + // + this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnReset.Location = new System.Drawing.Point(436, 281); + this.btnReset.Name = "btnReset"; + this.btnReset.Size = new System.Drawing.Size(75, 23); + this.btnReset.TabIndex = 5; + this.btnReset.Text = "Reset Counts"; + this.btnReset.UseVisualStyleBackColor = true; + this.btnReset.Click += new System.EventHandler(this.btnReset_Click); + // + // ctrlScrollableTextbox + // + this.ctrlScrollableTextbox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.tableLayoutPanel1.SetColumnSpan(this.ctrlScrollableTextbox, 2); + this.ctrlScrollableTextbox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlScrollableTextbox.FontSize = 13F; + this.ctrlScrollableTextbox.Location = new System.Drawing.Point(3, 35); + this.ctrlScrollableTextbox.Name = "ctrlScrollableTextbox"; + this.ctrlScrollableTextbox.ShowContentNotes = false; + this.ctrlScrollableTextbox.ShowLineNumberNotes = false; + this.ctrlScrollableTextbox.Size = new System.Drawing.Size(508, 240); + this.ctrlScrollableTextbox.TabIndex = 0; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel1.SetColumnSpan(this.tableLayoutPanel2, 2); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel2, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel1, 0, 0); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(514, 32); + this.tableLayoutPanel2.TabIndex = 6; + // + // ctrlMemoryAccessCounters + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "ctrlMemoryAccessCounters"; + this.Size = new System.Drawing.Size(514, 307); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.flowLayoutPanel2.ResumeLayout(false); + this.flowLayoutPanel2.PerformLayout(); + this.tableLayoutPanel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private ctrlScrollableTextbox ctrlScrollableTextbox; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label lblViewMemoryType; + private System.Windows.Forms.ComboBox cboMemoryType; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.Label lblSort; + private System.Windows.Forms.ComboBox cboSort; + private System.Windows.Forms.CheckBox chkHighlightUninitRead; + private System.Windows.Forms.Button btnReset; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + } +} diff --git a/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs new file mode 100644 index 00000000..114e8d2a --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger.Controls +{ + public partial class ctrlMemoryAccessCounters : UserControl + { + private MemoryCountData[] _data; + private AddressType _memoryType = AddressType.InternalRam; + private SortType _sortType = SortType.Address; + + public ctrlMemoryAccessCounters() + { + InitializeComponent(); + + bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime); + if(!designMode) { + cboMemoryType.SelectedIndex = 0; + cboSort.SelectedIndex = 0; + } + } + + public void RefreshData() + { + ctrlScrollableTextbox.ClearLineStyles(); + + int[] readCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Read, false); + int[] writeCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Write, false); + int[] execCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Exec, false); + + int[] uninitReads = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Read, true); + + int[] addresses = new int[readCounts.Length]; + string[] content = new string[readCounts.Length]; + + if(_data == null || _data.Length != readCounts.Length) { + _data = new MemoryCountData[readCounts.Length]; + for(int i = 0; i < readCounts.Length; i++) { + _data[i] = new MemoryCountData(); + } + } + + for(int i = 0; i < readCounts.Length; i++) { + _data[i].Address = i; + _data[i].ReadCount = readCounts[i]; + _data[i].WriteCount = writeCounts[i]; + _data[i].ExecCount = execCounts[i]; + _data[i].UninitRead = uninitReads[i] > 0; + } + + MemoryCountData[] data = new MemoryCountData[readCounts.Length]; + Array.Copy(_data, data, readCounts.Length); + + switch(_sortType) { + case SortType.Address: break; + case SortType.Read: Array.Sort(data.Select((e) => -e.ReadCount).ToArray(), data); break; + case SortType.Write: Array.Sort(data.Select((e) => -e.WriteCount).ToArray(), data); break; + case SortType.Exec: Array.Sort(data.Select((e) => -e.ExecCount).ToArray(), data); break; + case SortType.UninitRead: Array.Sort(data.Select((e) => e.UninitRead ? -e.ReadCount : (Int32.MaxValue - e.ReadCount)).ToArray(), data); break; + } + + for(int i = 0; i < readCounts.Length; i++) { + addresses[i] = data[i].Address; + content[i] = data[i].Content; + } + + ctrlScrollableTextbox.Header = "Read".PadRight(12) + "Write".PadRight(12) + "Execute".PadRight(12); + ctrlScrollableTextbox.LineNumbers = addresses; + ctrlScrollableTextbox.TextLines = content; + + if(chkHighlightUninitRead.Checked) { + foreach(int address in data.Where((e) => e.UninitRead).Select((e) => e.Address)) { + ctrlScrollableTextbox.SetLineColor(address, null, Color.LightCoral); + } + } + } + + private void cboMemoryType_SelectedIndexChanged(object sender, EventArgs e) + { + switch(this.cboMemoryType.SelectedIndex) { + default: + case 0: _memoryType = AddressType.InternalRam; break; + case 1: _memoryType = AddressType.PrgRom; break; + case 2: _memoryType = AddressType.SaveRam; break; + case 3: _memoryType = AddressType.WorkRam; break; + } + + RefreshData(); + } + + private void cboSort_SelectedIndexChanged(object sender, EventArgs e) + { + _sortType = (SortType)cboSort.SelectedIndex; + RefreshData(); + } + + private void chkHighlightUninitRead_CheckedChanged(object sender, EventArgs e) + { + RefreshData(); + } + + private void btnReset_Click(object sender, EventArgs e) + { + InteropEmu.DebugResetMemoryAccessCounts(); + RefreshData(); + } + + private enum SortType + { + Address = 0, + Read = 1, + Write = 2, + Exec = 3, + UninitRead = 4, + } + + private class MemoryCountData + { + private bool _needRecalc = true; + private int _readCount = 0; + private int _writeCount = 0; + private int _execCount = 0; + private string _content = string.Empty; + + public int Address { get; set; } + + public int ReadCount + { + get { return _readCount; } + set + { + if(this._readCount!=value) { + this._readCount = value; + this._needRecalc = true; + }; + } + } + + public int WriteCount + { + get { return _writeCount; } + set + { + if(this._writeCount!=value) { + this._writeCount = value; + this._needRecalc = true; + }; + } + } + + public int ExecCount + { + get { return _execCount; } + set + { + if(this._execCount!=value) { + this._execCount = value; + this._needRecalc = true; + }; + } + } + + public string Content + { + get + { + if(this._needRecalc) { + _content = (_readCount == 0 ? "0" : _readCount.ToString()).PadRight(12) + + (_writeCount == 0 ? "0" : _writeCount.ToString()).PadRight(12) + + (_execCount == 0 ? "0" : _execCount.ToString()); + _needRecalc = false; + } + return _content; + } + } + + public bool UninitRead { get; set; } + } + } +} diff --git a/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.resx b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/GUI.NET/Debugger/Controls/ctrlTextbox.cs b/GUI.NET/Debugger/Controls/ctrlTextbox.cs index 4d8c9749..0cc13d58 100644 --- a/GUI.NET/Debugger/Controls/ctrlTextbox.cs +++ b/GUI.NET/Debugger/Controls/ctrlTextbox.cs @@ -97,11 +97,13 @@ namespace Mesen.GUI.Debugger UpdateHorizontalScrollWidth(); - _lineNumbers = new int[_contents.Length]; - _lineNumberIndex.Clear(); - for(int i = _contents.Length - 1; i >=0; i--) { - _lineNumbers[i] = i; - _lineNumberIndex[i] = i; + if(_lineNumbers.Length != _contents.Length) { + _lineNumbers = new int[_contents.Length]; + _lineNumberIndex.Clear(); + for(int i = _contents.Length - 1; i >=0; i--) { + _lineNumbers[i] = i; + _lineNumberIndex[i] = i; + } } this.Invalidate(); } diff --git a/GUI.NET/Debugger/frmMemoryViewer.Designer.cs b/GUI.NET/Debugger/frmMemoryViewer.Designer.cs index fb1dab4b..202444c9 100644 --- a/GUI.NET/Debugger/frmMemoryViewer.Designer.cs +++ b/GUI.NET/Debugger/frmMemoryViewer.Designer.cs @@ -31,6 +31,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.ctrlHexViewer = new Mesen.GUI.Debugger.Controls.ctrlHexViewer(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.lblViewMemoryType = new System.Windows.Forms.Label(); @@ -57,17 +58,25 @@ this.toolStrip1 = new System.Windows.Forms.ToolStrip(); this.btnImport = new System.Windows.Forms.ToolStripButton(); this.btnExport = new System.Windows.Forms.ToolStripButton(); + this.tabMain = new System.Windows.Forms.TabControl(); + this.tpgMemoryViewer = new System.Windows.Forms.TabPage(); + this.tpgAccessCounters = new System.Windows.Forms.TabPage(); + this.ctrlMemoryAccessCounters = new Mesen.GUI.Debugger.Controls.ctrlMemoryAccessCounters(); + this.tmrRefresh = new System.Windows.Forms.Timer(this.components); this.flowLayoutPanel1.SuspendLayout(); this.menuStrip1.SuspendLayout(); this.toolStrip1.SuspendLayout(); + this.tabMain.SuspendLayout(); + this.tpgMemoryViewer.SuspendLayout(); + this.tpgAccessCounters.SuspendLayout(); this.SuspendLayout(); // // ctrlHexViewer // this.ctrlHexViewer.Dock = System.Windows.Forms.DockStyle.Fill; - this.ctrlHexViewer.Location = new System.Drawing.Point(0, 49); + this.ctrlHexViewer.Location = new System.Drawing.Point(3, 28); this.ctrlHexViewer.Name = "ctrlHexViewer"; - this.ctrlHexViewer.Size = new System.Drawing.Size(461, 378); + this.ctrlHexViewer.Size = new System.Drawing.Size(513, 346); this.ctrlHexViewer.TabIndex = 0; this.ctrlHexViewer.ColumnCountChanged += new System.EventHandler(this.ctrlHexViewer_ColumnCountChanged); // @@ -75,7 +84,7 @@ // this.flowLayoutPanel1.Controls.Add(this.lblViewMemoryType); this.flowLayoutPanel1.Controls.Add(this.cboMemoryType); - this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 49); + this.flowLayoutPanel1.Location = new System.Drawing.Point(6, 31); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(167, 27); this.flowLayoutPanel1.TabIndex = 1; @@ -120,7 +129,7 @@ this.toolStripMenuItem1}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(461, 24); + this.menuStrip1.Size = new System.Drawing.Size(527, 24); this.menuStrip1.TabIndex = 2; this.menuStrip1.Text = "menuStrip1"; // @@ -282,9 +291,9 @@ this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.btnImport, this.btnExport}); - this.toolStrip1.Location = new System.Drawing.Point(0, 24); + this.toolStrip1.Location = new System.Drawing.Point(3, 3); this.toolStrip1.Name = "toolStrip1"; - this.toolStrip1.Size = new System.Drawing.Size(461, 25); + this.toolStrip1.Size = new System.Drawing.Size(513, 25); this.toolStrip1.TabIndex = 3; this.toolStrip1.Text = "toolStrip1"; // @@ -306,17 +315,64 @@ this.btnExport.Text = "Export"; this.btnExport.Click += new System.EventHandler(this.mnuExport_Click); // + // tabMain + // + this.tabMain.Controls.Add(this.tpgMemoryViewer); + this.tabMain.Controls.Add(this.tpgAccessCounters); + this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabMain.Location = new System.Drawing.Point(0, 24); + this.tabMain.Name = "tabMain"; + this.tabMain.SelectedIndex = 0; + this.tabMain.Size = new System.Drawing.Size(527, 403); + this.tabMain.TabIndex = 4; + this.tabMain.SelectedIndexChanged += new System.EventHandler(this.tabMain_SelectedIndexChanged); + // + // tpgMemoryViewer + // + this.tpgMemoryViewer.Controls.Add(this.flowLayoutPanel1); + this.tpgMemoryViewer.Controls.Add(this.ctrlHexViewer); + this.tpgMemoryViewer.Controls.Add(this.toolStrip1); + this.tpgMemoryViewer.Location = new System.Drawing.Point(4, 22); + this.tpgMemoryViewer.Name = "tpgMemoryViewer"; + this.tpgMemoryViewer.Padding = new System.Windows.Forms.Padding(3); + this.tpgMemoryViewer.Size = new System.Drawing.Size(519, 377); + this.tpgMemoryViewer.TabIndex = 0; + this.tpgMemoryViewer.Text = "Memory Viewer"; + this.tpgMemoryViewer.UseVisualStyleBackColor = true; + // + // tpgAccessCounters + // + this.tpgAccessCounters.Controls.Add(this.ctrlMemoryAccessCounters); + this.tpgAccessCounters.Location = new System.Drawing.Point(4, 22); + this.tpgAccessCounters.Name = "tpgAccessCounters"; + this.tpgAccessCounters.Padding = new System.Windows.Forms.Padding(3); + this.tpgAccessCounters.Size = new System.Drawing.Size(519, 377); + this.tpgAccessCounters.TabIndex = 1; + this.tpgAccessCounters.Text = "Access Counters"; + this.tpgAccessCounters.UseVisualStyleBackColor = true; + // + // ctrlMemoryAccessCounters + // + this.ctrlMemoryAccessCounters.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlMemoryAccessCounters.Location = new System.Drawing.Point(3, 3); + this.ctrlMemoryAccessCounters.Name = "ctrlMemoryAccessCounters"; + this.ctrlMemoryAccessCounters.Size = new System.Drawing.Size(513, 371); + this.ctrlMemoryAccessCounters.TabIndex = 0; + // + // tmrRefresh + // + this.tmrRefresh.Enabled = true; + this.tmrRefresh.Tick += new System.EventHandler(this.tmrRefresh_Tick); + // // frmMemoryViewer // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(461, 427); - this.Controls.Add(this.flowLayoutPanel1); - this.Controls.Add(this.ctrlHexViewer); - this.Controls.Add(this.toolStrip1); + this.ClientSize = new System.Drawing.Size(527, 427); + this.Controls.Add(this.tabMain); this.Controls.Add(this.menuStrip1); this.MainMenuStrip = this.menuStrip1; - this.MinimumSize = new System.Drawing.Size(477, 465); + this.MinimumSize = new System.Drawing.Size(543, 465); this.Name = "frmMemoryViewer"; this.Text = "Memory Viewer"; this.flowLayoutPanel1.ResumeLayout(false); @@ -325,6 +381,10 @@ this.menuStrip1.PerformLayout(); this.toolStrip1.ResumeLayout(false); this.toolStrip1.PerformLayout(); + this.tabMain.ResumeLayout(false); + this.tpgMemoryViewer.ResumeLayout(false); + this.tpgMemoryViewer.PerformLayout(); + this.tpgAccessCounters.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -358,5 +418,10 @@ private System.Windows.Forms.ToolStrip toolStrip1; private System.Windows.Forms.ToolStripButton btnImport; private System.Windows.Forms.ToolStripButton btnExport; + private System.Windows.Forms.TabControl tabMain; + private System.Windows.Forms.TabPage tpgMemoryViewer; + private System.Windows.Forms.TabPage tpgAccessCounters; + private Controls.ctrlMemoryAccessCounters ctrlMemoryAccessCounters; + private System.Windows.Forms.Timer tmrRefresh; } } \ No newline at end of file diff --git a/GUI.NET/Debugger/frmMemoryViewer.cs b/GUI.NET/Debugger/frmMemoryViewer.cs index a8c50730..9a45926e 100644 --- a/GUI.NET/Debugger/frmMemoryViewer.cs +++ b/GUI.NET/Debugger/frmMemoryViewer.cs @@ -16,7 +16,6 @@ namespace Mesen.GUI.Debugger public partial class frmMemoryViewer : BaseForm { private InteropEmu.NotificationListener _notifListener; - private int _autoRefreshCounter = 0; private DebugMemoryType _memoryType = DebugMemoryType.CpuMemory; public frmMemoryViewer() @@ -44,13 +43,6 @@ namespace Mesen.GUI.Debugger { if(e.NotificationType == InteropEmu.ConsoleNotificationType.CodeBreak) { this.BeginInvoke((MethodInvoker)(() => this.RefreshData())); - } else if(e.NotificationType == InteropEmu.ConsoleNotificationType.PpuFrameDone) { - this.BeginInvoke((MethodInvoker)(() => { - if(_autoRefreshCounter % 4 == 0 && this.mnuAutoRefresh.Checked) { - this.RefreshData(); - } - _autoRefreshCounter++; - })); } } @@ -69,7 +61,11 @@ namespace Mesen.GUI.Debugger private void RefreshData() { - this.ctrlHexViewer.Data = InteropEmu.DebugGetMemoryState((DebugMemoryType)this.cboMemoryType.SelectedIndex); + if(this.tabMain.SelectedTab == this.tpgAccessCounters) { + this.ctrlMemoryAccessCounters.RefreshData(); + } else { + this.ctrlHexViewer.Data = InteropEmu.DebugGetMemoryState((DebugMemoryType)this.cboMemoryType.SelectedIndex); + } } private void ctrlHexViewer_ColumnCountChanged(object sender, EventArgs e) @@ -172,5 +168,17 @@ namespace Mesen.GUI.Debugger File.WriteAllBytes(sfd.FileName, this.ctrlHexViewer.Data); } } + + private void tmrRefresh_Tick(object sender, EventArgs e) + { + if(this.mnuAutoRefresh.Checked) { + this.RefreshData(); + } + } + + private void tabMain_SelectedIndexChanged(object sender, EventArgs e) + { + this.tmrRefresh.Interval = this.tabMain.SelectedTab == this.tpgMemoryViewer ? 100 : 500; + } } } diff --git a/GUI.NET/Debugger/frmMemoryViewer.resx b/GUI.NET/Debugger/frmMemoryViewer.resx index 4e7f6e36..51a3bedd 100644 --- a/GUI.NET/Debugger/frmMemoryViewer.resx +++ b/GUI.NET/Debugger/frmMemoryViewer.resx @@ -126,4 +126,7 @@ 222, 17 + + 327, 17 + \ No newline at end of file diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 76fbe3b6..23123afe 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -290,6 +290,12 @@ ctrlFunctionList.cs + + UserControl + + + ctrlMemoryAccessCounters.cs + UserControl @@ -616,6 +622,9 @@ ctrlFunctionList.cs + + ctrlMemoryAccessCounters.cs + ctrlPaletteViewer.cs diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index e1215338..948d25d6 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -179,6 +179,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern Byte DebugGetMemoryValue(UInt32 addr); [DllImport(DLLPath)] public static extern Int32 DebugGetRelativeAddress(UInt32 absoluteAddr, AddressType type); [DllImport(DLLPath)] public static extern Int32 DebugGetAbsoluteAddress(UInt32 relativeAddr); + [DllImport(DLLPath)] public static extern Int32 DebugGetMemorySize(DebugMemoryType type); [DllImport(DLLPath)] public static extern void DebugGetAbsoluteAddressAndType(UInt32 relativeAddr, ref AddressTypeInfo addressTypeInfo); [DllImport(DLLPath)] public static extern void DebugSetPpuViewerScanlineCycle(Int32 scanline, Int32 cycle); @@ -187,12 +188,13 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void DebugStartTraceLogger(TraceLoggerOptions options); [DllImport(DLLPath)] public static extern void DebugStopTraceLogger(); - + [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugLoadCdlFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string cdlFilepath); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugSaveCdlFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string cdlFilepath); [DllImport(DLLPath)] public static extern void DebugGetCdlRatios(ref CdlRatios ratios); [DllImport(DLLPath)] public static extern void DebugResetCdlLog(); - + [DllImport(DLLPath)] public static extern void DebugResetMemoryAccessCounts(); + [DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper(); public static string DebugGetCode() { return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper()); } @@ -298,6 +300,31 @@ namespace Mesen.GUI return frameData; } + [DllImport(DLLPath, EntryPoint= "DebugGetMemoryAccessCounts")] private static extern void DebugGetMemoryAccessCountsWrapper(AddressType type, MemoryOperationType operationType, IntPtr counts, [MarshalAs(UnmanagedType.I1)]bool forUninitReads); + public static Int32[] DebugGetMemoryAccessCounts(AddressType type, MemoryOperationType operationType, bool forUninitReads) + { + int size = 0; + switch(type) { + case AddressType.InternalRam: size = 0x2000; break; + case AddressType.PrgRom: size = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom); break; + case AddressType.WorkRam: size = InteropEmu.DebugGetMemorySize(DebugMemoryType.WorkRam); break; + case AddressType.SaveRam: size = InteropEmu.DebugGetMemorySize(DebugMemoryType.SaveRam); break; + } + + Int32[] counts = new Int32[size]; + + if(size > 0) { + GCHandle hCounts = GCHandle.Alloc(counts, GCHandleType.Pinned); + try { + InteropEmu.DebugGetMemoryAccessCountsWrapper(type, operationType, hCounts.AddrOfPinnedObject(), forUninitReads); + } finally { + hCounts.Free(); + } + } + + return counts; + } + [DllImport(DLLPath, EntryPoint="DebugGetCallstack")] private static extern void DebugGetCallstackWrapper(IntPtr callstackAbsolute, IntPtr callstackRelative); public static void DebugGetCallstack(out Int32[] callstackAbsolute, out Int32[] callstackRelative) { @@ -1078,6 +1105,14 @@ namespace Mesen.GUI Register = 4 } + public enum MemoryOperationType + { + //Note: Not identical to the C++ enum + Read = 0, + Write = 1, + Exec = 2, + } + public struct AddressTypeInfo { public Int32 Address; diff --git a/InteropDLL/DebugWrapper.cpp b/InteropDLL/DebugWrapper.cpp index 46696ac9..735e7740 100644 --- a/InteropDLL/DebugWrapper.cpp +++ b/InteropDLL/DebugWrapper.cpp @@ -3,6 +3,8 @@ #include "../Core/Debugger.h" #include "../Core/CodeDataLogger.h" #include "../Core/LabelManager.h" +#include "../Core/MemoryDumper.h" +#include "../Core/MemoryAccessCounter.h" shared_ptr GetDebugger() { @@ -73,4 +75,8 @@ extern "C" DllExport void __stdcall DebugStartTraceLogger(TraceLoggerOptions options) { GetDebugger()->StartTraceLogger(options); } DllExport void __stdcall DebugStopTraceLogger() { GetDebugger()->StopTraceLogger(); } + + DllExport int32_t __stdcall DebugGetMemorySize(DebugMemoryType type) { return GetDebugger()->GetMemorySize(type); } + DllExport void __stdcall DebugGetMemoryAccessCounts(AddressType memoryType, MemoryOperationType operationType, uint32_t* counts, bool forUninitReads) { GetDebugger()->GetMemoryAccessCounter()->GetAccessCounts(memoryType, operationType, counts, forUninitReads); } + DllExport void __stdcall DebugResetMemoryAccessCounts() { GetDebugger()->GetMemoryAccessCounter()->ResetCounts(); } };