diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAMemoryCallbackSystem.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAMemoryCallbackSystem.cs index 4b9c5b8fd6..490a028e71 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAMemoryCallbackSystem.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAMemoryCallbackSystem.cs @@ -9,16 +9,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA public class MGBAMemoryCallbackSystem : IMemoryCallbackSystem { private readonly MGBAHawk _mgba; - private LibmGBA.ExecCallback _executeCallback; - private readonly Dictionary _execPcs = new Dictionary { [0] = null }; + private readonly LibmGBA.MemCallback _readWriteCallback; + private readonly LibmGBA.ExecCallback _executeCallback; + private readonly List _callbacks = new(); public MGBAMemoryCallbackSystem(MGBAHawk mgba) { _mgba = mgba; + _readWriteCallback = RunReadWriteCallback; + _executeCallback = RunExecCallback; } - private readonly List _callbacks = new List(); - public string[] AvailableScopes { get; } = { "System Bus" }; public bool ExecuteCallbacksAvailable => true; @@ -50,18 +51,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA throw new NotImplementedException("Wildcard callbacks (no address specified) not currently implemented."); } + if (callback.AddressMask != 0xFFFFFFFF) + { + throw new NotImplementedException("Non 0xFFFFFFFF address masks are not currently implemented."); + } + var container = new CallbackContainer(callback); if (container.Callback.Type == MemoryCallbackType.Execute) { - _executeCallback = RunExecCallback; - _execPcs[callback.Address.Value] = callback; MGBAHawk.ZZHacky.BizSetExecCallback(_mgba.Core, _executeCallback); } else { - MGBAHawk.ZZHacky.BizSetMemCallback(_mgba.Core, container.CallDelegate); container.ID = MGBAHawk.ZZHacky.BizSetWatchpoint(_mgba.Core, callback.Address.Value, container.WatchPointType); + MGBAHawk.ZZHacky.BizSetMemCallback(_mgba.Core, _readWriteCallback); } _callbacks.Add(container); @@ -69,27 +73,40 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA private void Remove(CallbackContainer cb) { - if (MGBAHawk.ZZHacky.BizClearWatchpoint(_mgba.Core, cb.ID)) _callbacks.Remove(cb); + if (cb.Callback.Type == MemoryCallbackType.Execute) + { + _callbacks.Remove(cb); + + if (!HasExecutes) + { + MGBAHawk.ZZHacky.BizSetExecCallback(_mgba.Core, null); + } + } + else + { + if (!MGBAHawk.ZZHacky.BizClearWatchpoint(_mgba.Core, cb.ID)) + { + throw new InvalidOperationException("Unable to clear watchpoint???"); + } + + _callbacks.Remove(cb); + + if (!HasReads && !HasWrites) + { + MGBAHawk.ZZHacky.BizSetMemCallback(_mgba.Core, null); + } + } } public void Remove(MemoryCallbackDelegate action) { - var cbToRemove = _callbacks.SingleOrDefault(container => container.Callback.Callback == action); - if (cbToRemove == null) return; + var cbToRemove = _callbacks.SingleOrDefault(c => c.Callback.Callback == action); + if (cbToRemove == null) + { + return; + } - if (cbToRemove.Callback.Type is MemoryCallbackType.Execute) - { - _callbacks.Remove(cbToRemove); - if (!_callbacks.Any(cb => cb.Callback.Type is MemoryCallbackType.Execute)) - { - _executeCallback = null; - MGBAHawk.ZZHacky.BizSetExecCallback(_mgba.Core, null); - } - } - else - { - Remove(cbToRemove); - } + Remove(cbToRemove); } public void RemoveAll(IEnumerable actions) @@ -102,7 +119,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA public void Clear() { - foreach (var cb in _callbacks) Remove(cb); + foreach (var cb in _callbacks) + { + Remove(cb); + } } public IEnumerator GetEnumerator() => _callbacks.Select(c => c.Callback).GetEnumerator(); @@ -115,11 +135,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA // Not a thing in this implementation } + private void RunReadWriteCallback(uint addr, LibmGBA.mWatchpointType type, uint oldValue, uint newValue) + { + var flags = type switch + { + LibmGBA.mWatchpointType.WATCHPOINT_READ => MemoryCallbackFlags.AccessRead, + LibmGBA.mWatchpointType.WATCHPOINT_WRITE => MemoryCallbackFlags.AccessWrite, + _ => throw new InvalidOperationException("Invalid watchpoint type"), + }; + + foreach (var cb in _callbacks) + { + if (cb.WatchPointType == type && cb.Callback.Address == addr) + { + cb.Callback.Callback?.Invoke(addr, newValue, (uint)flags); + } + } + } + private void RunExecCallback(uint pc) { - if (_execPcs.TryGetValue(pc, out var callback)) - { - callback?.Callback?.Invoke(pc, 0, 0); + foreach (var cb in _callbacks) + { + if (cb.Callback.Address == pc) + { + cb.Callback.Callback?.Invoke(pc, 0, (uint)MemoryCallbackFlags.AccessExecute); + } } } @@ -144,36 +185,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA public CallbackContainer(IMemoryCallback callBack) { Callback = callBack; - CallDelegate = Call; } - public IMemoryCallback Callback { get; } - - // the core returns this when setting a wp and needs it to clear that wp + public IMemoryCallback Callback { get; } + + // the core returns this when setting a wp and needs it to clear that wp public int ID { get; set; } public LibmGBA.mWatchpointType WatchPointType { get { - switch (Callback.Type) - { - default: - case MemoryCallbackType.Read: - return LibmGBA.mWatchpointType.WATCHPOINT_READ; - case MemoryCallbackType.Write: - return LibmGBA.mWatchpointType.WATCHPOINT_WRITE; - case MemoryCallbackType.Execute: - throw new NotImplementedException("Executes can not be used from watch points."); - } + return Callback.Type switch + { + MemoryCallbackType.Read => LibmGBA.mWatchpointType.WATCHPOINT_READ, + MemoryCallbackType.Write => LibmGBA.mWatchpointType.WATCHPOINT_WRITE, + MemoryCallbackType.Execute => throw new NotImplementedException("Executes can not be used from watch points."), + _ => throw new InvalidOperationException("Invalid callback type"), + }; } } - - public LibmGBA.MemCallback CallDelegate; - - private void Call(uint addr, LibmGBA.mWatchpointType type, uint oldValue, uint newValue) - { - Callback.Callback?.Invoke(addr, newValue, 0); - } } }