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();