BizHawk/BizHawk.Client.EmuHawk/tools/Debugger/BreakpointControl.cs

334 lines
8.5 KiB
C#
Raw Normal View History

2014-12-06 15:07:01 +00:00
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Common.NumberExtensions;
2014-12-06 15:07:01 +00:00
using BizHawk.Emulation.Common;
using BizHawk.Client.EmuHawk.WinFormExtensions;
2014-12-06 15:07:01 +00:00
2017-05-24 14:06:31 +00:00
namespace BizHawk.Client.EmuHawk
2014-12-06 15:07:01 +00:00
{
public partial class BreakpointControl : UserControl
{
2019-12-22 18:06:46 +00:00
public MainForm MainForm { get; set; }
2014-12-06 15:07:01 +00:00
public IDebuggable Core { get; set; }
2019-12-22 18:06:46 +00:00
public IMemoryCallbackSystem Mcs { get; set; }
2014-12-06 15:07:01 +00:00
public GenericDebugger ParentDebugger { get; set; }
public IMemoryDomains MemoryDomains { get; set; }
2017-05-24 14:06:31 +00:00
private readonly BreakpointList _breakpoints = new BreakpointList();
2014-12-06 15:07:01 +00:00
public BreakpointControl()
{
InitializeComponent();
BreakpointView.RetrieveVirtualItem += BreakPointView_QueryItemText;
2014-12-06 15:07:01 +00:00
BreakpointView.VirtualMode = true;
2017-05-24 14:06:31 +00:00
_breakpoints.Callback = BreakpointCallback;
2014-12-06 15:07:01 +00:00
}
private void BreakpointControl_Load(object sender, EventArgs e)
{
UpdateStatsLabel();
2014-12-06 15:07:01 +00:00
}
private void BreakPointView_QueryItemText(object sender, RetrieveVirtualItemEventArgs e)
2014-12-06 15:07:01 +00:00
{
var entry = _breakpoints[e.ItemIndex];
e.Item = new ListViewItem($"{entry.Address:X}");
e.Item.SubItems.Add($"{entry.AddressMask:X}");
e.Item.SubItems.Add(entry.Type.ToString());
e.Item.SubItems.Add(entry.Name);
e.Item.BackColor = entry.ReadOnly ? SystemColors.Control
: entry.Active ? Color.LightCyan
: Color.White;
}
2019-06-06 09:04:47 +00:00
private void BreakpointCallback(uint addr, uint value, uint flags)
2014-12-06 15:07:01 +00:00
{
2019-12-22 18:06:46 +00:00
MainForm.PauseEmulator();
2014-12-06 15:07:01 +00:00
UpdateValues();
2019-12-22 18:06:46 +00:00
MainForm.AddOnScreenMessage("Breakpoint hit");
2014-12-06 15:07:01 +00:00
}
2019-06-06 09:04:47 +00:00
private void SeekCallback(uint addr, uint value, uint flags)
2014-12-06 15:07:01 +00:00
{
2019-06-06 09:04:47 +00:00
BreakpointCallback(addr, value, flags);
2017-05-24 14:06:31 +00:00
var seekBreakpoint = _breakpoints.FirstOrDefault(x => x.Name.StartsWith(SeekName));
if (seekBreakpoint != null)
2014-12-06 15:07:01 +00:00
{
2017-05-24 14:06:31 +00:00
_breakpoints.Remove(seekBreakpoint);
UpdateValues();
}
2015-01-21 23:04:47 +00:00
ParentDebugger.DisableCancelSeekBtn();
}
2014-12-06 15:07:01 +00:00
public void UpdateValues()
{
if (Enabled)
{
CheckForNewBreakpoints();
BreakpointView.VirtualListSize = _breakpoints.Count;
UpdateStatsLabel();
2014-12-06 15:07:01 +00:00
}
}
2017-05-24 14:06:31 +00:00
// Did any breakpoints get added from other sources such as lua?
private void CheckForNewBreakpoints()
{
2019-12-22 18:06:46 +00:00
if (Mcs != null)
{
2019-12-22 18:06:46 +00:00
foreach (var callback in Mcs)
{
2017-05-24 14:06:31 +00:00
if (!_breakpoints.Any(b =>
b.Type == callback.Type &&
b.Address == callback.Address &&
b.AddressMask == callback.AddressMask &&
b.Name == callback.Name &&
2017-05-24 14:06:31 +00:00
b.Callback == callback.Callback))
{
2017-05-24 14:06:31 +00:00
_breakpoints.Add(new Breakpoint(Core, callback));
}
}
}
}
2014-12-06 15:07:01 +00:00
public void GenerateUI()
{
2019-12-22 18:06:46 +00:00
if (Mcs != null)
2014-12-06 15:07:01 +00:00
{
2019-12-22 18:06:46 +00:00
foreach (var callback in Mcs)
{
2017-05-24 14:06:31 +00:00
_breakpoints.Add(new Breakpoint(Core, callback));
}
BreakpointView.VirtualListSize = _breakpoints.Count;
BreakpointView.Refresh();
UpdateBreakpointRemoveButton();
UpdateStatsLabel();
2014-12-06 15:07:01 +00:00
}
else
{
2017-05-24 14:06:31 +00:00
Enabled = false;
ParentDebugger.DisableBreakpointBox();
2014-12-06 15:07:01 +00:00
}
}
public void Shutdown()
{
2017-05-24 14:06:31 +00:00
_breakpoints.Clear();
UpdateStatsLabel();
2014-12-06 15:07:01 +00:00
}
public void AddBreakpoint(uint address, uint mask, MemoryCallbackType type)
{
_breakpoints.Add(Core, MemoryDomains.SystemBus.Name, address, mask, type);
BreakpointView.VirtualListSize = _breakpoints.Count;
UpdateBreakpointRemoveButton();
UpdateStatsLabel();
}
2014-12-06 15:07:01 +00:00
private void AddBreakpointButton_Click(object sender, EventArgs e)
{
var b = CreateAddBreakpointDialog(BreakpointOperation.Add);
if (b.ShowHawkDialog() == DialogResult.OK)
2014-12-06 15:07:01 +00:00
{
_breakpoints.Add(Core, MemoryDomains.SystemBus.Name, b.Address, b.AddressMask, b.BreakType);
2014-12-06 15:07:01 +00:00
}
BreakpointView.VirtualListSize = _breakpoints.Count;
2014-12-06 15:07:01 +00:00
UpdateBreakpointRemoveButton();
UpdateStatsLabel();
2014-12-06 15:07:01 +00:00
}
private const string SeekName = "Seek to PC 0x";
public void AddSeekBreakpoint(uint pcVal, int pcBitSize)
{
2017-05-24 14:06:31 +00:00
var name = SeekName + pcVal.ToHexString(pcBitSize / 4);
_breakpoints.Add(new Breakpoint(name, true, Core, MemoryDomains.SystemBus.Name, SeekCallback, pcVal, 0xFFFFFFFF, MemoryCallbackType.Execute));
}
2015-01-21 23:04:47 +00:00
public void RemoveCurrentSeek()
{
2017-05-24 14:06:31 +00:00
var seekBreakpoint = _breakpoints.FirstOrDefault(x => x.Name.StartsWith(SeekName));
2015-01-21 23:04:47 +00:00
if (seekBreakpoint != null)
{
2017-05-24 14:06:31 +00:00
_breakpoints.Remove(seekBreakpoint);
2015-01-21 23:04:47 +00:00
UpdateValues();
}
}
2017-05-24 14:06:31 +00:00
private IEnumerable<int> SelectedIndices => BreakpointView.SelectedIndices.Cast<int>();
2019-12-22 18:06:46 +00:00
private IEnumerable<Breakpoint> SelectedItems => SelectedIndices.Select(index => _breakpoints[index]);
2014-12-06 15:07:01 +00:00
2019-12-22 18:06:46 +00:00
private IEnumerable<Breakpoint> EditableItems => SelectedItems.Where(item => !item.ReadOnly);
2014-12-06 15:07:01 +00:00
private void RemoveBreakpointButton_Click(object sender, EventArgs e)
{
if (EditableItems.Any())
2014-12-06 15:07:01 +00:00
{
var items = EditableItems.ToList();
2014-12-06 15:07:01 +00:00
if (items.Any())
{
foreach (var item in items)
{
2017-05-24 14:06:31 +00:00
_breakpoints.Remove(item);
2014-12-06 15:07:01 +00:00
}
BreakpointView.VirtualListSize = _breakpoints.Count;
2014-12-06 15:07:01 +00:00
UpdateBreakpointRemoveButton();
UpdateStatsLabel();
2014-12-06 15:07:01 +00:00
}
}
}
private void UpdateBreakpointRemoveButton()
{
ToggleButton.Enabled =
RemoveBreakpointButton.Enabled =
EditableItems.Any();
DuplicateBreakpointButton.Enabled =
EditBreakpointButton.Enabled =
EditableItems.Count() == 1;
2014-12-06 15:07:01 +00:00
}
private void BreakpointView_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateBreakpointRemoveButton();
}
private void BreakpointView_ItemActivate(object sender, EventArgs e)
{
if (EditableItems.Any())
{
var items = EditableItems.ToList();
if (items.Any())
{
foreach (var item in items)
{
item.Active ^= true;
}
BreakpointView.VirtualListSize = _breakpoints.Count;
UpdateBreakpointRemoveButton();
UpdateStatsLabel();
}
}
}
private void BreakpointView_KeyDown(object sender, KeyEventArgs e)
{
if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.Delete) // Delete
{
RemoveBreakpointButton_Click(null, null);
}
}
private void UpdateStatsLabel()
{
2017-05-24 14:06:31 +00:00
BreakpointStatsLabel.Text = $"{_breakpoints.Count} Total / {_breakpoints.Count(b => b.Active)} Active";
}
private void ToggleButton_Click(object sender, EventArgs e)
{
foreach (var item in SelectedItems)
{
item.Active ^= true;
}
BreakpointView.VirtualListSize = _breakpoints.Count;
UpdateStatsLabel();
}
private void DuplicateBreakpointButton_Click(object sender, EventArgs e)
{
var breakpoint = SelectedItems.FirstOrDefault();
if (breakpoint != null && !breakpoint.ReadOnly)
{
var b = CreateAddBreakpointDialog(BreakpointOperation.Duplicate, breakpoint.Type, breakpoint.Address, breakpoint.AddressMask);
if (b.ShowHawkDialog() == DialogResult.OK)
{
_breakpoints.Add(new Breakpoint(Core, MemoryDomains.SystemBus.Name, breakpoint.Callback, b.Address, b.AddressMask, b.BreakType, breakpoint.Active));
}
}
BreakpointView.VirtualListSize = _breakpoints.Count;
UpdateBreakpointRemoveButton();
UpdateStatsLabel();
}
private void EditBreakpointButton_Click(object sender, EventArgs e)
{
var breakpoint = SelectedItems.FirstOrDefault();
if (breakpoint != null && !breakpoint.ReadOnly)
{
var b = CreateAddBreakpointDialog(BreakpointOperation.Edit, breakpoint.Type, breakpoint.Address, breakpoint.AddressMask);
if (b.ShowHawkDialog() == DialogResult.OK)
{
breakpoint.Type = b.BreakType;
breakpoint.Address = b.Address;
breakpoint.AddressMask = b.AddressMask;
breakpoint.ResetCallback();
}
}
BreakpointView.VirtualListSize = _breakpoints.Count;
UpdateBreakpointRemoveButton();
UpdateStatsLabel();
}
private AddBreakpointDialog CreateAddBreakpointDialog(BreakpointOperation op, MemoryCallbackType? type = null, uint? address = null, uint? mask = null)
{
var operation = (AddBreakpointDialog.BreakpointOperation)op;
var b = new AddBreakpointDialog(operation)
{
MaxAddressSize = MemoryDomains.SystemBus.Size - 1
};
if (type != null)
{
b.BreakType = (MemoryCallbackType)type;
}
if (address != null)
{
b.Address = (uint)address;
}
if (mask != null)
{
b.AddressMask = (uint)mask;
}
2019-12-22 18:06:46 +00:00
if (!Mcs.ExecuteCallbacksAvailable)
{
b.DisableExecuteOption();
}
return b;
}
2017-05-24 14:06:31 +00:00
private enum BreakpointOperation
{
Add, Edit, Duplicate
}
2014-12-06 15:07:01 +00:00
}
}