using Mesen.GUI.Debugger.Labels; using System; using System.Text; namespace Mesen.GUI.Debugger { public class Breakpoint { private SnesMemoryType _memoryType = SnesMemoryType.CpuMemory; private bool _isCpuBreakpoint = true; public bool BreakOnRead = false; public bool BreakOnWrite = false; public bool BreakOnExec = true; public bool Enabled = true; public bool MarkEvent = false; public UInt32 Address = UInt32.MaxValue; public UInt32 StartAddress; public UInt32 EndAddress; public CpuType CpuType; public BreakpointAddressType AddressType = BreakpointAddressType.SingleAddress; public string Condition = ""; public SnesMemoryType MemoryType { get { return _memoryType; } set { _memoryType = value; _isCpuBreakpoint = IsTypeCpuBreakpoint(value); } } public string GetAddressString(bool showLabel) { string addr = ""; string format = _memoryType == SnesMemoryType.SpcMemory ? "X4" : "X6"; switch(AddressType) { case BreakpointAddressType.AnyAddress: return ""; case BreakpointAddressType.SingleAddress: addr += $"${Address.ToString(format)}"; break; case BreakpointAddressType.AddressRange: addr = $"${StartAddress.ToString(format)} - ${EndAddress.ToString(format)}"; break; } if(showLabel) { string label = GetAddressLabel(); if(!string.IsNullOrWhiteSpace(label)) { addr += " [" + label + "]"; } } return addr; } public static bool IsTypeCpuBreakpoint(SnesMemoryType type) { return ( type != SnesMemoryType.Register && type != SnesMemoryType.VideoRam && type != SnesMemoryType.CGRam && type != SnesMemoryType.SpriteRam ); } public void SetEnabled(bool enabled) { Enabled = enabled; BreakpointManager.RefreshBreakpoints(this); } public void SetMarked(bool marked) { MarkEvent = marked; BreakpointManager.RefreshBreakpoints(this); } public bool IsAbsoluteAddress { get { return !MemoryType.IsRelativeMemory(); } } public bool IsCpuBreakpoint { get { return this._isCpuBreakpoint; } } private BreakpointTypeFlags Type { get { BreakpointTypeFlags type = BreakpointTypeFlags.None; if(BreakOnRead) { type |= BreakpointTypeFlags.Read; } if(BreakOnWrite) { type |= BreakpointTypeFlags.Write; } if(BreakOnExec && IsCpuBreakpoint) { type |= BreakpointTypeFlags.Execute; } return type; } } public string ToReadableType() { string type; switch(MemoryType) { default: throw new Exception("invalid type"); case SnesMemoryType.CpuMemory: type = "CPU"; break; case SnesMemoryType.SpcMemory: type = "SPC"; break; case SnesMemoryType.Sa1Memory: type = "SA1"; break; case SnesMemoryType.GsuMemory: type = "GSU"; break; case SnesMemoryType.PrgRom: type = "PRG"; break; case SnesMemoryType.WorkRam: type = "WRAM"; break; case SnesMemoryType.SaveRam: type = "SRAM"; break; case SnesMemoryType.VideoRam: type = "VRAM"; break; case SnesMemoryType.SpriteRam: type = "OAM"; break; case SnesMemoryType.CGRam: type = "CG"; break; case SnesMemoryType.SpcRam: type = "RAM"; break; case SnesMemoryType.SpcRom: type = "ROM"; break; case SnesMemoryType.Sa1InternalRam: type = "IRAM"; break; case SnesMemoryType.GsuWorkRam: type = "GWRAM"; break; case SnesMemoryType.Register: type = "REG"; break; } type += ":"; type += BreakOnRead ? "R" : "‒"; type += BreakOnWrite ? "W" : "‒"; if(IsCpuBreakpoint) { type += BreakOnExec ? "X" : "‒"; } return type; } public string GetAddressLabel() { UInt32 address = AddressType == BreakpointAddressType.SingleAddress ? this.Address : this.StartAddress; if(IsCpuBreakpoint) { CodeLabel label; if(this.IsAbsoluteAddress) { label = LabelManager.GetLabel(address, this.MemoryType); } else { label = LabelManager.GetLabel(new AddressInfo() { Address = (int)address, Type = this.MemoryType }); } if(label != null) { return label.Label; } } return string.Empty; } public int GetRelativeAddress() { UInt32 address = AddressType == BreakpointAddressType.SingleAddress ? this.Address : this.StartAddress; if(IsCpuBreakpoint && this.IsAbsoluteAddress) { return DebugApi.GetRelativeAddress(new AddressInfo() { Address = (int)address, Type = this.MemoryType }).Address; } else { return (int)address; } } private int GetRelativeAddressEnd() { if(this.AddressType == BreakpointAddressType.AddressRange){ if(IsCpuBreakpoint && this.IsAbsoluteAddress) { return DebugApi.GetRelativeAddress(new AddressInfo() { Address = (int)this.EndAddress, Type = this.MemoryType }).Address; } else { return (int)this.EndAddress; } } return -1; } public bool Matches(UInt32 address, SnesMemoryType type, CpuType? cpuType) { if((cpuType.HasValue && cpuType.Value != this.CpuType) || IsTypeCpuBreakpoint(type) != this.IsCpuBreakpoint) { return false; } if(this.AddressType == BreakpointAddressType.SingleAddress) { return address == this.Address && type == this.MemoryType; } else if(this.AddressType == BreakpointAddressType.AddressRange) { return address >= this.StartAddress && address <= this.EndAddress && type == this.MemoryType; } return false; } public InteropBreakpoint ToInteropBreakpoint(int breakpointId) { InteropBreakpoint bp = new InteropBreakpoint() { Id = breakpointId, CpuType = CpuType, MemoryType = MemoryType, Type = Type, MarkEvent = MarkEvent, Enabled = Enabled }; switch(AddressType) { case BreakpointAddressType.AnyAddress: bp.StartAddress = -1; bp.EndAddress = -1; break; case BreakpointAddressType.SingleAddress: bp.StartAddress = (Int32)Address; bp.EndAddress = -1; break; case BreakpointAddressType.AddressRange: bp.StartAddress = (Int32)StartAddress; bp.EndAddress = (Int32)EndAddress; break; } bp.Condition = new byte[1000]; byte[] condition = Encoding.UTF8.GetBytes(Condition.Replace(Environment.NewLine, " ")); Array.Copy(condition, bp.Condition, condition.Length); return bp; } } public enum BreakpointAddressType { AnyAddress, SingleAddress, AddressRange, } [Flags] public enum BreakpointTypeFlags { None = 0, Execute = 1, Read = 2, Write = 4, } }