1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Drawing;
5 using System.Data;
6 using System.Linq;
7 using System.Text;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
10 using Mesen.GUI.Config;
11 using System.Globalization;
12 using System.Text.RegularExpressions;
13 
14 namespace Mesen.GUI.Debugger.Controls
15 {
16 	public partial class CodeViewerActions : UserControl
17 	{
18 		public event SetNextStatementEventHandler OnSetNextStatement;
19 		public event ShowInSplitViewEventHandler OnShowInSplitView;
20 		public event ShowInSplitViewEventHandler OnGoToDestination;
21 		public event SwitchToSourceEventHandler OnSwitchView;
22 
23 		private int _lastClickedAddress = Int32.MaxValue;
24 		private Ld65DbgImporter.SymbolInfo _lastClickedSymbol = null;
25 		private CodeLabel _lastClickedLabel = null;
26 		private string _newWatchValue = string.Empty;
27 		private string _lastWord = string.Empty;
28 		private Point _lastLocation = Point.Empty;
29 		private DebugViewInfo _config;
30 
31 		public ICodeViewer Viewer { get; set; }
32 		public bool IsSourceView { get; private set; }
33 
CodeViewerActions()34 		public CodeViewerActions()
35 		{
36 			InitializeComponent();
37 		}
38 
CodeViewerActions(ICodeViewer viewer, bool isSourceView)39 		public CodeViewerActions(ICodeViewer viewer, bool isSourceView) : this()
40 		{
41 			Viewer = viewer;
42 			IsSourceView = isSourceView;
43 
44 			this.InitShortcuts();
45 		}
46 
InitShortcuts()47 		private void InitShortcuts()
48 		{
49 			Control parent = (Control)Viewer;
50 			mnuEditInMemoryViewer.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_EditInMemoryViewer));
51 			mnuEditLabel.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_EditLabel));
52 			mnuSetNextStatement.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_SetNextStatement));
53 			mnuShowNextStatement.InitShortcut(parent, nameof(DebuggerShortcutsConfig.GoToProgramCounter));
54 			mnuToggleBreakpoint.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_ToggleBreakpoint));
55 
56 			mnuUndoPrgChrEdit.InitShortcut(parent, nameof(DebuggerShortcutsConfig.Undo));
57 			mnuCopySelection.InitShortcut(parent, nameof(DebuggerShortcutsConfig.Copy));
58 
59 			mnuSwitchView.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_SwitchView));
60 
61 			if(!IsSourceView) {
62 				mnuNavigateBackward.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_NavigateBack));
63 				mnuNavigateForward.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_NavigateForward));
64 
65 				mnuEditSelectedCode.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_EditSelectedCode));
66 				mnuEditSubroutine.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_EditSubroutine));
67 
68 				mnuMarkAsCode.InitShortcut(parent, nameof(DebuggerShortcutsConfig.MarkAsCode));
69 				mnuMarkAsData.InitShortcut(parent, nameof(DebuggerShortcutsConfig.MarkAsData));
70 				mnuMarkAsUnidentifiedData.InitShortcut(parent, nameof(DebuggerShortcutsConfig.MarkAsUnidentified));
71 			} else {
72 				mnuEditSourceFile.InitShortcut(parent, nameof(DebuggerShortcutsConfig.CodeWindow_EditSourceFile));
73 			}
74 		}
75 
InitMenu(DebugViewInfo config)76 		public void InitMenu(DebugViewInfo config)
77 		{
78 			_config = config;
79 			mnuPrgShowInline.Checked = false;
80 			mnuPrgAddressReplace.Checked = false;
81 			mnuPrgAddressBelow.Checked = false;
82 			mnuHidePrgAddresses.Checked = false;
83 
84 			mnuShowByteCodeOnLeft.Checked = false;
85 			mnuShowByteCodeBelow.Checked = false;
86 			mnuHideByteCode.Checked = false;
87 
88 			mnuShowSourceAsComments.Checked = config.ShowSourceAsComments;
89 
90 			switch(config.ByteCodePosition) {
91 				case ByteCodePosition.Left:
92 					Viewer.CodeViewer.ShowContentNotes = true;
93 					Viewer.CodeViewer.ShowSingleContentLineNotes = true;
94 					this.mnuShowByteCodeOnLeft.Checked = true;
95 					break;
96 
97 				case ByteCodePosition.Below:
98 					Viewer.CodeViewer.ShowContentNotes = true;
99 					Viewer.CodeViewer.ShowSingleContentLineNotes = false;
100 					this.mnuShowByteCodeBelow.Checked = true;
101 					break;
102 
103 				case ByteCodePosition.Hidden:
104 					Viewer.CodeViewer.ShowContentNotes = false;
105 					Viewer.CodeViewer.ShowSingleContentLineNotes = false;
106 					this.mnuHideByteCode.Checked = true;
107 					break;
108 			}
109 
110 			switch(config.PrgAddressPosition) {
111 				case PrgAddressPosition.Inline:
112 					Viewer.CodeViewer.ShowCompactPrgAddresses = true;
113 					Viewer.CodeViewer.ShowLineNumberNotes = false;
114 					Viewer.CodeViewer.ShowSingleLineLineNumberNotes = false;
115 					this.mnuPrgShowInline.Checked = true;
116 					break;
117 
118 				case PrgAddressPosition.Replace:
119 					Viewer.CodeViewer.ShowCompactPrgAddresses = false;
120 					Viewer.CodeViewer.ShowLineNumberNotes = true;
121 					Viewer.CodeViewer.ShowSingleLineLineNumberNotes = true;
122 					this.mnuPrgAddressReplace.Checked = true;
123 					break;
124 
125 				case PrgAddressPosition.Below:
126 					Viewer.CodeViewer.ShowCompactPrgAddresses = false;
127 					Viewer.CodeViewer.ShowLineNumberNotes = true;
128 					Viewer.CodeViewer.ShowSingleLineLineNumberNotes = false;
129 					this.mnuPrgAddressBelow.Checked = true;
130 					break;
131 
132 				case PrgAddressPosition.Hidden:
133 					Viewer.CodeViewer.ShowCompactPrgAddresses = false;
134 					Viewer.CodeViewer.ShowLineNumberNotes = false;
135 					Viewer.CodeViewer.ShowSingleLineLineNumberNotes = false;
136 					this.mnuHidePrgAddresses.Checked = true;
137 					break;
138 			}
139 		}
140 
UpdateConfig()141 		private void UpdateConfig()
142 		{
143 			this.InitMenu(_config);
144 			ConfigManager.ApplyChanges();
145 		}
146 
contextMenuCode_Opening(object sender, CancelEventArgs e)147 		private void contextMenuCode_Opening(object sender, CancelEventArgs e)
148 		{
149 			UpdateContextMenuItemVisibility(contextMenu.Items);
150 
151 			int startAddress, endAddress;
152 			string range;
153 			GetSelectedAddressRange(out startAddress, out endAddress, out range);
154 			mnuMarkSelectionAs.Enabled = startAddress >= 0 && endAddress >= 0 && startAddress <= endAddress;
155 			if(mnuMarkSelectionAs.Enabled) {
156 				mnuMarkSelectionAs.Text = "Mark selection as... (" + range + ")";
157 			} else {
158 				mnuMarkSelectionAs.Text = "Mark selection as...";
159 			}
160 		}
161 
GetSelectedAddressRange(out int start, out int end, out string range)162 		private void GetSelectedAddressRange(out int start, out int end, out string range)
163 		{
164 			int firstLineOfSelection = Viewer.CodeViewer.SelectionStart;
165 			while(Viewer.CodeViewer.GetLineNumber(firstLineOfSelection) < 0) {
166 				firstLineOfSelection++;
167 			}
168 			int firstLineAfterSelection = Viewer.CodeViewer.SelectionStart + Viewer.CodeViewer.SelectionLength + 1;
169 			while(Viewer.CodeViewer.GetLineNumber(firstLineAfterSelection) < 0) {
170 				firstLineAfterSelection++;
171 			}
172 			start = Viewer.CodeViewer.GetLineNumber(firstLineOfSelection);
173 			end = Viewer.CodeViewer.GetLineNumber(firstLineAfterSelection) - 1;
174 
175 			range = "";
176 			if(start >= 0 && end >= 0) {
177 				range = $"${start.ToString("X4")} - ${end.ToString("X4")}";
178 				start = InteropEmu.DebugGetAbsoluteAddress((UInt32)start);
179 				end = InteropEmu.DebugGetAbsoluteAddress((UInt32)end);
180 			}
181 		}
182 
MarkSelectionAs(CdlPrgFlags type)183 		private void MarkSelectionAs(CdlPrgFlags type)
184 		{
185 			int startAddress, endAddress;
186 			string range;
187 			GetSelectedAddressRange(out startAddress, out endAddress, out range);
188 
189 			if(startAddress >= 0 && endAddress >= 0 && startAddress <= endAddress) {
190 				InteropEmu.DebugMarkPrgBytesAs((UInt32)startAddress, (UInt32)endAddress, type);
191 
192 				frmDebugger debugger = DebugWindowManager.GetDebugger();
193 				if(debugger != null) {
194 					debugger.UpdateDebugger(false);
195 				}
196 			}
197 		}
198 
mnuMarkAsCode_Click(object sender, EventArgs e)199 		private void mnuMarkAsCode_Click(object sender, EventArgs e)
200 		{
201 			this.MarkSelectionAs(CdlPrgFlags.Code);
202 		}
203 
mnuMarkAsData_Click(object sender, EventArgs e)204 		private void mnuMarkAsData_Click(object sender, EventArgs e)
205 		{
206 			this.MarkSelectionAs(CdlPrgFlags.Data);
207 		}
208 
mnuMarkAsUnidentifiedData_Click(object sender, EventArgs e)209 		private void mnuMarkAsUnidentifiedData_Click(object sender, EventArgs e)
210 		{
211 			this.MarkSelectionAs(CdlPrgFlags.None);
212 		}
213 
mnuGoToLocation_Click(object sender, EventArgs e)214 		private void mnuGoToLocation_Click(object sender, EventArgs e)
215 		{
216 			GoToLocation();
217 		}
218 
GoToDestination(GoToDestination dest)219 		public void GoToDestination(GoToDestination dest)
220 		{
221 			this.OnGoToDestination?.Invoke(Viewer, dest);
222 		}
223 
GetDestination()224 		private GoToDestination GetDestination()
225 		{
226 			Ld65DbgImporter.ReferenceInfo definitionInfo = _lastClickedSymbol != null ? Viewer.SymbolProvider?.GetSymbolDefinition(_lastClickedSymbol) : null;
227 			AddressTypeInfo addressInfo = _lastClickedSymbol != null ? Viewer.SymbolProvider?.GetSymbolAddressInfo(_lastClickedSymbol) : null;
228 			return new GoToDestination() {
229 				CpuAddress = _lastClickedAddress >= 0 ? _lastClickedAddress : (addressInfo != null ? InteropEmu.DebugGetRelativeAddress((UInt32)addressInfo.Address, addressInfo.Type) : -1),
230 				Label = _lastClickedLabel,
231 				AddressInfo = addressInfo,
232 				File = definitionInfo?.FileName,
233 				Line = definitionInfo?.LineNumber ?? 0
234 			};
235 		}
236 
GoToLocation()237 		private void GoToLocation()
238 		{
239 			GoToDestination(GetDestination());
240 		}
241 
mnuAddToWatch_Click(object sender, EventArgs e)242 		private void mnuAddToWatch_Click(object sender, EventArgs e)
243 		{
244 			AddWatch();
245 		}
246 
AddWatch()247 		private void AddWatch()
248 		{
249 			WatchManager.AddWatch(_newWatchValue);
250 		}
251 
mnuShowInSplitView_Click(object sender, EventArgs e)252 		private void mnuShowInSplitView_Click(object sender, EventArgs e)
253 		{
254 			ShowInSplitView();
255 		}
256 
ShowInSplitView()257 		private void ShowInSplitView()
258 		{
259 			this.OnShowInSplitView?.Invoke(Viewer, GetDestination());
260 		}
261 
mnuEditLabel_Click(object sender, EventArgs e)262 		private void mnuEditLabel_Click(object sender, EventArgs e)
263 		{
264 			if(UpdateContextMenu(_lastLocation)) {
265 				if(_lastClickedAddress >= 0) {
266 					AddressTypeInfo info = new AddressTypeInfo();
267 					InteropEmu.DebugGetAbsoluteAddressAndType((UInt32)_lastClickedAddress, info);
268 					if(info.Address >= 0) {
269 						ctrlLabelList.EditLabel((UInt32)info.Address, info.Type);
270 					} else {
271 						ctrlLabelList.EditLabel((UInt32)_lastClickedAddress, AddressType.Register);
272 					}
273 				} else if(_lastClickedLabel != null) {
274 					ctrlLabelList.EditLabel(_lastClickedLabel.Address, _lastClickedLabel.AddressType);
275 				} else if(_lastClickedSymbol != null) {
276 					AddressTypeInfo info = Viewer.SymbolProvider.GetSymbolAddressInfo(_lastClickedSymbol);
277 					if(info != null && info.Address >= 0) {
278 						ctrlLabelList.EditLabel((UInt32)info.Address, info.Type);
279 					}
280 				}
281 			}
282 		}
283 
mnuNavigateForward_Click(object sender, EventArgs e)284 		private void mnuNavigateForward_Click(object sender, EventArgs e)
285 		{
286 			Viewer.CodeViewer.NavigateForward();
287 		}
288 
mnuNavigateBackward_Click(object sender, EventArgs e)289 		private void mnuNavigateBackward_Click(object sender, EventArgs e)
290 		{
291 			Viewer.CodeViewer.NavigateBackward();
292 		}
293 
mnuToggleBreakpoint_Click(object sender, EventArgs e)294 		private void mnuToggleBreakpoint_Click(object sender, EventArgs e)
295 		{
296 			this.ToggleBreakpoint(false);
297 		}
298 
ToggleBreakpoint(bool toggleEnabledFlag)299 		public void ToggleBreakpoint(bool toggleEnabledFlag)
300 		{
301 			AddressTypeInfo info = Viewer.GetAddressInfo(Viewer.CodeViewer.SelectedLine);
302 			if(info.Address < 0) {
303 				//Current line has no address, try using the next line instead.
304 				//(Used when trying to set a breakpoint on a row containing only a label)
305 				info = Viewer.GetAddressInfo(Viewer.CodeViewer.SelectedLine + 1);
306 			}
307 
308 			if(info.Address >= 0) {
309 				BreakpointManager.ToggleBreakpoint(info, toggleEnabledFlag);
310 			}
311 		}
312 
mnuShowByteCodeOnLeft_Click(object sender, EventArgs e)313 		private void mnuShowByteCodeOnLeft_Click(object sender, EventArgs e)
314 		{
315 			_config.ByteCodePosition = ByteCodePosition.Left;
316 			this.UpdateConfig();
317 		}
318 
mnuShowByteCodeBelow_Click(object sender, EventArgs e)319 		private void mnuShowByteCodeBelow_Click(object sender, EventArgs e)
320 		{
321 			_config.ByteCodePosition = ByteCodePosition.Below;
322 			this.UpdateConfig();
323 		}
324 
mnuHideByteCode_Click(object sender, EventArgs e)325 		private void mnuHideByteCode_Click(object sender, EventArgs e)
326 		{
327 			_config.ByteCodePosition = ByteCodePosition.Hidden;
328 			this.UpdateConfig();
329 		}
330 
mnuShowSourceAsComments_Click(object sender, EventArgs e)331 		private void mnuShowSourceAsComments_Click(object sender, EventArgs e)
332 		{
333 			_config.ShowSourceAsComments = mnuShowSourceAsComments.Checked;
334 			this.UpdateConfig();
335 		}
336 
mnuShowInlineCompactDisplay_Click(object sender, EventArgs e)337 		private void mnuShowInlineCompactDisplay_Click(object sender, EventArgs e)
338 		{
339 			_config.PrgAddressPosition = PrgAddressPosition.Inline;
340 			this.UpdateConfig();
341 		}
342 
mnuReplaceCpuAddress_Click(object sender, EventArgs e)343 		private void mnuReplaceCpuAddress_Click(object sender, EventArgs e)
344 		{
345 			_config.PrgAddressPosition = PrgAddressPosition.Replace;
346 			this.UpdateConfig();
347 		}
348 
mnuBelowCpuAddress_Click(object sender, EventArgs e)349 		private void mnuBelowCpuAddress_Click(object sender, EventArgs e)
350 		{
351 			_config.PrgAddressPosition = PrgAddressPosition.Below;
352 			this.UpdateConfig();
353 		}
354 
mnuHidePrgAddresses_Click(object sender, EventArgs e)355 		private void mnuHidePrgAddresses_Click(object sender, EventArgs e)
356 		{
357 			_config.PrgAddressPosition = PrgAddressPosition.Hidden;
358 			this.UpdateConfig();
359 		}
360 
mnuCopySelection_Click(object sender, EventArgs e)361 		private void mnuCopySelection_Click(object sender, EventArgs e)
362 		{
363 			Viewer.CodeViewer.CopySelection(ConfigManager.Config.DebugInfo.CopyAddresses, ConfigManager.Config.DebugInfo.CopyByteCode, ConfigManager.Config.DebugInfo.CopyComments);
364 		}
365 
mnuShowNextStatement_Click(object sender, EventArgs e)366 		private void mnuShowNextStatement_Click(object sender, EventArgs e)
367 		{
368 			this.ScrollToActiveAddress();
369 		}
370 
ScrollToActiveAddress()371 		public void ScrollToActiveAddress()
372 		{
373 			if(Viewer.ActiveAddress.HasValue) {
374 				Viewer.ScrollToLineNumber((int)Viewer.ActiveAddress.Value);
375 			}
376 		}
377 
mnuShowLineNotes_Click(object sender, EventArgs e)378 		private void mnuShowLineNotes_Click(object sender, EventArgs e)
379 		{
380 			Viewer.CodeViewer.ShowLineNumberNotes = this.mnuShowLineNotes.Checked;
381 			this.UpdateConfig();
382 		}
383 
mnuFindOccurrences_Click(object sender, EventArgs e)384 		private void mnuFindOccurrences_Click(object sender, EventArgs e)
385 		{
386 			FindOccurrences();
387 		}
388 
FindOccurrences()389 		private void FindOccurrences()
390 		{
391 			if(_lastClickedSymbol != null) {
392 				Viewer.FindAllOccurrences(_lastClickedSymbol);
393 			} else {
394 				Viewer.FindAllOccurrences(_lastWord, true, true);
395 			}
396 		}
397 
mnuUndoPrgChrEdit_Click(object sender, EventArgs e)398 		private void mnuUndoPrgChrEdit_Click(object sender, EventArgs e)
399 		{
400 			if(InteropEmu.DebugHasUndoHistory()) {
401 				InteropEmu.DebugPerformUndo();
402 				frmDebugger debugger = DebugWindowManager.GetDebugger();
403 				if(debugger != null) {
404 					debugger.UpdateDebugger(false);
405 				}
406 			}
407 		}
408 
mnuSetNextStatement_Click(object sender, EventArgs e)409 		private void mnuSetNextStatement_Click(object sender, EventArgs e)
410 		{
411 			this.OnSetNextStatement?.Invoke(new AddressEventArgs() { Address = (UInt32)Viewer.CodeViewer.CurrentLine });
412 		}
413 
mnuEditSourceFile_Click(object sender, EventArgs e)414 		private void mnuEditSourceFile_Click(object sender, EventArgs e)
415 		{
416 			Viewer.EditSourceFile();
417 		}
418 
mnuEditSubroutine_Click(object sender, EventArgs e)419 		private void mnuEditSubroutine_Click(object sender, EventArgs e)
420 		{
421 			Viewer.EditSubroutine();
422 		}
423 
mnuEditSelectedCode_Click(object sender, EventArgs e)424 		private void mnuEditSelectedCode_Click(object sender, EventArgs e)
425 		{
426 			Viewer.EditSelectedCode();
427 		}
428 
mnuEditInMemoryViewer_Click(object sender, EventArgs e)429 		private void mnuEditInMemoryViewer_Click(object sender, EventArgs e)
430 		{
431 			if(UpdateContextMenu(_lastLocation)) {
432 				DebugWindowManager.OpenMemoryViewer(GetDestination());
433 			}
434 		}
435 
mnuSwitchView_Click(object sender, EventArgs e)436 		private void mnuSwitchView_Click(object sender, EventArgs e)
437 		{
438 			if(Viewer.SymbolProvider != null) {
439 				this.SwitchView();
440 			}
441 		}
442 
SwitchView()443 		public void SwitchView()
444 		{
445 			this.OnSwitchView?.Invoke(Viewer);
446 		}
447 
ProcessMouseUp(Point location, MouseButtons button)448 		public void ProcessMouseUp(Point location, MouseButtons button)
449 		{
450 			if(UpdateContextMenu(location)) {
451 				if(button == MouseButtons.Left) {
452 					if(ModifierKeys.HasFlag(Keys.Control) && ModifierKeys.HasFlag(Keys.Alt)) {
453 						ShowInSplitView();
454 					} else if(ModifierKeys.HasFlag(Keys.Control)) {
455 						AddWatch();
456 					} else if(ModifierKeys.HasFlag(Keys.Alt)) {
457 						FindOccurrences();
458 					}
459 				}
460 			}
461 		}
462 
ProcessMouseDoubleClick(Point location)463 		public void ProcessMouseDoubleClick(Point location)
464 		{
465 			if(UpdateContextMenu(location) && mnuGoToLocation.Enabled) {
466 				GoToLocation();
467 			}
468 		}
469 
contextMenuCode_Closed(object sender, ToolStripDropDownClosedEventArgs e)470 		private void contextMenuCode_Closed(object sender, ToolStripDropDownClosedEventArgs e)
471 		{
472 			mnuEditSelectedCode.Enabled = true;
473 			mnuEditSubroutine.Enabled = true;
474 			mnuEditSourceFile.Enabled = true;
475 		}
476 
UpdateContextMenuItemVisibility(ToolStripItemCollection items)477 		public void UpdateContextMenuItemVisibility(ToolStripItemCollection items)
478 		{
479 			items[nameof(mnuUndoPrgChrEdit)].Enabled = InteropEmu.DebugHasUndoHistory();
480 			items[nameof(mnuShowNextStatement)].Enabled = Viewer.ActiveAddress.HasValue;
481 			items[nameof(mnuSetNextStatement)].Enabled = Viewer.ActiveAddress.HasValue;
482 			items[nameof(mnuEditSelectedCode)].Enabled = items[nameof(mnuEditSubroutine)].Enabled = InteropEmu.DebugIsExecutionStopped() && Viewer.CodeViewer.CurrentLine >= 0;
483 
484 			bool hasSymbolProvider = Viewer.SymbolProvider != null;
485 			items[nameof(mnuShowSourceAsComments)].Visible = hasSymbolProvider;
486 			items[nameof(mnuSwitchView)].Visible = hasSymbolProvider;
487 			items[nameof(sepSwitchView)].Visible = hasSymbolProvider;
488 
489 			if(IsSourceView) {
490 				items[nameof(mnuMarkSelectionAs)].Visible = false;
491 
492 				items[nameof(mnuEditSubroutine)].Visible = false;
493 				items[nameof(mnuEditSelectedCode)].Visible = false;
494 				items[nameof(mnuNavigateForward)].Visible = false;
495 				items[nameof(mnuNavigateBackward)].Visible = false;
496 				items[nameof(mnuEditLabel)].Visible = false;
497 				items[nameof(sepNavigation)].Visible = false;
498 				items[nameof(mnuShowSourceAsComments)].Visible = false;
499 				items[nameof(sepMarkSelectionAs)].Visible = false;
500 			} else {
501 				items[nameof(mnuEditSourceFile)].Visible = false;
502 			}
503 
504 			AddressTypeInfo addressInfo = Viewer.GetAddressInfo(Viewer.CodeViewer.SelectedLine);
505 			if(addressInfo.Address >= 0) {
506 				int relAddress = InteropEmu.DebugGetRelativeAddress((uint)addressInfo.Address, addressInfo.Type);
507 				items[nameof(mnuPerfTracker)].Text = "Performance Tracker ($" + relAddress.ToString("X4") + ")";
508 				items[nameof(mnuPerfTracker)].Enabled = true;
509 			} else {
510 				items[nameof(mnuPerfTracker)].Text = "Performance Tracker";
511 				items[nameof(mnuPerfTracker)].Enabled = false;
512 			}
513 		}
514 
UpdateContextMenu(Point mouseLocation)515 		private bool UpdateContextMenu(Point mouseLocation)
516 		{
517 			_lastLocation = mouseLocation;
518 
519 			UpdateContextMenuItemVisibility(contextMenu.Items);
520 
521 			mnuSwitchView.Text = IsSourceView ? "Switch to Disassembly View" : "Switch to Source View";
522 
523 			string word = Viewer.CodeViewer.GetWordUnderLocation(mouseLocation);
524 			Ld65DbgImporter.SymbolInfo symbol = null;
525 			CodeLabel codeLabel = null;
526 
527 			if(!word.StartsWith("$")) {
528 				Match arrayMatch = CodeTooltipManager.LabelArrayFormat.Match(word);
529 				if(arrayMatch.Success) {
530 					word = arrayMatch.Groups[1].Value;
531 				}
532 
533 				codeLabel = LabelManager.GetLabel(word);
534 
535 				if(Viewer.SymbolProvider != null && IsSourceView) {
536 					int rangeStart, rangeEnd;
537 					if(Viewer.CodeViewer.GetNoteRangeAtLocation(mouseLocation.Y, out rangeStart, out rangeEnd)) {
538 						symbol = Viewer.SymbolProvider.GetSymbol(word, rangeStart, rangeEnd);
539 					}
540 				}
541 			}
542 
543 			if(word.StartsWith("$") || codeLabel != null || symbol != null) {
544 				//Cursor is on a numeric value or label
545 				_lastWord = word;
546 
547 				if(word.StartsWith("$")) {
548 					//CPU Address
549 					_lastClickedAddress = Int32.Parse(word.Substring(1), NumberStyles.AllowHexSpecifier);
550 					_lastClickedSymbol = null;
551 					_lastClickedLabel = null;
552 					_newWatchValue = "[$" + _lastClickedAddress.ToString("X") + "]";
553 				} else if(symbol != null) {
554 					//Symbol
555 					_lastClickedAddress = -1;
556 					_lastClickedLabel = null;
557 					_lastClickedSymbol = symbol;
558 					_newWatchValue = "[" + word + "]";
559 				} else if(codeLabel != null) {
560 					//Label
561 					_lastClickedLabel = codeLabel;
562 					_lastClickedAddress = -1;
563 					_lastClickedSymbol = null;
564 					_newWatchValue = "[" + word + "]";
565 				}
566 
567 				mnuGoToLocation.Enabled = true;
568 				mnuGoToLocation.Text = $"Go to Location ({word})";
569 
570 				mnuShowInSplitView.Enabled = true;
571 				mnuShowInSplitView.Text = $"Show in Split View ({word})";
572 
573 				mnuAddToWatch.Enabled = true;
574 				mnuAddToWatch.Text = $"Add to Watch ({word})";
575 
576 				mnuFindOccurrences.Enabled = true;
577 				mnuFindOccurrences.Text = $"Find Occurrences ({word})";
578 
579 				mnuEditLabel.Enabled = true;
580 				mnuEditLabel.Text = $"Edit Label ({word})";
581 
582 				mnuEditInMemoryViewer.Enabled = true;
583 				mnuEditInMemoryViewer.Text = $"Edit in Memory Viewer ({word})";
584 
585 				return true;
586 			} else {
587 				mnuGoToLocation.Enabled = false;
588 				mnuGoToLocation.Text = "Go to Location";
589 				mnuShowInSplitView.Enabled = false;
590 				mnuShowInSplitView.Text = "Show in Split View";
591 				mnuAddToWatch.Enabled = false;
592 				mnuAddToWatch.Text = "Add to Watch";
593 				mnuFindOccurrences.Enabled = false;
594 				mnuFindOccurrences.Text = "Find Occurrences";
595 				mnuEditLabel.Enabled = false;
596 				mnuEditLabel.Text = "Edit Label";
597 				mnuEditInMemoryViewer.Enabled = false;
598 				mnuEditInMemoryViewer.Text = $"Edit in Memory Viewer";
599 
600 				_lastClickedLabel = null;
601 				_lastClickedSymbol = null;
602 				if(mouseLocation.X < Viewer.CodeViewer.CodeMargin) {
603 					_lastClickedAddress = Viewer.CodeViewer.GetLineNumberAtPosition(mouseLocation.Y);
604 				} else {
605 					_lastClickedAddress = Viewer.CodeViewer.LastSelectedLine;
606 				}
607 
608 				if(_lastClickedAddress >= 0) {
609 					//Cursor is in the margin, over an address label
610 					string address = $"${_lastClickedAddress.ToString("X4")}";
611 					_newWatchValue = $"[{address}]";
612 					_lastWord = address;
613 
614 					mnuShowInSplitView.Enabled = true;
615 					mnuShowInSplitView.Text = $"Show in Split View ({address})";
616 					mnuAddToWatch.Enabled = true;
617 					mnuAddToWatch.Text = $"Add to Watch ({address})";
618 					mnuFindOccurrences.Enabled = true;
619 					mnuFindOccurrences.Text = $"Find Occurrences ({address})";
620 					mnuEditLabel.Enabled = true;
621 					mnuEditLabel.Text = $"Edit Label ({address})";
622 					mnuEditInMemoryViewer.Enabled = true;
623 					mnuEditInMemoryViewer.Text = $"Edit in Memory Viewer ({address})";
624 					return true;
625 				}
626 
627 				return false;
628 			}
629 		}
630 
SetPerformanceTracker(PerfTrackerMode mode)631 		private void SetPerformanceTracker(PerfTrackerMode mode)
632 		{
633 			AddressTypeInfo addressInfo = Viewer.GetAddressInfo(Viewer.CodeViewer.SelectedLine);
634 			InteropEmu.DebugSetPerformanceTracker(addressInfo.Address, addressInfo.Type, mode);
635 		}
636 
mnuPerfTrackerFullscreen_Click(object sender, EventArgs e)637 		private void mnuPerfTrackerFullscreen_Click(object sender, EventArgs e)
638 		{
639 			SetPerformanceTracker(PerfTrackerMode.Fullscreen);
640 		}
641 
mnuPerfTrackerCompact_Click(object sender, EventArgs e)642 		private void mnuPerfTrackerCompact_Click(object sender, EventArgs e)
643 		{
644 			SetPerformanceTracker(PerfTrackerMode.Compact);
645 		}
646 
mnuPerfTrackerTextOnly_Click(object sender, EventArgs e)647 		private void mnuPerfTrackerTextOnly_Click(object sender, EventArgs e)
648 		{
649 			SetPerformanceTracker(PerfTrackerMode.TextOnly);
650 		}
651 
mnuPerfTrackerDisabled_Click(object sender, EventArgs e)652 		private void mnuPerfTrackerDisabled_Click(object sender, EventArgs e)
653 		{
654 			SetPerformanceTracker(PerfTrackerMode.Disabled);
655 		}
656 
mnuPerfTracker_DropDownOpening(object sender, EventArgs e)657 		private void mnuPerfTracker_DropDownOpening(object sender, EventArgs e)
658 		{
659 			PerfTrackerMode mode = InteropEmu.DebugGetPerformanceTrackerMode();
660 			mnuPerfTrackerFullscreen.Checked = mode == PerfTrackerMode.Fullscreen;
661 			mnuPerfTrackerCompact.Checked = mode == PerfTrackerMode.Compact;
662 			mnuPerfTrackerTextOnly.Checked = mode == PerfTrackerMode.TextOnly;
663 			mnuPerfTrackerDisabled.Checked = mode == PerfTrackerMode.Disabled;
664 		}
665 	}
666 }
667