diff --git a/src/BizHawk.Client.Common/tools/Watch/NeoWatch.cs b/src/BizHawk.Client.Common/tools/Watch/NeoWatch.cs new file mode 100644 index 0000000000..78b66a8d1f --- /dev/null +++ b/src/BizHawk.Client.Common/tools/Watch/NeoWatch.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using System.Linq.Expressions; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Client.Common +{ + /// an extremely versatile implementation (pointers!), but with presumably lower performance + public sealed class NeoWatch : Watch + { + private static long CollapseAddress(long address, MemoryDomain domain) + => address; + + private Expression _addressAST; + + private uint _previous; + + private uint _value; + + private Watch _wrapped; + + public override string AddressString + => _addressAST.ToString(); + + public override string Diff + => $"{_value - (long) _previous:+#;-#;0}"; + + public override uint Previous + => _previous; + + public override string PreviousStr + => Watch.FormatValue(_previous, Size, Type); + + public override int Value + => _wrapped.Value; + + public override string ValueString + => _wrapped.ValueString; + + public int Width; + + private NeoWatch(Watch wrapped) + : base( + wrapped.Domain, + wrapped.Address, + wrapped.Size, + wrapped.Type, + bigEndian: wrapped.BigEndian, + note: wrapped.Notes) + => _wrapped = wrapped; + + internal NeoWatch( + MemoryDomain domain, + long address, + WatchSize size, + WatchDisplayType type, + bool bigEndian) + : this(Watch.GenerateWatch( + domain, + CollapseAddress(address, domain), + size, + type, + bigEndian: bigEndian)) {} + + public override IEnumerable AvailableTypes() + => Size switch + { + WatchSize.Byte => ByteWatch.ValidTypes, + WatchSize.Word => WordWatch.ValidTypes, + WatchSize.DWord => DWordWatch.ValidTypes, + _ => [ ], + }; + + private void CollapseAddress() + { + //TODO + } + + private uint CollapseAndPeek() + { + CollapseAddress(); + return Size switch + { + WatchSize.Byte => GetByte(), + WatchSize.Word => GetWord(), + _ => GetDWord(), + }; + } + + public override bool Poke(string value) + { + CollapseAddress(); + try + { + var parsed = Watch.ParseValue(value, Size, Type); + switch (Size) + { + case WatchSize.Byte: + PokeByte(unchecked((byte) parsed)); + break; + case WatchSize.Word: + PokeWord(unchecked((ushort) parsed)); + break; + case WatchSize.DWord: + PokeDWord(parsed); + break; + } + return true; + } + catch + { + return false; + } + } + + public override void ResetPrevious() + => _previous = CollapseAndPeek(); + + public override void Update(PreviousType previousType) + { + switch (previousType) + { + case PreviousType.Original: + CollapseAddress(); + // no-op + break; + case PreviousType.LastSearch: + CollapseAddress(); + //TODO no-op? + break; + case PreviousType.LastFrame: + _previous = _value; + _value = CollapseAndPeek(); + if (_value != _previous) ChangeCount++; + break; + case PreviousType.LastChange: + var newValue = CollapseAndPeek(); + if (newValue != _value) + { + _previous = _value; + ChangeCount++; + } + _value = newValue; + break; + } + } + } +} diff --git a/src/BizHawk.Client.Common/tools/Watch/Watch.cs b/src/BizHawk.Client.Common/tools/Watch/Watch.cs index c196beada7..fbe50899ea 100644 --- a/src/BizHawk.Client.Common/tools/Watch/Watch.cs +++ b/src/BizHawk.Client.Common/tools/Watch/Watch.cs @@ -558,7 +558,7 @@ namespace BizHawk.Client.Common /// /// Gets the address in the formatted as string /// - public string AddressString => Address.ToString(AddressFormatStr); + public virtual string AddressString => Address.ToString(AddressFormatStr); /// /// Gets or sets a value indicating the endianess of current diff --git a/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs b/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs index 88e6c05b9a..da7cc06fde 100644 --- a/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs +++ b/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs @@ -25,6 +25,8 @@ namespace BizHawk.Client.EmuHawk private Mode _mode = Mode.New; private bool _loading = true; + private string _sysBusDomainName = null!; + private bool _changedSize; private bool _changedDisplayType; @@ -34,6 +36,8 @@ namespace BizHawk.Client.EmuHawk private readonly HexTextBox AddressBox; + private readonly TextBox AddressWithPointersBox; + private readonly CheckBox BigEndianCheckBox; private readonly ComboBox DisplayTypeDropDown; @@ -54,6 +58,8 @@ namespace BizHawk.Client.EmuHawk private readonly ComboBox SizeDropDown; + private readonly CheckBoxEx UsePointerSyntaxCheckbox; + public WatchEditor() { _changedDisplayType = false; @@ -94,8 +100,40 @@ namespace BizHawk.Client.EmuHawk { Controls = { new LabelEx { Text = "0x" }, AddressBox }, }; + AddressWithPointersBox = new() { Size = new(100, 20), Visible = false }; + SingleColumnFLP flpAddrOptions = new() + { + Controls = { flpAddr, AddressWithPointersBox }, + }; tlpMain.Controls.Add(label1, row: row, column: 0); - tlpMain.Controls.Add(flpAddr, row: row, column: 1); + tlpMain.Controls.Add(flpAddrOptions, row: row, column: 1); + row++; + + UsePointerSyntaxCheckbox = new() { Enabled = MemoryDomains.HasSystemBus, Text = "Use pointer syntax" }; + UsePointerSyntaxCheckbox.CheckedChanged += (checkedChangedSender, _) => + { + var isChecked = ((CheckBox) checkedChangedSender).Checked; + flpAddr.Visible = !(AddressWithPointersBox.Visible = isChecked); + if (isChecked) + { + if ((string) DomainDropDown.SelectedItem == _sysBusDomainName!) + { + AddressWithPointersBox.Text = $"0x{AddressBox.Text}"; + } + else + { + DomainDropDown.SelectedItem = _sysBusDomainName; + AddressWithPointersBox.Text = string.Empty; + } + AddressBox.Text = string.Empty; + } + else + { + //TODO eval and copy back + AddressWithPointersBox.Text = string.Empty; + } + }; + tlpMain.Controls.Add(UsePointerSyntaxCheckbox, row: row, column: 1); row++; LocLabelEx label3 = new() { Anchor = AnchorStyles.Right, Text = "Size:" }; @@ -187,6 +225,7 @@ namespace BizHawk.Client.EmuHawk } _loading = false; + _sysBusDomainName = MemoryDomains.SystemBus.ToString(); SetAddressBoxProperties(); switch (_mode) @@ -206,7 +245,9 @@ namespace BizHawk.Client.EmuHawk NotesBox.Enabled = false; NotesBox.Text = ""; - AddressBox.Enabled = false; + AddressBox.Enabled = AddressWithPointersBox.Enabled + = UsePointerSyntaxCheckbox.Enabled + = false; AddressBox.Text = Watches.Select(a => a.AddressString).Aggregate((addrStr, nextStr) => $"{addrStr},{nextStr}"); BigEndianCheckBox.ThreeState = true; @@ -220,7 +261,15 @@ namespace BizHawk.Client.EmuHawk { NotesBox.Text = Watches[0].Notes; NotesBox.Select(); - AddressBox.SetFromLong(Watches[0].Address); + if (Watches[0] is NeoWatch neo) + { + UsePointerSyntaxCheckbox.Checked = true; + AddressWithPointersBox.Text = neo.AddressString; + } + else + { + AddressBox.SetFromLong(Watches[0].Address); + } } SetBigEndianCheckBox(); @@ -321,17 +370,25 @@ namespace BizHawk.Client.EmuHawk default: case Mode.New: var domain = MemoryDomains.FirstOrDefault(d => d.Name == DomainDropDown.SelectedItem.ToString()); - var address = AddressBox.ToLong() ?? 0; var notes = NotesBox.Text; var type = Watch.StringToDisplayType(DisplayTypeDropDown.SelectedItem.ToString()); var bigEndian = BigEndianCheckBox.Checked; - Watches.Add(Watch.GenerateWatch( - domain, - address, - (WatchSize) SelectedWidth, - type, - bigEndian: bigEndian, - note: notes)); + var addrWithPointers = AddressWithPointersBox.Text; + if (addrWithPointers.Length is not 0) + { + //TODO + } + else + { + var address = AddressBox.ToLong() ?? 0; + Watches.Add(Watch.GenerateWatch( + domain, + address, + (WatchSize) SelectedWidth, + type, + bigEndian: bigEndian, + note: notes)); + } break; case Mode.Edit: DoEdit();