diff --git a/src/BizHawk.Client.Common/tools/Cheat.cs b/src/BizHawk.Client.Common/tools/Cheat.cs
index bae019699e3..227c0b56264 100644
--- a/src/BizHawk.Client.Common/tools/Cheat.cs
+++ b/src/BizHawk.Client.Common/tools/Cheat.cs
@@ -91,35 +91,20 @@ public MemoryDomain Domain
public string AddressStr => _watch.AddressString;
- public string ValueStr =>
- _watch.Size switch
- {
- WatchSize.Byte => ((ByteWatch) _watch).FormatValue((byte)_val),
- WatchSize.Word => ((WordWatch) _watch).FormatValue((ushort)_val),
- WatchSize.DWord => ((DWordWatch) _watch).FormatValue((uint)_val),
- WatchSize.Separator => "",
- _ => string.Empty,
- };
+ public string ValueStr
+ => _watch.Size is WatchSize.Byte or WatchSize.Word or WatchSize.DWord
+ ? _watch.IsValid
+ ? Watch.FormatValue(unchecked((uint) _val), _watch.Size, _watch.Type)
+ : "-"
+ : string.Empty;
public string CompareStr
- {
- get
- {
- if (_compare.HasValue)
- {
- return _watch.Size switch
- {
- WatchSize.Byte => ((ByteWatch) _watch).FormatValue((byte)_compare.Value),
- WatchSize.Word => ((WordWatch) _watch).FormatValue((ushort)_compare.Value),
- WatchSize.DWord => ((DWordWatch) _watch).FormatValue((uint)_compare.Value),
- WatchSize.Separator => "",
- _ => string.Empty,
- };
- }
-
- return "";
- }
- }
+ => _compare.Value is int compareValue
+ && _watch.Size is WatchSize.Byte or WatchSize.Word or WatchSize.DWord
+ ? _watch.IsValid
+ ? Watch.FormatValue(unchecked((uint) compareValue), _watch.Size, _watch.Type)
+ : "-"
+ : string.Empty;
public CompareType ComparisonType { get; }
@@ -167,17 +152,24 @@ public void Pulse()
{
if (ShouldPoke())
{
- switch (_watch.Size)
+ try
+ {
+ switch (_watch.Size)
+ {
+ case WatchSize.Byte:
+ _watch.PokeByte(unchecked((byte) _val));
+ break;
+ case WatchSize.Word:
+ _watch.PokeWord(unchecked((ushort) _val));
+ break;
+ case WatchSize.DWord:
+ _watch.PokeDWord(unchecked((uint) _val));
+ break;
+ }
+ }
+ catch
{
- case WatchSize.Byte:
- _watch.Poke(((ByteWatch)_watch).FormatValue((byte)_val));
- break;
- case WatchSize.Word:
- _watch.Poke(((WordWatch)_watch).FormatValue((ushort)_val));
- break;
- case WatchSize.DWord:
- _watch.Poke(((DWordWatch)_watch).FormatValue((uint)_val));
- break;
+ // ignore (matches `*Watch.Poke` implementations)
}
}
diff --git a/src/BizHawk.Client.Common/tools/Watch/ByteWatch.cs b/src/BizHawk.Client.Common/tools/Watch/ByteWatch.cs
index 5aacf977aeb..224e1a4e990 100644
--- a/src/BizHawk.Client.Common/tools/Watch/ByteWatch.cs
+++ b/src/BizHawk.Client.Common/tools/Watch/ByteWatch.cs
@@ -1,5 +1,5 @@
using System.Collections.Generic;
-using System.Globalization;
+
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
@@ -42,43 +42,21 @@ internal ByteWatch(MemoryDomain domain, long address, WatchDisplayType type, boo
WatchDisplayType.Binary,
];
- ///
- /// Get a list a that can be used for this
- ///
- /// An enumeration that contains all valid
public override IEnumerable AvailableTypes()
{
return ValidTypes;
}
- ///
- /// Reset the previous value; set it to the current one
- ///
public override void ResetPrevious()
{
_previous = GetByte();
}
- ///
- /// Try to sets the value into the
- /// at the current address
- ///
- /// Value to set
- /// True if value successfully sets; otherwise, false
public override bool Poke(string value)
{
try
{
- byte val = Type switch
- {
- WatchDisplayType.Unsigned => byte.Parse(value),
- WatchDisplayType.Signed => (byte)sbyte.Parse(value),
- WatchDisplayType.Hex => byte.Parse(value, NumberStyles.HexNumber),
- WatchDisplayType.Binary => Convert.ToByte(value, 2),
- _ => 0,
- };
-
- PokeByte(val);
+ PokeByte(unchecked((byte) Watch.ParseValue(value, Size, Type)));
return true;
}
catch
@@ -87,9 +65,6 @@ public override bool Poke(string value)
}
}
- ///
- /// Update the Watch (read it from
- ///
public override void Update(PreviousType previousType)
{
switch (previousType)
@@ -120,52 +95,16 @@ public override void Update(PreviousType previousType)
// TODO: Implements IFormattable
public string FormatValue(byte val)
- {
- return Type switch
- {
- _ when !IsValid => "-",
- WatchDisplayType.Unsigned => val.ToString(),
- WatchDisplayType.Signed => ((sbyte) val).ToString(),
- WatchDisplayType.Hex => $"{val:X2}",
- WatchDisplayType.Binary => Convert.ToString(val, 2).PadLeft(8, '0').Insert(4, " "),
- _ => val.ToString(),
- };
- }
+ => IsValid ? Watch.FormatValue(val, Size, Type) : "-";
- ///
- /// Get a string representation of difference
- /// between current value and the previous one
- ///
public override string Diff => $"{_value - _previous:+#;-#;0}";
- ///
- /// Returns true if the Watch is valid, false otherwise
- ///
- public override bool IsValid => Domain.Size == 0 || Address < Domain.Size;
-
- ///
- /// Get the maximum possible value
- ///
- public override uint MaxValue => byte.MaxValue;
-
- ///
- /// Get the current value
- ///
public override int Value => GetByte();
- ///
- /// Get a string representation of the current value
- ///
public override string ValueString => FormatValue(GetByte());
- ///
- /// Get the previous value
- ///
public override uint Previous => _previous;
- ///
- /// Get a string representation of the previous value
- ///
public override string PreviousStr => FormatValue(_previous);
}
}
diff --git a/src/BizHawk.Client.Common/tools/Watch/DWordWatch.cs b/src/BizHawk.Client.Common/tools/Watch/DWordWatch.cs
index f64bd59639e..986b6022ab7 100644
--- a/src/BizHawk.Client.Common/tools/Watch/DWordWatch.cs
+++ b/src/BizHawk.Client.Common/tools/Watch/DWordWatch.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
-using System.Globalization;
-using BizHawk.Common.NumberExtensions;
+
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
@@ -46,44 +45,19 @@ internal DWordWatch(MemoryDomain domain, long address, WatchDisplayType type, bo
WatchDisplayType.Float,
];
- ///
- /// Get a list of that can be used for a
- ///
- /// An enumeration that contains all valid
public override IEnumerable AvailableTypes()
{
return ValidTypes;
}
- ///
- /// Reset the previous value; set it to the current one
- ///
public override void ResetPrevious()
=> _previous = GetDWord();
- ///
- /// Try to sets the value into the
- /// at the current address
- ///
- /// Value to set
- /// True if value successfully sets; otherwise, false
public override bool Poke(string value)
{
try
{
- uint val = Type switch
- {
- WatchDisplayType.Unsigned => uint.Parse(value),
- WatchDisplayType.Signed => (uint)int.Parse(value),
- WatchDisplayType.Hex => uint.Parse(value, NumberStyles.HexNumber),
- WatchDisplayType.FixedPoint_20_12 => (uint)(double.Parse(value, NumberFormatInfo.InvariantInfo) * 4096.0),
- WatchDisplayType.FixedPoint_16_16 => (uint)(double.Parse(value, NumberFormatInfo.InvariantInfo) * 65536.0),
- WatchDisplayType.Float => NumberExtensions.ReinterpretAsUInt32(float.Parse(value, NumberFormatInfo.InvariantInfo)),
- WatchDisplayType.Binary => Convert.ToUInt32(value, 2),
- _ => 0,
- };
-
- PokeDWord(val);
+ PokeDWord(Watch.ParseValue(value, Size, Type));
return true;
}
catch
@@ -92,9 +66,6 @@ public override bool Poke(string value)
}
}
- ///
- /// Update the Watch (read it from
- ///
public override void Update(PreviousType previousType)
{
switch (previousType)
@@ -125,71 +96,16 @@ public override void Update(PreviousType previousType)
// TODO: Implements IFormattable
public string FormatValue(uint val)
- {
- string FormatFloat()
- {
- var _float = NumberExtensions.ReinterpretAsF32(val);
- return _float.ToString(NumberFormatInfo.InvariantInfo);
- }
-
- string FormatBinary()
- {
- var str = Convert.ToString(val, 2).PadLeft(32, '0');
- for (var i = 28; i > 0; i -= 4)
- {
- str = str.Insert(i, " ");
- }
- return str;
- }
-
- return Type switch
- {
- _ when !IsValid => "-",
- WatchDisplayType.Unsigned => val.ToString(),
- WatchDisplayType.Signed => ((int)val).ToString(),
- WatchDisplayType.Hex => $"{val:X8}",
- WatchDisplayType.FixedPoint_20_12 => ((int)val / 4096.0).ToString("0.######", NumberFormatInfo.InvariantInfo),
- WatchDisplayType.FixedPoint_16_16 => ((int)val / 65536.0).ToString("0.######", NumberFormatInfo.InvariantInfo),
- WatchDisplayType.Float => FormatFloat(),
- WatchDisplayType.Binary => FormatBinary(),
- _ => val.ToString(),
- };
- }
+ => IsValid ? Watch.FormatValue(val, Size, Type) : "-";
- ///
- /// Get a string representation of difference
- /// between current value and the previous one
- ///
public override string Diff => $"{_value - (long)_previous:+#;-#;0}";
- ///
- /// Returns true if the Watch is valid, false otherwise
- ///
- public override bool IsValid => Domain.Size == 0 || Address < (Domain.Size - 3);
-
- ///
- /// Get the maximum possible value
- ///
- public override uint MaxValue => uint.MaxValue;
-
- ///
- /// Get the current value
- ///
public override int Value => (int)GetDWord();
- ///
- /// Get a string representation of the current value
- ///
public override string ValueString => FormatValue(GetDWord());
- ///
- /// Get the previous value
- ///
public override uint Previous => _previous;
- ///
- /// Get a string representation of the previous value
- ///
public override string PreviousStr => FormatValue(_previous);
}
}
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 00000000000..78b66a8d1f6
--- /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/SeparatorWatch.cs b/src/BizHawk.Client.Common/tools/Watch/SeparatorWatch.cs
index 50497f3e7d5..4cc21bb0fbb 100644
--- a/src/BizHawk.Client.Common/tools/Watch/SeparatorWatch.cs
+++ b/src/BizHawk.Client.Common/tools/Watch/SeparatorWatch.cs
@@ -56,11 +56,6 @@ public override IEnumerable AvailableTypes()
///
public override string PreviousStr => "";
- ///
- /// TTransform the current instance into a displayable (short representation) string
- /// It's used by the "Display on screen" option in the RamWatch window
- ///
- /// A well formatted string representation
public override string ToDisplayString()
{
return string.IsNullOrEmpty(Notes)
@@ -68,10 +63,6 @@ public override string ToDisplayString()
: Notes;
}
- ///
- /// Transforms the current instance into a string
- ///
- /// A representation of the current
public override string ToString()
{
return $"0\tS\t_\t1\t\t{Notes.Trim('\r', '\n')}";
@@ -99,11 +90,6 @@ public override void ResetPrevious()
///
public override string Diff => "";
- ///
- /// Ignore that stuff
- ///
- public override uint MaxValue => 0;
-
///
/// Ignore that stuff
///
diff --git a/src/BizHawk.Client.Common/tools/Watch/Watch.cs b/src/BizHawk.Client.Common/tools/Watch/Watch.cs
index f1f0d24e35f..fbe50899eaf 100644
--- a/src/BizHawk.Client.Common/tools/Watch/Watch.cs
+++ b/src/BizHawk.Client.Common/tools/Watch/Watch.cs
@@ -19,6 +19,78 @@ public abstract class Watch
IEquatable,
IComparable
{
+ private const string ERR_MSG_INVALID_WIDTH = "can only parse numeric strings for 1-, 2-, or 4-octet watches";
+
+ public static string FormatValue(uint value, WatchSize width, WatchDisplayType dispType)
+ => width switch
+ {
+ WatchSize.Byte => dispType switch
+ {
+ WatchDisplayType.Signed => unchecked((sbyte) value).ToString(),
+ WatchDisplayType.Unsigned => value.ToString(),
+ WatchDisplayType.Hex => $"{value:X2}",
+ WatchDisplayType.Binary => Convert.ToString(unchecked((byte) value), toBase: 2).PadLeft(8, '0').Insert(4, " "),
+ _ => value.ToString(), //TODO throw instead?
+ },
+ WatchSize.Word => dispType switch
+ {
+ WatchDisplayType.Signed => unchecked((short) value).ToString(),
+ WatchDisplayType.Unsigned => value.ToString(),
+ WatchDisplayType.Hex => $"{value:X4}",
+ WatchDisplayType.Binary => Convert.ToString(unchecked((ushort) value), toBase: 2).PadLeft(16, '0')
+ .Insert(8, " ").Insert(4, " ").Insert(14, " "),
+ WatchDisplayType.FixedPoint_12_4 => $"{unchecked((short) value) / 16.0:F4}",
+ _ => value.ToString(), //TODO throw instead?
+ },
+ WatchSize.DWord => dispType switch
+ {
+ WatchDisplayType.Signed => unchecked((int) value).ToString(),
+ WatchDisplayType.Unsigned => value.ToString(),
+ WatchDisplayType.Hex => $"{value:X8}",
+ WatchDisplayType.Binary => Convert.ToString(value, toBase: 2).PadLeft(32, '0')
+ .Insert(28, " ").Insert(24, " ").Insert(20, " ").Insert(16, " ").Insert(12, " ").Insert(8, " ").Insert(4, " "),
+ WatchDisplayType.FixedPoint_20_12 => $"{unchecked((int) value) / 4096.0:0.######}",
+ WatchDisplayType.FixedPoint_16_16 => $"{unchecked((int) value) / 65536.0:0.######}",
+ WatchDisplayType.Float => NumberExtensions.ReinterpretAsF32(value).ToString(NumberFormatInfo.InvariantInfo),
+ _ => value.ToString(), //TODO throw instead?
+ },
+ _ => throw new ArgumentOutOfRangeException(paramName: nameof(width), width, message: ERR_MSG_INVALID_WIDTH),
+ };
+
+ public static uint ParseValue(string value, WatchSize width, WatchDisplayType dispType)
+ => width switch
+ {
+ WatchSize.Byte => dispType switch
+ {
+ WatchDisplayType.Signed => unchecked((byte) sbyte.Parse(value)),
+ WatchDisplayType.Unsigned => byte.Parse(value),
+ WatchDisplayType.Hex => byte.Parse(value, NumberStyles.HexNumber),
+ WatchDisplayType.Binary => Convert.ToByte(value, fromBase: 2),
+ _ => 0, //TODO throw instead?
+ },
+ WatchSize.Word => dispType switch
+ {
+ WatchDisplayType.Signed => unchecked((ushort) short.Parse(value)),
+ WatchDisplayType.Unsigned => ushort.Parse(value),
+ WatchDisplayType.Hex => ushort.Parse(value, NumberStyles.HexNumber),
+ WatchDisplayType.Binary => Convert.ToUInt16(value, fromBase: 2),
+ WatchDisplayType.FixedPoint_12_4 => unchecked((ushort) (16.0 * double.Parse(value, NumberFormatInfo.InvariantInfo))),
+ _ => 0, //TODO throw instead?
+ },
+ WatchSize.DWord => dispType switch
+ {
+ WatchDisplayType.Signed => unchecked((uint) int.Parse(value)),
+ WatchDisplayType.Unsigned => uint.Parse(value),
+ WatchDisplayType.Hex => uint.Parse(value, NumberStyles.HexNumber),
+ WatchDisplayType.Binary => Convert.ToUInt32(value, fromBase: 2),
+ WatchDisplayType.FixedPoint_20_12 => unchecked((uint) (4096.0 * double.Parse(value, NumberFormatInfo.InvariantInfo))),
+ WatchDisplayType.FixedPoint_16_16 => unchecked((uint) (65536.0 * double.Parse(value, NumberFormatInfo.InvariantInfo))),
+ WatchDisplayType.Float => NumberExtensions.ReinterpretAsUInt32(float.Parse(value, NumberFormatInfo.InvariantInfo)),
+ _ => 0, //TODO throw instead?
+ },
+ _ => throw new ArgumentOutOfRangeException(paramName: nameof(width), width, message: ERR_MSG_INVALID_WIDTH),
+ };
+
private MemoryDomain _domain;
private WatchDisplayType _type;
@@ -285,7 +357,7 @@ protected uint GetDWord()
: 0;
}
- protected void PokeByte(byte val)
+ protected internal void PokeByte(byte val)
{
if (IsValid)
{
@@ -293,7 +365,7 @@ protected void PokeByte(byte val)
}
}
- protected void PokeWord(ushort val)
+ protected internal void PokeWord(ushort val)
{
if (IsValid)
{
@@ -301,7 +373,7 @@ protected void PokeWord(ushort val)
}
}
- protected void PokeDWord(uint val)
+ protected internal void PokeDWord(uint val)
{
if (IsValid)
{
@@ -430,7 +502,15 @@ public override string ToString()
///
/// Gets the maximum possible value
///
- public abstract uint MaxValue { get; }
+ public uint MaxValue
+ => Size switch
+ {
+ WatchSize.Separator => 0,
+ WatchSize.Byte => byte.MaxValue,
+ WatchSize.Word => ushort.MaxValue,
+ WatchSize.DWord => uint.MaxValue,
+ _ => throw new InvalidOperationException(),
+ };
///
/// Gets the current value
@@ -445,7 +525,8 @@ public override string ToString()
///
/// Returns true if the Watch is valid, false otherwise
///
- public abstract bool IsValid { get; }
+ public virtual bool IsValid
+ => Domain.Size is 0 || Address <= Domain.Size - unchecked((long) Size);
///
/// Try to sets the value into the
@@ -477,7 +558,7 @@ public override string ToString()
///
/// 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.Common/tools/Watch/WordWatch.cs b/src/BizHawk.Client.Common/tools/Watch/WordWatch.cs
index f5fd8c0ae99..10fcaa6bd0e 100644
--- a/src/BizHawk.Client.Common/tools/Watch/WordWatch.cs
+++ b/src/BizHawk.Client.Common/tools/Watch/WordWatch.cs
@@ -1,5 +1,5 @@
using System.Collections.Generic;
-using System.Globalization;
+
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
@@ -43,41 +43,18 @@ internal WordWatch(MemoryDomain domain, long address, WatchDisplayType type, boo
WatchDisplayType.FixedPoint_12_4,
];
- ///
- /// Get a list a that can be used for this
- ///
- /// An enumeration that contains all valid
public override IEnumerable AvailableTypes() => ValidTypes;
- ///
- /// Reset the previous value; set it to the current one
- ///
public override void ResetPrevious()
{
_previous = GetWord();
}
- ///
- /// Try to sets the value into the
- /// at the current address
- ///
- /// Value to set
- /// True if value successfully sets; otherwise, false
public override bool Poke(string value)
{
try
{
- ushort val = Type switch
- {
- WatchDisplayType.Unsigned => ushort.Parse(value),
- WatchDisplayType.Signed => (ushort)short.Parse(value),
- WatchDisplayType.Hex => ushort.Parse(value, NumberStyles.HexNumber),
- WatchDisplayType.Binary => Convert.ToUInt16(value, 2),
- WatchDisplayType.FixedPoint_12_4 => (ushort)(double.Parse(value, NumberFormatInfo.InvariantInfo) * 16.0),
- _ => 0,
- };
-
- PokeWord(val);
+ PokeWord(unchecked((ushort) Watch.ParseValue(value, Size, Type)));
return true;
}
catch
@@ -86,9 +63,6 @@ public override bool Poke(string value)
}
}
- ///
- /// Update the Watch (read it from
- ///
public override void Update(PreviousType previousType)
{
switch (previousType)
@@ -120,57 +94,16 @@ public override void Update(PreviousType previousType)
// TODO: Implements IFormattable
public string FormatValue(ushort val)
- {
- return Type switch
- {
- _ when !IsValid => "-",
- WatchDisplayType.Unsigned => val.ToString(),
- WatchDisplayType.Signed => ((short) val).ToString(), WatchDisplayType.Hex => $"{val:X4}",
- WatchDisplayType.FixedPoint_12_4 => ((short)val / 16.0).ToString("F4", NumberFormatInfo.InvariantInfo),
- WatchDisplayType.Binary => Convert
- .ToString(val, 2)
- .PadLeft(16, '0')
- .Insert(8, " ")
- .Insert(4, " ")
- .Insert(14, " "),
- _ => val.ToString(),
- };
- }
+ => IsValid ? Watch.FormatValue(val, Size, Type) : "-";
- ///
- /// Get a string representation of difference
- /// between current value and the previous one
- ///
public override string Diff => $"{_value - _previous:+#;-#;0}";
- ///
- /// Returns true if the Watch is valid, false otherwise
- ///
- public override bool IsValid => Domain.Size == 0 || Address < (Domain.Size - 1);
-
- ///
- /// Get the maximum possible value
- ///
- public override uint MaxValue => ushort.MaxValue;
-
- ///
- /// Gets the current value
- ///
public override int Value => GetWord();
- ///
- /// Get a string representation of the current value
- ///
public override string ValueString => FormatValue(GetWord());
- ///
- /// Get the previous value
- ///
public override uint Previous => _previous;
- ///
- /// Get a string representation of the previous value
- ///
public override string PreviousStr => FormatValue(_previous);
}
}
diff --git a/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs b/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs
index 88e6c05b9ac..da7cc06fde9 100644
--- a/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/Watch/WatchEditor.cs
@@ -25,6 +25,8 @@ public enum Mode
private Mode _mode = Mode.New;
private bool _loading = true;
+ private string _sysBusDomainName = null!;
+
private bool _changedSize;
private bool _changedDisplayType;
@@ -34,6 +36,8 @@ public enum Mode
private readonly HexTextBox AddressBox;
+ private readonly TextBox AddressWithPointersBox;
+
private readonly CheckBox BigEndianCheckBox;
private readonly ComboBox DisplayTypeDropDown;
@@ -54,6 +58,8 @@ private int SelectedWidth
private readonly ComboBox SizeDropDown;
+ private readonly CheckBoxEx UsePointerSyntaxCheckbox;
+
public WatchEditor()
{
_changedDisplayType = false;
@@ -94,8 +100,40 @@ public WatchEditor()
{
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 @@ private void RamWatchNewWatch_Load(object sender, EventArgs e)
}
_loading = false;
+ _sysBusDomainName = MemoryDomains.SystemBus.ToString();
SetAddressBoxProperties();
switch (_mode)
@@ -206,7 +245,9 @@ private void RamWatchNewWatch_Load(object sender, EventArgs e)
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 @@ private void RamWatchNewWatch_Load(object sender, EventArgs e)
{
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 @@ private void Ok_Click(object sender, EventArgs e)
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();