From 329021c63c78d74c15c073f3edb23dcfb263e062 Mon Sep 17 00:00:00 2001 From: Sour Date: Fri, 28 May 2021 21:39:18 -0400 Subject: [PATCH] Label list, call stack --- Core/Debugger/Debugger.cpp | 4 +- Core/Debugger/Disassembler.cpp | 19 +++- Core/Debugger/LabelManager.cpp | 43 +-------- Core/Debugger/MemoryDumper.cpp | 1 + NewUI/Debugger/DebugUtilities.cs | 41 ++++++++ NewUI/Debugger/DebuggerDockFactory.cs | 4 +- NewUI/Debugger/Disassembly/CodeLineData.cs | 9 +- NewUI/Debugger/Labels/CodeLabel.cs | 30 ++++-- NewUI/Debugger/Labels/LabelManager.cs | 22 +---- .../Debugger/ViewModels/CallStackViewModel.cs | 86 +++++++++++++++++ .../ViewModels/DebuggerWindowViewModel.cs | 20 ++-- .../Debugger/ViewModels/LabelEditViewModel.cs | 66 +++++++++++++ .../Debugger/ViewModels/LabelListViewModel.cs | 34 +++++++ NewUI/Debugger/Views/BreakpointListView.axaml | 34 ------- NewUI/Debugger/Views/CallStackView.axaml | 40 ++++++++ NewUI/Debugger/Views/CallStackView.axaml.cs | 27 ++++++ NewUI/Debugger/Views/LabelListView.axaml | 66 +++++++++++++ NewUI/Debugger/Views/LabelListView.axaml.cs | 94 +++++++++++++++++++ NewUI/Debugger/Views/SnesCpuView.axaml | 11 ++- .../Debugger/Windows/DebuggerWindow.axaml.cs | 2 + NewUI/Debugger/Windows/LabelEditWindow.axaml | 80 ++++++++++++++++ .../Debugger/Windows/LabelEditWindow.axaml.cs | 33 +++++++ NewUI/Interop/DebugState.cs | 2 +- NewUI/NewUI.csproj | 9 ++ NewUI/Styles/MesenStyles.xaml | 87 +++++++++++++++++ NewUI/Utilities/WindowExtensions.cs | 24 +++-- 26 files changed, 753 insertions(+), 135 deletions(-) create mode 100644 NewUI/Debugger/DebugUtilities.cs create mode 100644 NewUI/Debugger/ViewModels/CallStackViewModel.cs create mode 100644 NewUI/Debugger/ViewModels/LabelEditViewModel.cs create mode 100644 NewUI/Debugger/ViewModels/LabelListViewModel.cs create mode 100644 NewUI/Debugger/Views/CallStackView.axaml create mode 100644 NewUI/Debugger/Views/CallStackView.axaml.cs create mode 100644 NewUI/Debugger/Views/LabelListView.axaml create mode 100644 NewUI/Debugger/Views/LabelListView.axaml.cs create mode 100644 NewUI/Debugger/Windows/LabelEditWindow.axaml create mode 100644 NewUI/Debugger/Windows/LabelEditWindow.axaml.cs diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index 23555285..f493e8f4 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -582,7 +582,7 @@ shared_ptr Debugger::GetScriptManager() shared_ptr Debugger::GetCallstackManager(CpuType cpuType) { if(_debuggers[(int)cpuType].Debugger) { - _debuggers[(int)cpuType].Debugger->GetCallstackManager(); + return _debuggers[(int)cpuType].Debugger->GetCallstackManager(); } throw std::runtime_error("GetCallstackManager() - Unsupported CPU type"); } @@ -600,7 +600,7 @@ Emulator* Debugger::GetEmulator() shared_ptr Debugger::GetAssembler(CpuType cpuType) { if(_debuggers[(int)cpuType].Debugger) { - _debuggers[(int)cpuType].Debugger->GetAssembler(); + return _debuggers[(int)cpuType].Debugger->GetAssembler(); } throw std::runtime_error("GetAssembler() - Unsupported CPU type"); } diff --git a/Core/Debugger/Disassembler.cpp b/Core/Debugger/Disassembler.cpp index 584b74e6..5147a3d8 100644 --- a/Core/Debugger/Disassembler.cpp +++ b/Core/Debugger/Disassembler.cpp @@ -293,13 +293,22 @@ CodeLineData Disassembler::GetLineData(DisassemblyResult& row, CpuType type, Sne switch(row.Address.Type) { default: break; case SnesMemoryType::GbPrgRom: - case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom; break; + case SnesMemoryType::PrgRom: + case SnesMemoryType::NesPrgRom: + data.Flags |= (uint8_t)LineFlags::PrgRom; + break; case SnesMemoryType::GbWorkRam: - case SnesMemoryType::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam; break; + case SnesMemoryType::WorkRam: + case SnesMemoryType::NesWorkRam: + data.Flags |= (uint8_t)LineFlags::WorkRam; + break; case SnesMemoryType::GbCartRam: - case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam; break; + case SnesMemoryType::SaveRam: + case SnesMemoryType::NesSaveRam: + data.Flags |= (uint8_t)LineFlags::SaveRam; + break; } bool isBlockStartEnd = (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) != 0; @@ -511,8 +520,8 @@ uint32_t Disassembler::GetDisassemblyOutput(CpuType type, uint32_t address, Code SnesMemoryType memType = DebugUtilities::GetCpuMemoryType(type); uint32_t maxBank = (_memoryDumper->GetMemorySize(memType) - 1) >> 16; - uint32_t row; - for(row = 0; row < rowCount; row++){ + int32_t row; + for(row = 0; row < (int32_t)rowCount; row++){ if(row + i >= rows.size()) { if(bank < maxBank) { bank++; diff --git a/Core/Debugger/LabelManager.cpp b/Core/Debugger/LabelManager.cpp index 3f10cbb7..c1f20fa2 100644 --- a/Core/Debugger/LabelManager.cpp +++ b/Core/Debugger/LabelManager.cpp @@ -44,51 +44,12 @@ void LabelManager::SetLabel(uint32_t address, SnesMemoryType memType, string lab int64_t LabelManager::GetLabelKey(uint32_t absoluteAddr, SnesMemoryType memType) { - switch(memType) { - case SnesMemoryType::PrgRom: return absoluteAddr | ((uint64_t)1 << 32); - case SnesMemoryType::WorkRam: return absoluteAddr | ((uint64_t)2 << 32); - case SnesMemoryType::SaveRam: return absoluteAddr | ((uint64_t)3 << 32); - case SnesMemoryType::Register: return absoluteAddr | ((uint64_t)4 << 32); - case SnesMemoryType::SpcRam: return absoluteAddr | ((uint64_t)5 << 32); - case SnesMemoryType::SpcRom: return absoluteAddr | ((uint64_t)6 << 32); - case SnesMemoryType::Sa1InternalRam: return absoluteAddr | ((uint64_t)7 << 32); - case SnesMemoryType::GsuWorkRam: return absoluteAddr | ((uint64_t)8 << 32); - case SnesMemoryType::BsxPsRam: return absoluteAddr | ((uint64_t)9 << 32); - case SnesMemoryType::BsxMemoryPack: return absoluteAddr | ((uint64_t)10 << 32); - case SnesMemoryType::DspProgramRom: return absoluteAddr | ((uint64_t)11 << 32); - case SnesMemoryType::GbPrgRom: return absoluteAddr | ((uint64_t)12 << 32); - case SnesMemoryType::GbWorkRam: return absoluteAddr | ((uint64_t)13 << 32); - case SnesMemoryType::GbCartRam: return absoluteAddr | ((uint64_t)14 << 32); - case SnesMemoryType::GbHighRam: return absoluteAddr | ((uint64_t)15 << 32); - case SnesMemoryType::GbBootRom: return absoluteAddr | ((uint64_t)16 << 32); - case SnesMemoryType::GameboyMemory: return absoluteAddr | ((uint64_t)17 << 32); - default: return -1; - } + return absoluteAddr | ((uint64_t)memType << 32); } SnesMemoryType LabelManager::GetKeyMemoryType(uint64_t key) { - switch(key & ~(uint64_t)0xFFFFFFFF) { - case ((uint64_t)1 << 32): return SnesMemoryType::PrgRom; break; - case ((uint64_t)2 << 32): return SnesMemoryType::WorkRam; break; - case ((uint64_t)3 << 32): return SnesMemoryType::SaveRam; break; - case ((uint64_t)4 << 32): return SnesMemoryType::Register; break; - case ((uint64_t)5 << 32): return SnesMemoryType::SpcRam; break; - case ((uint64_t)6 << 32): return SnesMemoryType::SpcRom; break; - case ((uint64_t)7 << 32): return SnesMemoryType::Sa1InternalRam; break; - case ((uint64_t)8 << 32): return SnesMemoryType::GsuWorkRam; break; - case ((uint64_t)9 << 32): return SnesMemoryType::BsxPsRam; break; - case ((uint64_t)10 << 32): return SnesMemoryType::BsxMemoryPack; break; - case ((uint64_t)11 << 32): return SnesMemoryType::DspProgramRom; break; - case ((uint64_t)12 << 32): return SnesMemoryType::GbPrgRom; break; - case ((uint64_t)13 << 32): return SnesMemoryType::GbWorkRam; break; - case ((uint64_t)14 << 32): return SnesMemoryType::GbCartRam; break; - case ((uint64_t)15 << 32): return SnesMemoryType::GbHighRam; break; - case ((uint64_t)16 << 32): return SnesMemoryType::GbBootRom; break; - case ((uint64_t)17 << 32): return SnesMemoryType::GameboyMemory; break; - } - - throw std::runtime_error("Invalid label key"); + return (SnesMemoryType)(key >> 32); } string LabelManager::GetLabel(AddressInfo address) diff --git a/Core/Debugger/MemoryDumper.cpp b/Core/Debugger/MemoryDumper.cpp index 6fd66d79..9d338f0e 100644 --- a/Core/Debugger/MemoryDumper.cpp +++ b/Core/Debugger/MemoryDumper.cpp @@ -73,6 +73,7 @@ uint32_t MemoryDumper::GetMemorySize(SnesMemoryType type) case SnesMemoryType::Cx4Memory: return 0x1000000; case SnesMemoryType::GameboyMemory: return 0x10000; case SnesMemoryType::NesMemory: return 0x10000; + case SnesMemoryType::Register: return 0x10000; default: return _emu->GetMemory(type).Size; } } diff --git a/NewUI/Debugger/DebugUtilities.cs b/NewUI/Debugger/DebugUtilities.cs new file mode 100644 index 00000000..d5849b68 --- /dev/null +++ b/NewUI/Debugger/DebugUtilities.cs @@ -0,0 +1,41 @@ +using Mesen.Interop; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.Debugger +{ + public static class DebugUtilities + { + public static uint GetProgramCounter(CpuType cpuType) + { + switch(cpuType) { + case CpuType.Cpu: + case CpuType.Sa1: { + CpuState state = DebugApi.GetState(cpuType); + return (uint)(state.K << 16) | state.PC; + } + + case CpuType.Spc: { + SpcState state = DebugApi.GetState(cpuType); + return (uint)state.PC; + } + + case CpuType.Gameboy: { + GbCpuState state = DebugApi.GetState(cpuType); + return (uint)state.PC; + } + + case CpuType.Nes: { + NesCpuState state = DebugApi.GetState(cpuType); + return (uint)state.PC; + } + + default: throw new Exception("Invalid cpu type"); + } + + } + } +} diff --git a/NewUI/Debugger/DebuggerDockFactory.cs b/NewUI/Debugger/DebuggerDockFactory.cs index b8803ef3..3964fe03 100644 --- a/NewUI/Debugger/DebuggerDockFactory.cs +++ b/NewUI/Debugger/DebuggerDockFactory.cs @@ -64,7 +64,7 @@ namespace Mesen.Debugger new SplitterDockable(), new ToolDock { Proportion = 0.33, - VisibleDockables = CreateList(new DummyTool() { Id = "Labels", Title = "Labels" }) + VisibleDockables = CreateList(_context.LabelList) } ) } @@ -87,7 +87,7 @@ namespace Mesen.Debugger new SplitterDockable(), new ToolDock { Proportion = 0.33, - VisibleDockables = CreateList(new DummyTool() { Id = "CallStack", Title = "Call Stack" }) + VisibleDockables = CreateList(_context.CallStack) } ) } diff --git a/NewUI/Debugger/Disassembly/CodeLineData.cs b/NewUI/Debugger/Disassembly/CodeLineData.cs index 7a8e8415..bd16867c 100644 --- a/NewUI/Debugger/Disassembly/CodeLineData.cs +++ b/NewUI/Debugger/Disassembly/CodeLineData.cs @@ -1,4 +1,5 @@ -using Mesen.Interop; +using Mesen.Debugger.Labels; +using Mesen.Interop; using System; using System.Collections.Generic; using System.Linq; @@ -33,7 +34,7 @@ namespace Mesen.Debugger { if(EffectiveAddress >= 0) { AddressInfo relAddress = new AddressInfo() { Address = EffectiveAddress, Type = _cpuType.ToMemoryType() }; - /*CodeLabel label = LabelManager.GetLabel(relAddress); + CodeLabel? label = LabelManager.GetLabel(relAddress); if(label != null) { if(label.Length > 1) { int gap = DebugApi.GetAbsoluteAddress(relAddress).Address - label.GetAbsoluteAddress().Address; @@ -42,9 +43,9 @@ namespace Mesen.Debugger } } return "[" + label.Label + "]"; - } else {*/ + } else { return "[$" + EffectiveAddress.ToString(format) + "]"; - //} + } } else { return ""; } diff --git a/NewUI/Debugger/Labels/CodeLabel.cs b/NewUI/Debugger/Labels/CodeLabel.cs index d5f31853..223bc7d9 100644 --- a/NewUI/Debugger/Labels/CodeLabel.cs +++ b/NewUI/Debugger/Labels/CodeLabel.cs @@ -1,18 +1,20 @@ using Mesen.Interop; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; using System; using System.Globalization; using System.Text; namespace Mesen.Debugger.Labels { - public class CodeLabel + public class CodeLabel : ReactiveObject { - public UInt32 Address; - public SnesMemoryType MemoryType; - public string Label = ""; - public string Comment = ""; - public CodeLabelFlags Flags; - public UInt32 Length = 1; + [Reactive] public UInt32 Address { get; set; } + [Reactive] public SnesMemoryType MemoryType { get; set; } + [Reactive] public string Label { get; set; } = ""; + [Reactive] public string Comment { get; set; } = ""; + [Reactive] public CodeLabelFlags Flags { get; set; } + [Reactive] public UInt32 Length { get; set; } = 1; public override string ToString() { @@ -49,10 +51,10 @@ namespace Mesen.Debugger.Labels return sb.ToString(); } - private static char[] _separatar = new char[1] { ':' }; + private static char[] _separator = new char[1] { ':' }; public static CodeLabel? FromString(string data) { - string[] rowData = data.Split(_separatar, 4); + string[] rowData = data.Split(_separator, 4); if(rowData.Length < 3) { //Invalid row return null; @@ -151,5 +153,15 @@ namespace Mesen.Debugger.Labels { return (CodeLabel)this.MemberwiseClone(); } + + public void CopyFrom(CodeLabel copy) + { + Address = copy.Address; + MemoryType = copy.MemoryType; + Label = copy.Label; + Comment = copy.Comment; + Flags = copy.Flags; + Length = copy.Length; + } } } diff --git a/NewUI/Debugger/Labels/LabelManager.cs b/NewUI/Debugger/Labels/LabelManager.cs index e554d8ed..eb321f57 100644 --- a/NewUI/Debugger/Labels/LabelManager.cs +++ b/NewUI/Debugger/Labels/LabelManager.cs @@ -77,26 +77,7 @@ namespace Mesen.Debugger.Labels private static UInt64 GetKey(UInt32 address, SnesMemoryType memType) { - switch(memType) { - case SnesMemoryType.PrgRom: return address | ((ulong)1 << 32); - case SnesMemoryType.WorkRam: return address | ((ulong)2 << 32); - case SnesMemoryType.SaveRam: return address | ((ulong)3 << 32); - case SnesMemoryType.Register: return address | ((ulong)4 << 32); - case SnesMemoryType.SpcRam: return address | ((ulong)5 << 32); - case SnesMemoryType.SpcRom: return address | ((ulong)6 << 32); - case SnesMemoryType.Sa1InternalRam: return address | ((ulong)7 << 32); - case SnesMemoryType.GsuWorkRam: return address | ((ulong)8 << 32); - case SnesMemoryType.BsxPsRam: return address | ((ulong)9 << 32); - case SnesMemoryType.BsxMemoryPack: return address | ((ulong)10 << 32); - case SnesMemoryType.DspProgramRom: return address | ((ulong)11 << 32); - case SnesMemoryType.GbPrgRom: return address | ((ulong)12 << 32); - case SnesMemoryType.GbWorkRam: return address | ((ulong)13 << 32); - case SnesMemoryType.GbCartRam: return address | ((ulong)14 << 32); - case SnesMemoryType.GbHighRam: return address | ((ulong)15 << 32); - case SnesMemoryType.GbBootRom: return address | ((ulong)16 << 32); - case SnesMemoryType.GameboyMemory: return address | ((ulong)17 << 32); - } - throw new Exception("Invalid type"); + return address | ((UInt64)memType << 32); } private static void SetLabel(uint address, SnesMemoryType memType, string label, string comment) @@ -234,6 +215,7 @@ namespace Mesen.Debugger.Labels public static void SetDefaultLabels() { + SetSnesDefaultLabels(); //TODO /*CoprocessorType coproc = EmuApi.GetRomInfo().CoprocessorType; if(coproc == CoprocessorType.SGB) { diff --git a/NewUI/Debugger/ViewModels/CallStackViewModel.cs b/NewUI/Debugger/ViewModels/CallStackViewModel.cs new file mode 100644 index 00000000..bae400b2 --- /dev/null +++ b/NewUI/Debugger/ViewModels/CallStackViewModel.cs @@ -0,0 +1,86 @@ +using Dock.Model.ReactiveUI.Controls; +using Mesen.Debugger.Labels; +using Mesen.Interop; +using Mesen.ViewModels; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Mesen.Debugger.ViewModels +{ + public class CallStackViewModel : Tool + { + private CpuType _cpuType; + + [Reactive] public List StackFrames { get; private set; } = new List(); + + //For designer + public CallStackViewModel() : this(CpuType.Cpu) { } + + public CallStackViewModel(CpuType cpuType) + { + _cpuType = cpuType; + Id = "CallStack"; + Title = "Call Stack"; + UpdateCallStack(); + } + + public void UpdateCallStack() + { + StackFrames = GetStackInfo(); + } + + private List GetStackInfo() + { + StackFrameInfo[] stackFrames = DebugApi.GetCallstack(_cpuType); + + int relDestinationAddr = -1; + + List stack = new List(); + for(int i = 0, len = stackFrames.Length; i < len; i++) { + int relSubEntryAddr = i == 0 ? -1 : (int)stackFrames[i - 1].Target; + + stack.Insert(0, new StackInfo() { + SubName = this.GetFunctionName(relSubEntryAddr, i == 0 ? StackFrameFlags.None : stackFrames[i - 1].Flags), + Address = stackFrames[i].Source, + }); + + relDestinationAddr = (int)stackFrames[i].Target; + } + + //Add current location + stack.Insert(0, new StackInfo() { + SubName = this.GetFunctionName(relDestinationAddr, stackFrames.Length == 0 ? StackFrameFlags.None : stackFrames[^1].Flags), + Address = DebugUtilities.GetProgramCounter(_cpuType), + }); + + return stack; + } + + private string GetFunctionName(int relSubEntryAddr, StackFrameFlags flags) + { + if(relSubEntryAddr < 0) { + return "[bottom of stack]"; + } + + string format = "X" + _cpuType.GetAddressSize(); + CodeLabel? label = relSubEntryAddr >= 0 ? LabelManager.GetLabel(new AddressInfo() { Address = relSubEntryAddr, Type = _cpuType.ToMemoryType() }) : null; + if(label != null) { + return label.Label + " ($" + relSubEntryAddr.ToString(format) + ")"; + } else if(flags == StackFrameFlags.Nmi) { + return "[nmi] $" + relSubEntryAddr.ToString(format); + } else if(flags == StackFrameFlags.Irq) { + return "[irq] $" + relSubEntryAddr.ToString(format); + } + + return "$" + relSubEntryAddr.ToString(format); + } + + public class StackInfo + { + public string SubName { get; set; } + public UInt32 Address { get; set; } + } + } +} diff --git a/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs b/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs index 424c3595..4bb3d81b 100644 --- a/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs +++ b/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs @@ -2,6 +2,7 @@ using Dock.Model.Core; using Dock.Model.ReactiveUI.Controls; using Mesen.Debugger.Disassembly; +using Mesen.Debugger.Labels; using Mesen.Interop; using Mesen.ViewModels; using ReactiveUI; @@ -15,6 +16,8 @@ namespace Mesen.Debugger.ViewModels { [Reactive] public DisassemblyViewerViewModel Disassembly { get; private set; } [Reactive] public BreakpointListViewModel BreakpointList { get; private set; } + [Reactive] public LabelListViewModel LabelList { get; private set; } + [Reactive] public CallStackViewModel CallStack { get; private set; } [Reactive] public DebuggerDockFactory DockFactory { get; private set; } [Reactive] public IRootDock DockLayout { get; private set; } @@ -33,13 +36,8 @@ namespace Mesen.Debugger.ViewModels Disassembly = new DisassemblyViewerViewModel(); BreakpointList = new BreakpointListViewModel(); - - var factory = new DebuggerDockFactory(this); - var layout = factory.CreateLayout(); - factory.InitLayout(layout); - - DockFactory = factory; - DockLayout = layout; + + DockFactory = new DebuggerDockFactory(this); RomInfo romInfo = EmuApi.GetRomInfo(); @@ -64,6 +62,14 @@ namespace Mesen.Debugger.ViewModels ConfigApi.SetDebuggerFlag(DebuggerFlags.GbDebuggerEnabled, true); break; } + + LabelManager.SetDefaultLabels(); + LabelList = new LabelListViewModel(CpuType); + + CallStack = new CallStackViewModel(CpuType); + + DockLayout = DockFactory.CreateLayout(); + DockFactory.InitLayout(DockLayout); } internal void UpdateDisassembly() diff --git a/NewUI/Debugger/ViewModels/LabelEditViewModel.cs b/NewUI/Debugger/ViewModels/LabelEditViewModel.cs new file mode 100644 index 00000000..fe9ded5f --- /dev/null +++ b/NewUI/Debugger/ViewModels/LabelEditViewModel.cs @@ -0,0 +1,66 @@ +using Dock.Model.ReactiveUI.Controls; +using Mesen.Debugger.Labels; +using Mesen.Interop; +using Mesen.ViewModels; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reactive.Linq; + +namespace Mesen.Debugger.ViewModels +{ + public class LabelEditViewModel : ViewModelBase + { + [Reactive] public CodeLabel Label { get; set; } + [ObservableAsProperty] public bool OkEnabled { get; } + [ObservableAsProperty] public string MaxAddress { get; } + + //For designer + public LabelEditViewModel() : this(new CodeLabel()) { } + + public LabelEditViewModel(CodeLabel label, CodeLabel? originalLabel = null) + { + Label = label; + + this.WhenAnyValue(x => x.Label.MemoryType, (memoryType) => { + int maxAddress = DebugApi.GetMemorySize(memoryType) - 1; + if(maxAddress <= 0) { + return "(unavailable)"; + } else { + return "(Max: $" + maxAddress.ToString("X4") + ")"; + } + }).ToPropertyEx(this, x => x.MaxAddress); + + this.WhenAnyValue(x => x.Label.Label, x => x.Label.Comment, x => x.Label.Length, x => x.Label.MemoryType, x => x.Label.Address, (label, comment, length, memoryType, address) => { + CodeLabel? sameLabel = LabelManager.GetLabel(label); + int maxAddress = DebugApi.GetMemorySize(memoryType) - 1; + + for(UInt32 i = 0; i < length; i++) { + CodeLabel? sameAddress = LabelManager.GetLabel(address + i, memoryType); + if(sameAddress != null) { + if(originalLabel == null) { + //A label already exists and we're not editing an existing label, so we can't add it + return false; + } else { + if(sameAddress.Label != originalLabel.Label && !sameAddress.Label.StartsWith(originalLabel.Label + "+")) { + //A label already exists, we're trying to edit an existing label, but the existing label + //and the label we're editing aren't the same label. Can't override an existing label with a different one. + return false; + } + } + } + } + + return + length >= 1 && length <= 65536 && + address + (length - 1) <= maxAddress && + (sameLabel == null || sameLabel == originalLabel) + && (label.Length > 0 || comment.Length > 0) + && !comment.Contains('\x1') + && (label.Length == 0 || LabelManager.LabelRegex.IsMatch(label)); + }).ToPropertyEx(this, x => x.OkEnabled); + } + } +} diff --git a/NewUI/Debugger/ViewModels/LabelListViewModel.cs b/NewUI/Debugger/ViewModels/LabelListViewModel.cs new file mode 100644 index 00000000..7275594b --- /dev/null +++ b/NewUI/Debugger/ViewModels/LabelListViewModel.cs @@ -0,0 +1,34 @@ +using Dock.Model.ReactiveUI.Controls; +using Mesen.Debugger.Labels; +using Mesen.Interop; +using Mesen.ViewModels; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Mesen.Debugger.ViewModels +{ + public class LabelListViewModel : Tool + { + private CpuType _cpuType; + + [Reactive] public List Labels { get; private set; } = new List(); + + //For designer + public LabelListViewModel() : this(CpuType.Cpu) { } + + public LabelListViewModel(CpuType cpuType) + { + _cpuType = cpuType; + Id = "Labels"; + Title = "Labels"; + UpdateLabelList(); + } + + public void UpdateLabelList() + { + Labels = LabelManager.GetLabels(_cpuType); + } + } +} diff --git a/NewUI/Debugger/Views/BreakpointListView.axaml b/NewUI/Debugger/Views/BreakpointListView.axaml index 4489e70b..8a4734ef 100644 --- a/NewUI/Debugger/Views/BreakpointListView.axaml +++ b/NewUI/Debugger/Views/BreakpointListView.axaml @@ -13,40 +13,6 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/NewUI/Debugger/Views/CallStackView.axaml.cs b/NewUI/Debugger/Views/CallStackView.axaml.cs new file mode 100644 index 00000000..e3956d1f --- /dev/null +++ b/NewUI/Debugger/Views/CallStackView.axaml.cs @@ -0,0 +1,27 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Mesen.ViewModels; +using Mesen.Debugger; +using Mesen.Debugger.ViewModels; +using Mesen.Debugger.Labels; +using Mesen.Debugger.Windows; +using Mesen.Utilities; +using Avalonia.Input; + +namespace Mesen.Debugger.Views +{ + public class CallStackView : UserControl + { + public CallStackView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/NewUI/Debugger/Views/LabelListView.axaml b/NewUI/Debugger/Views/LabelListView.axaml new file mode 100644 index 00000000..5a85f497 --- /dev/null +++ b/NewUI/Debugger/Views/LabelListView.axaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NewUI/Debugger/Views/LabelListView.axaml.cs b/NewUI/Debugger/Views/LabelListView.axaml.cs new file mode 100644 index 00000000..7a597ae8 --- /dev/null +++ b/NewUI/Debugger/Views/LabelListView.axaml.cs @@ -0,0 +1,94 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Mesen.ViewModels; +using Mesen.Debugger; +using Mesen.Debugger.ViewModels; +using Mesen.Debugger.Labels; +using Mesen.Debugger.Windows; +using Mesen.Utilities; +using Avalonia.Input; + +namespace Mesen.Debugger.Views +{ + public class LabelListView : UserControl + { + public LabelListView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void OnCellPointerPressed(object sender, DataGridCellPointerPressedEventArgs e) + { + DataGrid grid = this.FindControl("DataGrid"); + grid.SelectedIndex = e.Row.GetIndex(); + } + + private void OnGridClick(object sender, RoutedEventArgs e) + { + + } + + private async void mnuAddLabel_Click(object sender, RoutedEventArgs e) + { + CodeLabel newLabel = new CodeLabel(); + LabelEditWindow wnd = new LabelEditWindow() { + DataContext = new LabelEditViewModel(newLabel) + }; + + bool result = await wnd.ShowCenteredDialog(this); + if(result) { + LabelManager.SetLabel(newLabel, true); + ((LabelListViewModel)DataContext!).UpdateLabelList(); + } + } + + private void mnuEditLabel_Click(object sender, RoutedEventArgs e) + { + DataGrid grid = this.FindControl("DataGrid"); + CodeLabel ? label = grid.SelectedItem as CodeLabel; + if(label != null && grid != null) { + EditLabel(label); + } + } + + private void mnuDeleteLabel_Click(object sender, RoutedEventArgs e) + { + DataGrid grid = this.FindControl("DataGrid"); + CodeLabel? label = grid.SelectedItem as CodeLabel; + if(label != null && grid != null) { + LabelManager.DeleteLabel(label, true); + ((LabelListViewModel)DataContext!).UpdateLabelList(); + } + } + + private void OnGridDoubleClick(object sender, RoutedEventArgs e) + { + DataGrid grid = (DataGrid)sender; + CodeLabel? label = grid.SelectedItem as CodeLabel; + if(label != null && grid != null) { + EditLabel(label); + } + } + + private async void EditLabel(CodeLabel label) + { + CodeLabel copy = label.Clone(); + LabelEditWindow wnd = new LabelEditWindow() { + DataContext = new LabelEditViewModel(copy, label) + }; + + bool result = await wnd.ShowCenteredDialog(this); + if(result) { + label.CopyFrom(copy); + LabelManager.SetLabel(label, true); + } + } + } +} diff --git a/NewUI/Debugger/Views/SnesCpuView.axaml b/NewUI/Debugger/Views/SnesCpuView.axaml index 07f83aac..c3219b48 100644 --- a/NewUI/Debugger/Views/SnesCpuView.axaml +++ b/NewUI/Debugger/Views/SnesCpuView.axaml @@ -27,6 +27,7 @@ @@ -39,6 +40,8 @@ Y: + K: + PC: @@ -46,20 +49,20 @@ D: - + DB: - + S: - + P: - + + + + + + + + + + + +