1 using Mesen.GUI.Forms; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Runtime.InteropServices; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace Mesen.GUI.Debugger 10 { 11 public class Breakpoint 12 { 13 private DebugMemoryType _memoryType = DebugMemoryType.CpuMemory; 14 private bool _isCpuBreakpoint = true; 15 private AddressType _equivalentAddressType = GUI.AddressType.InternalRam; 16 17 public DebugMemoryType MemoryType 18 { 19 get { return _memoryType; } 20 set 21 { 22 _memoryType = value; 23 _isCpuBreakpoint = IsTypeCpuBreakpoint(value); 24 if(_isCpuBreakpoint) { 25 _equivalentAddressType = value.ToAddressType(); 26 } 27 } 28 } 29 public bool BreakOnRead = false; 30 public bool BreakOnWrite = false; 31 public bool BreakOnExec = true; 32 33 public bool Enabled = true; 34 public bool MarkEvent = false; 35 public bool ProcessDummyReadWrites = false; 36 public UInt32 Address = UInt32.MaxValue; 37 public UInt32 StartAddress; 38 public UInt32 EndAddress; 39 public BreakpointAddressType AddressType = BreakpointAddressType.SingleAddress; 40 public string Condition = ""; 41 GetAddressString(bool showLabel)42 public string GetAddressString(bool showLabel) 43 { 44 string addr = ""; 45 switch(AddressType) { 46 case BreakpointAddressType.AnyAddress: return "<any>"; 47 case BreakpointAddressType.SingleAddress: 48 if(IsAbsoluteAddress) { 49 int relativeAddress = this.GetRelativeAddress(); 50 if(relativeAddress >= 0) { 51 addr += $"${relativeAddress.ToString("X4")} "; 52 } else { 53 addr += "N/A "; 54 } 55 addr += $"[${Address.ToString("X4")}]"; 56 } else { 57 addr = $"${Address.ToString("X4")}"; 58 } 59 break; 60 61 case BreakpointAddressType.AddressRange: 62 if(IsAbsoluteAddress) { 63 addr = $"[${StartAddress.ToString("X4")}] - [${EndAddress.ToString("X4")}]"; 64 } else { 65 addr = $"${StartAddress.ToString("X4")} - ${EndAddress.ToString("X4")}"; 66 } 67 break; 68 } 69 70 string label = GetAddressLabel(); 71 if(showLabel && !string.IsNullOrWhiteSpace(label)) { 72 addr = label + $", {addr}"; 73 } 74 return addr; 75 } 76 IsTypeCpuBreakpoint(DebugMemoryType type)77 public static bool IsTypeCpuBreakpoint(DebugMemoryType type) 78 { 79 return ( 80 type == DebugMemoryType.CpuMemory || 81 type == DebugMemoryType.WorkRam || 82 type == DebugMemoryType.SaveRam || 83 type == DebugMemoryType.PrgRom 84 ); 85 } 86 SetEnabled(bool enabled)87 public void SetEnabled(bool enabled) 88 { 89 Enabled = enabled; 90 BreakpointManager.RefreshBreakpoints(this); 91 } 92 SetMarked(bool marked)93 public void SetMarked(bool marked) 94 { 95 MarkEvent = marked; 96 BreakpointManager.RefreshBreakpoints(this); 97 } 98 99 public bool IsAbsoluteAddress { get { return MemoryType != DebugMemoryType.CpuMemory && MemoryType != DebugMemoryType.PpuMemory; } } 100 101 public bool IsCpuBreakpoint { get { return this._isCpuBreakpoint; } } 102 103 private BreakpointTypeFlags Type 104 { 105 get 106 { 107 BreakpointTypeFlags type = BreakpointTypeFlags.Global; 108 if(BreakOnRead) { 109 type |= IsCpuBreakpoint ? BreakpointTypeFlags.Read : BreakpointTypeFlags.ReadVram; 110 } 111 if(BreakOnWrite) { 112 type |= IsCpuBreakpoint ? BreakpointTypeFlags.Write : BreakpointTypeFlags.WriteVram; 113 } 114 if(BreakOnExec && IsCpuBreakpoint) { 115 type |= BreakpointTypeFlags.Execute; 116 } 117 return type; 118 } 119 } 120 ToReadableType()121 public string ToReadableType() 122 { 123 string type; 124 125 switch(MemoryType) { 126 default: throw new Exception("invalid type"); 127 case DebugMemoryType.CpuMemory: type = "CPU"; break; 128 case DebugMemoryType.PpuMemory: type = "PPU"; break; 129 case DebugMemoryType.PrgRom: type = "PRG"; break; 130 case DebugMemoryType.WorkRam: type = "WRAM"; break; 131 case DebugMemoryType.SaveRam: type = "SRAM"; break; 132 case DebugMemoryType.ChrRam: type = "CHR"; break; 133 case DebugMemoryType.ChrRom: type = "CHR"; break; 134 case DebugMemoryType.NametableRam: type = "NT"; break; 135 case DebugMemoryType.PaletteMemory: type = "PAL"; break; 136 } 137 138 type += ":"; 139 type += BreakOnRead ? "R" : "‒"; 140 type += BreakOnWrite ? "W" : "‒"; 141 if(IsCpuBreakpoint) { 142 type += BreakOnExec ? "X" : "‒"; 143 } 144 return type; 145 } 146 GetAddressLabel()147 public string GetAddressLabel() 148 { 149 UInt32 address = AddressType == BreakpointAddressType.SingleAddress ? this.Address : this.StartAddress; 150 151 if(IsCpuBreakpoint) { 152 CodeLabel label; 153 if(this.IsAbsoluteAddress) { 154 label = LabelManager.GetLabel(address, this.MemoryType.ToAddressType()); 155 } else { 156 label = LabelManager.GetLabel((UInt16)address); 157 } 158 if(label != null) { 159 return label.Label; 160 } 161 } 162 return string.Empty; 163 } 164 GetRelativeAddress()165 public int GetRelativeAddress() 166 { 167 UInt32 address = AddressType == BreakpointAddressType.SingleAddress ? this.Address : this.StartAddress; 168 if(this.IsAbsoluteAddress) { 169 if(IsCpuBreakpoint) { 170 return InteropEmu.DebugGetRelativeAddress(address, this.MemoryType.ToAddressType()); 171 } else { 172 return InteropEmu.DebugGetRelativePpuAddress(address, this.MemoryType.ToPpuAddressType()); 173 } 174 } 175 return -1; 176 } 177 GetRelativeAddressEnd()178 private int GetRelativeAddressEnd() 179 { 180 if(this.AddressType == BreakpointAddressType.AddressRange && this.IsAbsoluteAddress) { 181 if(IsCpuBreakpoint) { 182 return InteropEmu.DebugGetRelativeAddress(this.EndAddress, this.MemoryType.ToAddressType()); 183 } else { 184 return InteropEmu.DebugGetRelativePpuAddress(this.EndAddress, this.MemoryType.ToPpuAddressType()); 185 } 186 } 187 return -1; 188 } 189 Matches(int address, DebugMemoryType type)190 public bool Matches(int address, DebugMemoryType type) 191 { 192 if(IsTypeCpuBreakpoint(type) != this.IsCpuBreakpoint) { 193 return false; 194 } 195 196 bool isRelativeMemory = type == DebugMemoryType.CpuMemory || type == DebugMemoryType.PpuMemory; 197 198 if(this.AddressType == BreakpointAddressType.SingleAddress) { 199 if(isRelativeMemory && this.IsAbsoluteAddress) { 200 return address == this.GetRelativeAddress(); 201 } 202 return address == this.Address && type == this.MemoryType; 203 } else if(this.AddressType == BreakpointAddressType.AddressRange) { 204 if(isRelativeMemory && this.IsAbsoluteAddress) { 205 return address >= GetRelativeAddress() && address <= this.GetRelativeAddressEnd(); 206 } 207 return address >= this.StartAddress && address <= this.EndAddress && type == this.MemoryType; 208 } 209 210 return false; 211 } 212 Matches(int relativeAddress, AddressTypeInfo info)213 public bool Matches(int relativeAddress, AddressTypeInfo info) 214 { 215 if(this.IsCpuBreakpoint && this.AddressType == BreakpointAddressType.SingleAddress) { 216 if(this.MemoryType == DebugMemoryType.CpuMemory) { 217 return relativeAddress == this.Address; 218 } else { 219 return _equivalentAddressType == info.Type && info.Address == this.Address; 220 } 221 } 222 return false; 223 } 224 ToInteropBreakpoint(int breakpointId)225 public InteropBreakpoint ToInteropBreakpoint(int breakpointId) 226 { 227 InteropBreakpoint bp = new InteropBreakpoint() { 228 Id = breakpointId, 229 MemoryType = MemoryType, 230 Type = Type, 231 MarkEvent = MarkEvent, 232 ProcessDummyReadWrites = ProcessDummyReadWrites, 233 Enabled = Enabled 234 }; 235 switch(AddressType) { 236 case BreakpointAddressType.AnyAddress: 237 bp.StartAddress = -1; 238 bp.EndAddress = -1; 239 break; 240 241 case BreakpointAddressType.SingleAddress: 242 bp.StartAddress = (Int32)Address; 243 bp.EndAddress = -1; 244 break; 245 246 case BreakpointAddressType.AddressRange: 247 bp.StartAddress = (Int32)StartAddress; 248 bp.EndAddress = (Int32)EndAddress; 249 break; 250 } 251 252 bp.Condition = new byte[1000]; 253 byte[] condition = Encoding.UTF8.GetBytes(Condition.Replace(Environment.NewLine, " ")); 254 Array.Copy(condition, bp.Condition, condition.Length); 255 return bp; 256 } 257 } 258 259 public enum BreakpointAddressType 260 { 261 AnyAddress, 262 SingleAddress, 263 AddressRange, 264 } 265 }