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 }