Debugger: Show break source (breakpoint, break on [...] option, etc)

This commit is contained in:
Sour 2019-05-07 20:06:04 -04:00
parent 71b2add83b
commit ea74b764e9
11 changed files with 127 additions and 187 deletions

View file

@ -64,13 +64,13 @@ BreakpointType BreakpointManager::GetBreakpointType(MemoryOperationType type)
}
}
bool BreakpointManager::CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address)
int BreakpointManager::CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address)
{
BreakpointCategory category = Breakpoint::GetBreakpointCategory(address.Type);
BreakpointType type = GetBreakpointType(operationInfo.Type);
if(!_hasBreakpoint[(int)category][(int)type]) {
return false;
return -1;
}
CpuType cpuType = category == BreakpointCategory::Spc ? CpuType::Spc : CpuType::Cpu;
@ -82,10 +82,10 @@ bool BreakpointManager::CheckBreakpoint(MemoryOperationInfo operationInfo, Addre
for(size_t i = 0; i < breakpoints.size(); i++) {
if(breakpoints[i].Matches(operationInfo.Address, address)) {
if(!breakpoints[i].HasCondition() || _bpExpEval[(int)cpuType]->Evaluate(_rpnList[(int)category][(int)type][i], state, resultType, operationInfo)) {
return true;
return breakpoints[i].GetId();
}
}
}
return false;
return -1;
}

View file

@ -29,5 +29,5 @@ public:
BreakpointManager(Debugger *debugger);
void SetBreakpoints(Breakpoint breakpoints[], uint32_t count);
bool CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address);
int CheckBreakpoint(MemoryOperationInfo operationInfo, AddressInfo &address);
};

View file

@ -38,15 +38,6 @@ struct MemoryOperationInfo
uint32_t Address;
int32_t Value;
MemoryOperationType Type;
MemoryOperationInfo() { }
MemoryOperationInfo(uint32_t address, int32_t value, MemoryOperationType type)
{
Address = address;
Value = value;
Type = type;
}
};
enum class BreakpointTypeFlags
@ -211,6 +202,25 @@ struct StackFrameInfo
StackFrameFlags Flags;
};
enum class BreakSource
{
Unspecified = -1,
Breakpoint = 0,
CpuStep = 1,
PpuStep = 2,
BreakOnBrk = 3,
BreakOnCop = 4,
BreakOnWdm = 5,
BreakOnStp = 6
};
struct BreakEvent
{
BreakSource Source;
MemoryOperationInfo Operation;
int32_t BreakpointId;
};
enum class StepType
{
CpuStep,

View file

@ -95,6 +95,7 @@ void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType
AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr);
MemoryOperationInfo operation = { addr, value, type };
CpuState state = _cpu->GetState();
BreakSource breakSource = BreakSource::Unspecified;
if(type == MemoryOperationType::ExecOpCode) {
if(addressInfo.Address >= 0) {
@ -138,12 +139,16 @@ void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType
if(value == 0x00 || value == 0x02 || value == 0x42 || value == 0xDB) {
//Break on BRK/STP/WDM/COP
if(value == 0x00 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnBrk)) {
breakSource = BreakSource::BreakOnBrk;
_cpuStepCount = 0;
} else if(value == 0x02 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnCop)) {
breakSource = BreakSource::BreakOnCop;
_cpuStepCount = 0;
} else if(value == 0x42 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnWdm)) {
breakSource = BreakSource::BreakOnWdm;
_cpuStepCount = 0;
} else if(value == 0xDB && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnStp)) {
breakSource = BreakSource::BreakOnStp;
_cpuStepCount = 0;
}
}
@ -164,7 +169,7 @@ void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType
_eventManager->AddEvent(DebugEventType::Register, operation);
}
ProcessBreakConditions(operation, addressInfo);
ProcessBreakConditions(operation, addressInfo, breakSource);
}
void Debugger::ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
@ -188,7 +193,7 @@ void Debugger::ProcessWorkRamRead(uint32_t addr, uint8_t value)
{
AddressInfo addressInfo { (int32_t)addr, SnesMemoryType::WorkRam };
//TODO Make this more flexible/accurate
MemoryOperationInfo operation(0x7E0000 | addr, value, MemoryOperationType::Read);
MemoryOperationInfo operation { 0x7E0000 | addr, value, MemoryOperationType::Read };
ProcessBreakConditions(operation, addressInfo);
}
@ -196,7 +201,7 @@ void Debugger::ProcessWorkRamWrite(uint32_t addr, uint8_t value)
{
AddressInfo addressInfo { (int32_t)addr, SnesMemoryType::WorkRam };
//TODO Make this more flexible/accurate
MemoryOperationInfo operation(0x7E0000 | addr, value, MemoryOperationType::Write);
MemoryOperationInfo operation { 0x7E0000 | addr, value, MemoryOperationType::Write };
ProcessBreakConditions(operation, addressInfo);
}
@ -208,7 +213,7 @@ void Debugger::ProcessSpcRead(uint16_t addr, uint8_t value, MemoryOperationType
}
AddressInfo addressInfo = _spc->GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type);
MemoryOperationInfo operation { addr, value, type };
if(type == MemoryOperationType::ExecOpCode) {
_disassembler->BuildCache(addressInfo, 0, CpuType::Spc);
@ -254,7 +259,7 @@ void Debugger::ProcessSpcRead(uint16_t addr, uint8_t value, MemoryOperationType
void Debugger::ProcessSpcWrite(uint16_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo { addr, SnesMemoryType::SpcRam }; //Writes never affect the SPC ROM
MemoryOperationInfo operation(addr, value, type);
MemoryOperationInfo operation { addr, value, type };
ProcessBreakConditions(operation, addressInfo);
_disassembler->InvalidateCache(addressInfo);
@ -265,7 +270,7 @@ void Debugger::ProcessSpcWrite(uint16_t addr, uint8_t value, MemoryOperationType
void Debugger::ProcessPpuRead(uint16_t addr, uint8_t value, SnesMemoryType memoryType)
{
AddressInfo addressInfo { addr, memoryType };
MemoryOperationInfo operation(addr, value, MemoryOperationType::Read);
MemoryOperationInfo operation { addr, value, MemoryOperationType::Read };
ProcessBreakConditions(operation, addressInfo);
_memoryAccessCounter->ProcessMemoryAccess(addressInfo, MemoryOperationType::Read, _memoryManager->GetMasterClock());
@ -274,7 +279,7 @@ void Debugger::ProcessPpuRead(uint16_t addr, uint8_t value, SnesMemoryType memor
void Debugger::ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memoryType)
{
AddressInfo addressInfo { addr, memoryType };
MemoryOperationInfo operation(addr, value, MemoryOperationType::Write);
MemoryOperationInfo operation { addr, value, MemoryOperationType::Write };
ProcessBreakConditions(operation, addressInfo);
_memoryAccessCounter->ProcessMemoryAccess(addressInfo, MemoryOperationType::Write, _memoryManager->GetMasterClock());
@ -290,18 +295,18 @@ void Debugger::ProcessPpuCycle()
_ppuStepCount--;
if(_ppuStepCount == 0) {
_cpuStepCount = 0;
SleepUntilResume();
SleepUntilResume(BreakSource::PpuStep);
}
}
if(cycle == 0 && scanline == _breakScanline) {
_cpuStepCount = 0;
_breakScanline = -1;
SleepUntilResume();
SleepUntilResume(BreakSource::PpuStep);
}
}
void Debugger::SleepUntilResume()
void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operation, int breakpointId)
{
if(_suspendRequestCount) {
return;
@ -313,9 +318,15 @@ void Debugger::SleepUntilResume()
_executionStopped = true;
if(_cpuStepCount == 0) {
if(_cpuStepCount == 0 && _breakRequestCount == 0) {
//Only trigger code break event if the pause was caused by user action
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak);
BreakEvent evt = {};
evt.BreakpointId = breakpointId;
evt.Source = source;
if(operation) {
evt.Operation = *operation;
}
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak, &evt);
}
while((_cpuStepCount == 0 && !_suspendRequestCount) || _breakRequestCount) {
@ -333,14 +344,16 @@ void Debugger::ProcessStepConditions(uint8_t opCode, uint32_t currentPc)
}
}
void Debugger::ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo)
void Debugger::ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source)
{
if(_breakpointManager->CheckBreakpoint(operation, addressInfo)) {
_cpuStepCount = 0;
}
if(_cpuStepCount == 0 || _breakRequestCount) {
SleepUntilResume();
SleepUntilResume(source);
} else {
int breakpointId = _breakpointManager->CheckBreakpoint(operation, addressInfo);
if(breakpointId >= 0) {
_cpuStepCount = 0;
SleepUntilResume(BreakSource::Breakpoint, &operation, breakpointId);
}
}
}

View file

@ -71,9 +71,9 @@ private:
uint32_t _spcPrevProgramCounter = 0;
void Reset();
void SleepUntilResume();
void SleepUntilResume(BreakSource source, MemoryOperationInfo *operation = nullptr, int breakpointId = -1);
void ProcessStepConditions(uint8_t opCode, uint32_t currentPc);
void ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo);
void ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source = BreakSource::Unspecified);
public:
Debugger(shared_ptr<Console> console);

View file

@ -556,7 +556,7 @@ void ExpressionEvaluator::RunTests()
//Some basic unit tests to run in debug mode
auto test = [=](string expr, EvalResultType expectedType, int expectedResult) {
DebugState state = { 0 };
MemoryOperationInfo opInfo(0, 0, MemoryOperationType::Read);
MemoryOperationInfo opInfo { 0, 0, MemoryOperationType::Read };
EvalResultType type;
int32_t result = Evaluate(expr, state, type, opInfo);

View file

@ -125,5 +125,13 @@ namespace Mesen.GUI.Debugger
}
DebugApi.SetBreakpoints(breakpoints.ToArray(), (UInt32)breakpoints.Count);
}
public static Breakpoint GetBreakpointById(int breakpointId)
{
if(breakpointId < 0 || breakpointId >= _breakpoints.Count) {
return null;
}
return _breakpoints[breakpointId];
}
}
}

View file

@ -141,7 +141,7 @@ namespace Mesen.GUI.Debugger.Controls
public void SetActiveAddress(int? address)
{
if(_styleProvider.ActiveAddress == address) {
if(_styleProvider.ActiveAddress == null && address == null) {
return;
}
@ -275,5 +275,10 @@ namespace Mesen.GUI.Debugger.Controls
UpdateCode();
}
}
public void SetMessage(TextboxMessageInfo msg)
{
ctrlCode.SetMessage(msg);
}
}
}

View file

@ -1,5 +1,6 @@
using Mesen.GUI.Config;
using Mesen.GUI.Debugger.Code;
using Mesen.GUI.Debugger.Controls;
using Mesen.GUI.Debugger.Workspace;
using Mesen.GUI.Forms;
using System;
@ -8,6 +9,7 @@ using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -295,6 +297,27 @@ namespace Mesen.GUI.Debugger
ctrlCallstack.UpdateCallstack(_cpuType);
}
void ProcessBreakEvent(BreakEvent evt, DebugState state, int activeAddress)
{
if(ConfigManager.Config.Debug.Debugger.BringToFrontOnBreak) {
Breakpoint bp = BreakpointManager.GetBreakpointById(evt.BreakpointId);
if(bp?.MemoryType.ToCpuType() == _cpuType || evt.Source > BreakSource.PpuStep) {
DebugWindowManager.BringToFront(this);
}
}
UpdateContinueAction();
UpdateDebugger(state, activeAddress);
if(evt.Source == BreakSource.Breakpoint || evt.Source > BreakSource.PpuStep) {
string message = ResourceHelper.GetEnumText(evt.Source);
if(evt.Source == BreakSource.Breakpoint) {
message += ": " + ResourceHelper.GetEnumText(evt.Operation.Type) + " ($" + evt.Operation.Address.ToString("X4") + ":$" + evt.Operation.Value.ToString("X2") + ")";
}
ctrlDisassemblyView.SetMessage(new TextboxMessageInfo() { Message = message });
}
}
private void OnNotificationReceived(NotificationEventArgs e)
{
switch(e.NotificationType) {
@ -326,16 +349,12 @@ namespace Mesen.GUI.Debugger
break;
case ConsoleNotificationType.CodeBreak: {
BreakEvent evt = (BreakEvent)Marshal.PtrToStructure(e.Parameter, typeof(BreakEvent));
DebugState state = DebugApi.GetState();
int activeAddress = _cpuType == CpuType.Cpu ? (int)((state.Cpu.K << 16) | state.Cpu.PC) : (int)state.Spc.PC;
this.BeginInvoke((MethodInvoker)(() => {
if(ConfigManager.Config.Debug.Debugger.BringToFrontOnBreak) {
DebugWindowManager.BringToFront(this);
}
UpdateContinueAction();
UpdateDebugger(state, activeAddress);
ProcessBreakEvent(evt, state, activeAddress);
if(_firstBreak && !ConfigManager.Config.Debug.Debugger.BreakOnOpen) {
DebugApi.ResumeExecution();

View file

@ -1098,158 +1098,24 @@
<Value ID="Text">Text</Value>
<Value ID="CompactText">Text (Active only)</Value>
</Enum>
<Enum ID="MemorySizes">
<Value ID="None">None</Value>
<Value ID="_128Bytes">128 bytes</Value>
<Value ID="_256Bytes">256 bytes</Value>
<Value ID="_512Bytes">512 bytes</Value>
<Value ID="_1KB">1 KB</Value>
<Value ID="_2KB">2 KB</Value>
<Value ID="_4KB">4 KB</Value>
<Value ID="_8KB">8 KB</Value>
<Value ID="_16KB">16 KB</Value>
<Value ID="_32KB">32 KB</Value>
<Value ID="_64KB">64 KB</Value>
<Value ID="_128KB">128 KB</Value>
<Value ID="_256KB">256 KB</Value>
<Value ID="_512KB">512 KB</Value>
<Value ID="_1024KB">1024 KB</Value>
<Value ID="Reserved">Reserved</Value>
</Enum>
<Enum ID="FrameTiming">
<Value ID="Ntsc">NTSC</Value>
<Value ID="Pal">PAL</Value>
<Value ID="NtscAndPal">NTSC and PAL</Value>
<Value ID="Dendy">Dendy</Value>
</Enum>
<Enum ID="TvSystem">
<Value ID="NesFamicomDendy">NES / Famicom / Dendy</Value>
<Value ID="VsSystem">VS System</Value>
<Value ID="Playchoice">Playchoice-10</Value>
<Value ID="BitCorporationCreator">Bit Corporation Creator</Value>
<Value ID="Vt01Mono">VT01 Monochrome</Value>
<Value ID="Vt01RedCyan">VT01 Red/Cyan</Value>
<Value ID="Vt02">VT02</Value>
<Value ID="Vt03">VT03</Value>
<Value ID="Vt09">VT09</Value>
<Value ID="Vt3x">VT3x</Value>
<Value ID="Vt36x">VT36x</Value>
</Enum>
<Enum ID="iNesMirroringType">
<Value ID="Horizontal">Horizontal</Value>
<Value ID="Vertical">Vertical</Value>
<Value ID="FourScreens">Four Screens</Value>
</Enum>
<Enum ID="MirroringType">
<Value ID="Horizontal">Horizontal</Value>
<Value ID="Vertical">Vertical</Value>
<Value ID="ScreenAOnly">Screen A</Value>
<Value ID="ScreenBOnly">Screen B</Value>
<Value ID="FourScreens">Four Screens</Value>
</Enum>
<Enum ID="DebugEventType">
<Value ID="PpuRegisterWrite">PPU Register Write</Value>
<Value ID="PpuRegisterRead">PPU Register Read</Value>
<Value ID="MapperRegisterWrite">Mapper Register Write</Value>
<Value ID="MapperRegisterRead">Mapper Register Read</Value>
<Value ID="Nmi">NMI</Value>
<Value ID="Irq">IRQ</Value>
<Value ID="SpriteZeroHit">Sprite 0 Hit</Value>
</Enum>
<Enum ID="BreakpointType">
<Value ID="Global">Global</Value>
<Value ID="Execute">Execute</Value>
<Value ID="ReadRam">CPU Read</Value>
<Value ID="WriteRam">CPU Write</Value>
<Value ID="ReadVram">PPU Read</Value>
<Value ID="WriteVram">PPU Write</Value>
<Value ID="DummyReadRam">(Dummy) CPU Read</Value>
<Value ID="DummyWriteRam">(Dummy) CPU Write</Value>
</Enum>
<Enum ID="BreakSource">
<Value ID="Breakpoint">Breakpoint</Value>
<Value ID="CpuStep">CPU Step</Value>
<Value ID="PpuStep">PPU Step</Value>
<Value ID="BreakOnBrk">BRK</Value>
<Value ID="BreakOnUnofficialOpCode">Unofficial OP code</Value>
<Value ID="BreakOnReset">Reset event</Value>
<Value ID="BreakOnFocus">Debugger focused</Value>
<Value ID="BreakOnCop">COP</Value>
<Value ID="BreakOnWdm">WDM</Value>
<Value ID="BreakOnStp">STP</Value>
<Value ID="BreakOnUninitMemoryRead">Uninitialized memory read</Value>
<Value ID="BreakOnDecayedOamRead">Decayed OAM read</Value>
<Value ID="BreakOnCpuCrash">CPU crashed</Value>
</Enum>
<Enum ID="VsPpuType">
<Value ID="RP2C03B">RP2C03B</Value>
<Value ID="RP2C03G">RP2C03G</Value>
<Value ID="RP2C040001">RP2C04-0001</Value>
<Value ID="RP2C040002">RP2C04-0002</Value>
<Value ID="RP2C040003">RP2C04-0003</Value>
<Value ID="RP2C040004">RP2C04-0004</Value>
<Value ID="RC2C03B">RC2C03B</Value>
<Value ID="RC2C03C">RC2C03C</Value>
<Value ID="RC2C0501">RC2C05-01</Value>
<Value ID="RC2C0502">RC2C05-02</Value>
<Value ID="RC2C0503">RC2C05-03</Value>
<Value ID="RC2C0504">RC2C05-04</Value>
<Value ID="RC2C0505">RC2C05-05</Value>
<Value ID="Undefined">Undefined</Value>
<Value ID="Undefined2">Undefined (2)</Value>
<Value ID="Undefined3">Undefined (3)</Value>
</Enum>
<Enum ID="VsSystemType">
<Value ID="Default">Default</Value>
<Value ID="RbiBaseballProtection">RBI Baseball</Value>
<Value ID="TkoBoxingProtection">TKO Boxing</Value>
<Value ID="SuperXeviousProtection">Super Xevious</Value>
<Value ID="IceClimberProtection">Ice Climber</Value>
<Value ID="VsDualSystem">VS. Dual System</Value>
<Value ID="RaidOnBungelingBayProtection">Raid on Bungeling Bay</Value>
</Enum>
<Enum ID="GameInputType">
<Value ID="Unspecified">Unspecified</Value>
<Value ID="StandardControllers">Standard Controllers</Value>
<Value ID="FourScore">Four Score (NES)</Value>
<Value ID="FourPlayerAdapter">Four Player Adapter (Famicom)</Value>
<Value ID="VsSystem">VS System</Value>
<Value ID="VsSystemSwapped">VS System - Swap P1/P2</Value>
<Value ID="VsSystemSwapAB">VS System - Swap A/B buttons</Value>
<Value ID="VsZapper">VS Zapper</Value>
<Value ID="Zapper">Zapper</Value>
<Value ID="TwoZappers">2 zappers</Value>
<Value ID="BandaiHypershot">Bandai Hypershot</Value>
<Value ID="PowerPadSideA">Power Pad Side A</Value>
<Value ID="PowerPadSideB">Power Pad Side B</Value>
<Value ID="FamilyTrainerSideA">Family Trainer Side A</Value>
<Value ID="FamilyTrainerSideB">Family Trainer Side B</Value>
<Value ID="ArkanoidControllerNes">Arkanoid Controller (NES)</Value>
<Value ID="ArkanoidControllerFamicom">Arkanoid Controller (Famicom)</Value>
<Value ID="DoubleArkanoidController">2x Arkanoid Controllers (NES)</Value>
<Value ID="KonamiHyperShot">Konami Hyper Shot</Value>
<Value ID="PachinkoController">Pachinko Controller</Value>
<Value ID="ExcitingBoxing">Exciting Boxing</Value>
<Value ID="JissenMahjong">Jissen Mahjong</Value>
<Value ID="PartyTap">Party Tap</Value>
<Value ID="OekaKidsTablet">Oeka Kids Tablet</Value>
<Value ID="BarcodeBattler">Barcode Battler</Value>
<Value ID="MiraclePiano">Miracle Piano</Value>
<Value ID="PokkunMoguraa">Pokkun Moguraa</Value>
<Value ID="TopRider">Top Rider</Value>
<Value ID="DoubleFisted">Double Fisted</Value>
<Value ID="Famicom3dSystem">Famicom 3D System</Value>
<Value ID="DoremikkoKeyboard">Doremikko Keyboard</Value>
<Value ID="ROB">ROB</Value>
<Value ID="FamicomDataRecorder">Family Data Recorder</Value>
<Value ID="TurboFile">Turbo File</Value>
<Value ID="BattleBox">Battle Box</Value>
<Value ID="FamilyBasicKeyboard">Family Basic Keyboard</Value>
<Value ID="Pec586Keyboard">PEC-586 Keyboard</Value>
<Value ID="Bit79Keyboard">Bit-79 Keyboard</Value>
<Value ID="SuborKeyboard">Subor Keyboard</Value>
<Value ID="SuborKeyboardMouse1">Subor Keyboard+Mouse (Type A)</Value>
<Value ID="SuborKeyboardMouse2">Subor Keyboard+Mouse (Type B)</Value>
<Value ID="SnesMouse">SNES Mouse</Value>
<Value ID="GenericMulticart">Generic Multicart</Value>
<Value ID="SnesControllers">SNES Controllers</Value>
<Enum ID="MemoryOperationType">
<Value ID="Read">Read</Value>
<Value ID="Write">Write</Value>
<Value ID="ExecOpCode">Exec</Value>
<Value ID="ExecOperand">Exec</Value>
<Value ID="DmaRead">DMA Read</Value>
<Value ID="DmaWrite">DMA Write</Value>
<Value ID="DummyRead">Dummy Read</Value>
</Enum>
</Enums>
</Resources>

View file

@ -572,6 +572,25 @@ namespace Mesen.GUI
SpecificScanline,
}
public enum BreakSource
{
Unspecified = -1,
Breakpoint = 0,
CpuStep = 1,
PpuStep = 2,
BreakOnBrk = 3,
BreakOnCop = 4,
BreakOnWdm = 5,
BreakOnStp = 6
}
public struct BreakEvent
{
public BreakSource Source;
public MemoryOperationInfo Operation;
public Int32 BreakpointId;
}
public enum CdlFlags : byte
{
None = 0x00,