Skip to content

Commit 17bfab9

Browse files
committed
Add 64-bit-wide watches
1 parent d34f8a3 commit 17bfab9

File tree

8 files changed

+306
-4
lines changed

8 files changed

+306
-4
lines changed

src/BizHawk.Client.Common/tools/Cheat.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public MemoryDomain Domain
105105
WatchSize.Byte => ((ByteWatch) _watch).FormatValue((byte)_val),
106106
WatchSize.Word => ((WordWatch) _watch).FormatValue((ushort)_val),
107107
WatchSize.DWord => ((DWordWatch) _watch).FormatValue((uint)_val),
108+
WatchSize.QWord => ((QWordWatch) _watch).FormatValue(unchecked((ulong) _val)),
108109
WatchSize.Separator => "",
109110
_ => string.Empty,
110111
};
@@ -120,6 +121,7 @@ public string CompareStr
120121
WatchSize.Byte => ((ByteWatch) _watch).FormatValue((byte)_compare.Value),
121122
WatchSize.Word => ((WordWatch) _watch).FormatValue((ushort)_compare.Value),
122123
WatchSize.DWord => ((DWordWatch) _watch).FormatValue((uint)_compare.Value),
124+
WatchSize.QWord => ((QWordWatch) _watch).FormatValue(unchecked((ulong) _compare.Value)),
123125
WatchSize.Separator => "",
124126
_ => string.Empty,
125127
};
@@ -186,6 +188,9 @@ public void Pulse()
186188
case WatchSize.DWord:
187189
_watch.Poke(((DWordWatch)_watch).FormatValue((uint)_val));
188190
break;
191+
case WatchSize.QWord:
192+
_watch.Poke(((QWordWatch) _watch).FormatValue(unchecked((ulong) _val)));
193+
break;
189194
}
190195
}
191196

@@ -237,6 +242,8 @@ public bool Contains(long addr)
237242
return addr == _watch.Address || addr == _watch.Address + 1;
238243
case WatchSize.DWord:
239244
return addr >= _watch.Address && addr <= _watch.Address + 3;
245+
case WatchSize.QWord:
246+
return _watch.Address <= addr && addr <= _watch.Address + (sizeof(ulong) - 1);
240247
}
241248
}
242249

src/BizHawk.Client.Common/tools/RamSearchEngine/Extensions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ public static IEnumerable<IMiniWatch> ToDWords(this IEnumerable<long> addresses,
2121
? addresses.ToDetailedDWords(settings.Domain, settings.BigEndian)
2222
: addresses.ToDWords(settings.Domain, settings.BigEndian);
2323

24+
public static IEnumerable<IMiniWatch> ToQWords(this IEnumerable<long> addresses, SearchEngineSettings settings)
25+
=> settings.IsDetailed()
26+
? addresses.ToDetailedQWords(settings.Domain, settings.BigEndian)
27+
: addresses.ToQWords(settings.Domain, settings.BigEndian);
28+
2429
private static IEnumerable<IMiniWatch> ToBytes(this IEnumerable<long> addresses, MemoryDomain domain)
2530
=> addresses.Select(a => new MiniByteWatch(domain, a));
2631

@@ -38,5 +43,11 @@ private static IEnumerable<IMiniWatch> ToDWords(this IEnumerable<long> addresses
3843

3944
private static IEnumerable<IMiniWatch> ToDetailedDWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
4045
=> addresses.Select(a => new MiniDWordWatchDetailed(domain, a, bigEndian));
46+
47+
private static IEnumerable<IMiniWatch> ToQWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
48+
=> addresses.Select(a => new MiniQWordWatch(domain, a, bigEndian));
49+
50+
private static IEnumerable<IMiniWatch> ToDetailedQWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
51+
=> addresses.Select(a => new MiniQWordWatchDetailed(domain, a, bigEndian));
4152
}
4253
}

src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatch.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,16 @@ protected override ulong GetValueInner(long address, MemoryDomain domain, bool b
7979
protected override bool IsValid(long address, MemoryDomain domain)
8080
=> 0L <= address && address <= domain.Size - sizeof(uint);
8181
}
82+
83+
internal class MiniQWordWatch : MiniWatchBase
84+
{
85+
public MiniQWordWatch(MemoryDomain domain, long addr, bool bigEndian)
86+
: base(addr: addr, prevValue: domain.PeekUint(addr, bigEndian: bigEndian)) {}
87+
88+
protected override ulong GetValueInner(long address, MemoryDomain domain, bool bigEndian)
89+
=> domain.PeekUlong(address, bigEndian);
90+
91+
protected override bool IsValid(long address, MemoryDomain domain)
92+
=> 0L <= address && address <= domain.Size - sizeof(ulong);
93+
}
8294
}

src/BizHawk.Client.Common/tools/RamSearchEngine/IMiniWatchDetails.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,41 @@ public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
126126

127127
public void ClearChangeCount() => ChangeCount = 0;
128128
}
129+
130+
internal sealed class MiniQWordWatchDetailed : MiniQWordWatch, IMiniWatchDetails
131+
{
132+
private ulong _current;
133+
134+
public MiniQWordWatchDetailed(MemoryDomain domain, long addr, bool bigEndian) : base(domain, addr, bigEndian)
135+
=> Previous = _current = GetValueInner(Address, domain, bigEndian: bigEndian);
136+
137+
public override void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
138+
=> Previous = _current;
139+
140+
public ulong Current => _current;
141+
142+
public int ChangeCount { get; private set; }
143+
144+
public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
145+
{
146+
var newValue = GetValueInner(Address, domain, bigEndian: bigEndian);
147+
if (newValue != _current)
148+
{
149+
ChangeCount++;
150+
if (type is PreviousType.LastChange)
151+
{
152+
Previous = _current;
153+
}
154+
}
155+
156+
if (type is PreviousType.LastFrame)
157+
{
158+
Previous = _current;
159+
}
160+
161+
_current = newValue;
162+
}
163+
164+
public void ClearChangeCount() => ChangeCount = 0;
165+
}
129166
}

src/BizHawk.Client.Common/tools/RamSearchEngine/RamSearchEngine.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,22 @@ public void Start()
109109
}
110110
}
111111
break;
112+
case WatchSize.QWord:
113+
if (_settings.IsDetailed())
114+
{
115+
for (var i = 0; i < _watchList.Length; i++)
116+
{
117+
_watchList[i] = new MiniQWordWatchDetailed(domain, i * stepSize, _settings.BigEndian);
118+
}
119+
}
120+
else
121+
{
122+
for (var i = 0; i < _watchList.Length; i++)
123+
{
124+
_watchList[i] = new MiniQWordWatch(domain, i * stepSize, _settings.BigEndian);
125+
}
126+
}
127+
break;
112128
}
113129
}
114130

@@ -284,6 +300,7 @@ public void AddRange(IEnumerable<long> addresses, bool append)
284300
WatchSize.Byte => addresses.ToBytes(_settings),
285301
WatchSize.Word => addresses.ToWords(_settings),
286302
WatchSize.DWord => addresses.ToDWords(_settings),
303+
WatchSize.QWord => addresses.ToQWords(_settings),
287304
_ => addresses.ToBytes(_settings),
288305
};
289306

@@ -302,6 +319,8 @@ public void ConvertTo(WatchSize size)
302319
WatchSize.Word => addresses.Where(static address => address % 2 == 0).ToWords(_settings).ToArray(),
303320
WatchSize.DWord when _settings.CheckMisAligned => addresses.ToDWords(_settings).ToArray(),
304321
WatchSize.DWord => addresses.Where(static address => address % 4 == 0).ToDWords(_settings).ToArray(),
322+
WatchSize.QWord when _settings.CheckMisAligned => addresses.ToQWords(_settings).ToArray(),
323+
WatchSize.QWord => addresses.Where(static address => address % sizeof(ulong) is 0).ToQWords(_settings).ToArray(),
305324
_ => _watchList,
306325
};
307326

@@ -325,6 +344,15 @@ private IEnumerable<long> AllAddresses()
325344
yield return addr + 2;
326345
yield return addr + 3;
327346
break;
347+
case WatchSize.QWord:
348+
yield return addr + 1;
349+
yield return addr + 2;
350+
yield return addr + 3;
351+
yield return addr + 4;
352+
yield return addr + 5;
353+
yield return addr + 6;
354+
yield return addr + 7;
355+
break;
328356
}
329357
}
330358
}
@@ -649,6 +677,7 @@ private long SignExtendAsNeeded(ulong val)
649677
WatchSize.Byte => (sbyte) val,
650678
WatchSize.Word => (short) val,
651679
WatchSize.DWord => (int) val,
680+
WatchSize.QWord => (long) val,
652681
_ => (sbyte) val,
653682
};
654683
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
using System.Collections.Generic;
2+
using System.Globalization;
3+
using BizHawk.Common.NumberExtensions;
4+
using BizHawk.Emulation.Common;
5+
6+
namespace BizHawk.Client.Common
7+
{
8+
/// <summary>
9+
/// This class holds a quad word (64 bits) <see cref="Watch"/>
10+
/// </summary>
11+
public sealed class QWordWatch : Watch
12+
{
13+
/// <summary>
14+
/// Gets a list of <see cref="WatchDisplayType"/> for a <see cref="QWordWatch"/>
15+
/// </summary>
16+
public static readonly IReadOnlyList<WatchDisplayType> ValidTypes = [
17+
WatchDisplayType.Unsigned,
18+
WatchDisplayType.Signed,
19+
WatchDisplayType.Hex,
20+
WatchDisplayType.Binary,
21+
WatchDisplayType.Float,
22+
];
23+
24+
private ulong _value;
25+
26+
private ulong _previous;
27+
28+
/// <summary>
29+
/// Initializes a new instance of the <see cref="QWordWatch"/> class
30+
/// </summary>
31+
/// <param name="domain"><see cref="MemoryDomain"/> where you want to track</param>
32+
/// <param name="address">The address you want to track</param>
33+
/// <param name="type">How you you want to display the value See <see cref="WatchDisplayType"/></param>
34+
/// <param name="bigEndian">Specify the endianess. true for big endian</param>
35+
/// <param name="note">A custom note about the <see cref="Watch"/></param>
36+
/// <param name="value">Current value</param>
37+
/// <param name="previous">Previous value</param>
38+
/// <param name="changeCount">How many times value has changed</param>
39+
/// <exception cref="ArgumentException">Occurs when a <see cref="WatchDisplayType"/> is incompatible with <see cref="WatchSize.QWord"/></exception>
40+
internal QWordWatch(
41+
MemoryDomain domain,
42+
long address,
43+
WatchDisplayType type,
44+
bool bigEndian,
45+
string note,
46+
ulong value,
47+
ulong previous,
48+
int changeCount)
49+
: base(domain, address, WatchSize.QWord, type, bigEndian: bigEndian, note)
50+
{
51+
_value = value is 0 ? GetQWord() : value;
52+
_previous = previous;
53+
ChangeCount = changeCount;
54+
}
55+
56+
/// <summary>
57+
/// Get a list of <see cref="WatchDisplayType"/> that can be used for a <see cref="QWordWatch"/>
58+
/// </summary>
59+
/// <returns>An enumeration that contains all valid <see cref="WatchDisplayType"/></returns>
60+
public override IReadOnlyList<WatchDisplayType> AvailableTypes()
61+
=> ValidTypes;
62+
63+
/// <summary>
64+
/// Reset the previous value; set it to the current one
65+
/// </summary>
66+
public override void ResetPrevious()
67+
=> _previous = GetQWord();
68+
69+
/// <summary>
70+
/// Try to sets the value into the <see cref="MemoryDomain"/>
71+
/// at the current <see cref="Watch"/> address
72+
/// </summary>
73+
/// <param name="value">Value to set</param>
74+
/// <returns>True if value successfully sets; otherwise, false</returns>
75+
public override bool Poke(string value)
76+
{
77+
try
78+
{
79+
PokeQWord(Type switch
80+
{
81+
WatchDisplayType.Unsigned => ulong.Parse(value),
82+
WatchDisplayType.Signed => (ulong) long.Parse(value),
83+
WatchDisplayType.Hex => ulong.Parse(value, NumberStyles.HexNumber),
84+
WatchDisplayType.Float => NumberExtensions.ReinterpretAsUInt64(float.Parse(value, NumberFormatInfo.InvariantInfo)),
85+
WatchDisplayType.Binary => Convert.ToUInt64(value, fromBase: 2),
86+
_ => 0,
87+
});
88+
return true;
89+
}
90+
catch
91+
{
92+
return false;
93+
}
94+
}
95+
96+
/// <summary>
97+
/// Update the Watch (read it from <see cref="MemoryDomain"/>
98+
/// </summary>
99+
public override void Update(PreviousType previousType)
100+
{
101+
switch (previousType)
102+
{
103+
case PreviousType.Original:
104+
return;
105+
case PreviousType.LastChange:
106+
var nextValue = GetQWord();
107+
if (nextValue != _value)
108+
{
109+
_previous = nextValue;
110+
ChangeCount++;
111+
}
112+
_value = nextValue;
113+
break;
114+
case PreviousType.LastFrame:
115+
_previous = _value;
116+
_value = GetQWord();
117+
if (_value != Previous) ChangeCount++;
118+
break;
119+
}
120+
}
121+
122+
// TODO: Implements IFormattable
123+
public string FormatValue(ulong val)
124+
{
125+
string FormatFloat()
126+
=> NumberExtensions.ReinterpretAsF64(val).ToString(NumberFormatInfo.InvariantInfo);
127+
string FormatBinary()
128+
{
129+
var str = Convert.ToString(unchecked((long) val), toBase: 2).PadLeft(64, '0');
130+
for (var i = 60; i > 0; i -= 4) str = str.Insert(i, " ");
131+
return str;
132+
}
133+
return Type switch
134+
{
135+
_ when !IsValid => "-",
136+
WatchDisplayType.Unsigned => val.ToString(),
137+
WatchDisplayType.Signed => ((long) val).ToString(),
138+
WatchDisplayType.Hex => $"{val:X16}",
139+
WatchDisplayType.Float => FormatFloat(),
140+
WatchDisplayType.Binary => FormatBinary(),
141+
_ => val.ToString(),
142+
};
143+
}
144+
145+
/// <summary>
146+
/// Get a string representation of difference
147+
/// between current value and the previous one
148+
/// </summary>
149+
public override string Diff
150+
=> $"{unchecked((long) _value - (long) _previous):+#;-#;0}";
151+
152+
/// <summary>
153+
/// Returns true if the Watch is valid, false otherwise
154+
/// </summary>
155+
public override bool IsValid
156+
=> Domain.Size is 0 || Address < (Domain.Size - (sizeof(ulong) - 1));
157+
158+
/// <summary>
159+
/// Get the maximum possible value
160+
/// </summary>
161+
public override ulong MaxValue
162+
=> ulong.MaxValue;
163+
164+
/// <summary>
165+
/// Get the current value
166+
/// </summary>
167+
public override long Value
168+
=> unchecked((long) GetQWord());
169+
170+
/// <summary>
171+
/// Get a string representation of the current value
172+
/// </summary>
173+
public override string ValueString
174+
=> FormatValue(GetQWord());
175+
176+
/// <summary>
177+
/// Get the previous value
178+
/// </summary>
179+
public override ulong Previous
180+
=> _previous;
181+
182+
/// <summary>
183+
/// Get a string representation of the previous value
184+
/// </summary>
185+
public override string PreviousStr
186+
=> FormatValue(_previous);
187+
}
188+
}

0 commit comments

Comments
 (0)