Example #1
0
        void WireMemoryDomainPointers()
        {
            var s = new LibmGBA.MemoryAreas();

            LibmGBA.BizGetMemoryAreas(core, s);

            _iwram.Data  = s.iwram;
            _ewram.Data  = s.wram;
            _bios.Data   = s.bios;
            _palram.Data = s.palram;
            _vram.Data   = s.vram;
            _oam.Data    = s.oam;
            _rom.Data    = s.rom;
            _sram.Data   = s.sram;
            _sram.SetSize(s.sram_size);

            // special combined ram memory domain

            _cwram.Peek =
                delegate(long addr)
            {
                LibmGBA.BizGetMemoryAreas(core, s);
                if (addr < 0 || addr >= (256 + 32) * 1024)
                {
                    throw new IndexOutOfRangeException();
                }
                if (addr >= 256 * 1024)
                {
                    return(PeekWRAM(s.iwram, addr & 32767));
                }
                else
                {
                    return(PeekWRAM(s.wram, addr));
                }
            };
            _cwram.Poke =
                delegate(long addr, byte val)
            {
                if (addr < 0 || addr >= (256 + 32) * 1024)
                {
                    throw new IndexOutOfRangeException();
                }
                if (addr >= 256 * 1024)
                {
                    PokeWRAM(s.iwram, addr & 32767, val);
                }
                else
                {
                    PokeWRAM(s.wram, addr, val);
                }
            };

            _gpumem = new GBAGPUMemoryAreas
            {
                mmio   = s.mmio,
                oam    = s.oam,
                palram = s.palram,
                vram   = s.vram
            };
        }
Example #2
0
        public void FrameAdvance(IController controller, bool render, bool rendersound = true)
        {
            Frame++;
            if (controller.IsPressed("Power"))
            {
                LibmGBA.BizReset(_core);

                // BizReset caused memorydomain pointers to change.
                WireMemoryDomainPointers();
            }

            IsLagFrame = LibmGBA.BizAdvance(
                _core,
                VBANext.GetButtons(controller),
                _videobuff,
                ref _nsamp,
                _soundbuff,
                RTCTime(),
                (short)controller.GetFloat("Tilt X"),
                (short)controller.GetFloat("Tilt Y"),
                (short)controller.GetFloat("Tilt Z"),
                (byte)(255 - controller.GetFloat("Light Sensor")));

            if (IsLagFrame)
            {
                LagCount++;
            }

            // this should be called in hblank on the appropriate line, but until we implement that, just do it here
            _scanlinecb?.Invoke();
        }
Example #3
0
        private MemoryDomainList CreateMemoryDomains(int romsize)
        {
            var s  = new LibmGBA.MemoryAreas();
            var mm = new List <MemoryDomain>();

            LibmGBA.BizGetMemoryAreas(core, s);

            var l = MemoryDomain.Endian.Little;

            mm.Add(MemoryDomain.FromIntPtr("IWRAM", 32 * 1024, l, s.iwram, true, 4));
            mm.Add(MemoryDomain.FromIntPtr("EWRAM", 256 * 1024, l, s.wram, true, 4));
            mm.Add(MemoryDomain.FromIntPtr("BIOS", 16 * 1024, l, s.bios, false, 4));
            mm.Add(MemoryDomain.FromIntPtr("PALRAM", 1024, l, s.palram, false, 4));
            mm.Add(MemoryDomain.FromIntPtr("VRAM", 96 * 1024, l, s.vram, true, 4));
            mm.Add(MemoryDomain.FromIntPtr("OAM", 1024, l, s.oam, false, 4));
            mm.Add(MemoryDomain.FromIntPtr("ROM", romsize, l, s.rom, false, 4));

            _gpumem = new GBAGPUMemoryAreas
            {
                mmio   = s.mmio,
                oam    = s.oam,
                palram = s.palram,
                vram   = s.vram
            };

            return(new MemoryDomainList(mm));
        }
Example #4
0
        public MGBAHawk(byte[] file, CoreComm comm, SyncSettings syncSettings, Settings settings, bool deterministic)
        {
            _syncSettings          = syncSettings ?? new SyncSettings();
            _settings              = settings ?? new Settings();
            DeterministicEmulation = deterministic;

            byte[] bios = comm.CoreFileProvider.GetFirmware("GBA", "Bios", false);
            DeterministicEmulation &= bios != null;

            if (DeterministicEmulation != deterministic)
            {
                throw new InvalidOperationException("A BIOS is required for deterministic recordings!");
            }
            if (!DeterministicEmulation && bios != null && !_syncSettings.RTCUseRealTime && !_syncSettings.SkipBios)
            {
                // in these situations, this core is deterministic even though it wasn't asked to be
                DeterministicEmulation = true;
            }

            if (bios != null && bios.Length != 16384)
            {
                throw new InvalidOperationException("BIOS must be exactly 16384 bytes!");
            }
            core = LibmGBA.BizCreate(bios);
            if (core == IntPtr.Zero)
            {
                throw new InvalidOperationException("BizCreate() returned NULL!  Bad BIOS?");
            }
            try
            {
                if (!LibmGBA.BizLoad(core, file, file.Length))
                {
                    throw new InvalidOperationException("BizLoad() returned FALSE!  Bad ROM?");
                }

                if (!DeterministicEmulation && _syncSettings.SkipBios)
                {
                    LibmGBA.BizSkipBios(core);
                }

                var ser = new BasicServiceProvider(this);
                ser.Register <IDisassemblable>(new ArmV4Disassembler());
                ser.Register <IMemoryDomains>(CreateMemoryDomains(file.Length));

                ServiceProvider = ser;
                CoreComm        = comm;

                CoreComm.VsyncNum      = 262144;
                CoreComm.VsyncDen      = 4389;
                CoreComm.NominalWidth  = 240;
                CoreComm.NominalHeight = 160;

                InitStates();
            }
            catch
            {
                LibmGBA.BizDestroy(core);
                throw;
            }
        }
Example #5
0
        public bool FrameAdvance(IController controller, bool render, bool renderSound = true)
        {
            Frame++;
            if (controller.IsPressed("Power"))
            {
                LibmGBA.BizReset(Core);

                // BizReset caused memorydomain pointers to change.
                WireMemoryDomainPointers();
            }

            LibmGBA.BizSetTraceCallback(Tracer.Enabled ? _tracecb : null);

            IsLagFrame = LibmGBA.BizAdvance(
                Core,
                LibmGBA.GetButtons(controller),
                render ? _videobuff : _dummyvideobuff,
                ref _nsamp,
                renderSound ? _soundbuff : _dummysoundbuff,
                RTCTime(),
                (short)controller.AxisValue("Tilt X"),
                (short)controller.AxisValue("Tilt Y"),
                (short)controller.AxisValue("Tilt Z"),
                (byte)(255 - controller.AxisValue("Light Sensor")));

            if (IsLagFrame)
            {
                LagCount++;
            }

            // this should be called in hblank on the appropriate line, but until we implement that, just do it here
            _scanlinecb?.Invoke();

            return(true);
        }
Example #6
0
        public void StoreSaveRam(byte[] data)
        {
            if (data.Take(8).SequenceEqual(Encoding.ASCII.GetBytes("GBABATT\0")))
            {
                data = LegacyFix(data);
            }

            int len = LibmGBA.BizGetSaveRamSize(core);

            if (len > data.Length)
            {
                byte[] _tmp = new byte[len];
                Array.Copy(data, _tmp, data.Length);
                for (int i = data.Length; i < len; i++)
                {
                    _tmp[i] = 0xff;
                }
                data = _tmp;
            }
            else if (len < data.Length)
            {
                // we could continue from this, but we don't expect it
                throw new InvalidOperationException("Saveram will be truncated!");
            }
            LibmGBA.BizPutSaveRam(core, data);
        }
        public void SetCpuRegister(string register, int value)
        {
            int index = register?.ToUpper() switch
            {
                "R0" => 0,
                "R1" => 1,
                "R2" => 2,
                "R3" => 3,
                "R4" => 4,
                "R5" => 5,
                "R6" => 6,
                "R7" => 7,
                "R8" => 8,
                "R9" => 9,
                "R10" => 10,
                "R11" => 11,
                "R12" => 12,
                "R13" => 13,
                "R14" => 14,
                "R15" => 15,
                "CPSR" => 16,
                "SPSR" => 17,
                _ => - 1
            };

            if (index != -1)
            {
                LibmGBA.BizSetRegister(Core, index, value);
            }
        }
Example #8
0
 public bool PutSettings(Settings o)
 {
     LibmGBA.Layers mask = 0;
     if (o.DisplayBG0)
     {
         mask |= LibmGBA.Layers.BG0;
     }
     if (o.DisplayBG1)
     {
         mask |= LibmGBA.Layers.BG1;
     }
     if (o.DisplayBG2)
     {
         mask |= LibmGBA.Layers.BG2;
     }
     if (o.DisplayBG3)
     {
         mask |= LibmGBA.Layers.BG3;
     }
     if (o.DisplayOBJ)
     {
         mask |= LibmGBA.Layers.OBJ;
     }
     LibmGBA.BizSetLayerMask(core, mask);
     _settings = o;
     return(false);
 }
Example #9
0
        static MGBAHawk()
        {
            var resolver = new DynamicLibraryImportResolver(
                OSTailoredCode.IsUnixHost ? "libmgba.dll.so" : "mgba.dll", hasLimitedLifetime: false);

            LibmGBA = BizInvoker.GetInvoker <LibmGBA>(resolver, CallingConventionAdapters.Native);
        }
        public void Add(IMemoryCallback callback)
        {
            if (!AvailableScopes.Contains(callback.Scope))
            {
                throw new InvalidOperationException($"{callback.Scope} is not currently supported for callbacks");
            }

            if (!callback.Address.HasValue)
            {
                throw new NotImplementedException("Wildcard callbacks (no address specified) not currently implemented.");
            }

            var container = new CallbackContainer(callback);

            if (container.Callback.Type == MemoryCallbackType.Execute)
            {
                _executeCallback = RunExecCallback;
                _execPcs[callback.Address.Value] = callback;
                LibmGBA.BizSetExecCallback(_executeCallback);
            }
            else
            {
                LibmGBA.BizSetMemCallback(container.CallDelegate);
                container.ID = LibmGBA.BizSetWatchpoint(_mgba.Core, callback.Address.Value, container.WatchPointType);
            }

            _callbacks.Add(container);
        }
Example #11
0
        public void SaveStateBinary(BinaryWriter writer)
        {
            IntPtr p    = IntPtr.Zero;
            int    size = 0;

            if (!LibmGBA.BizStartGetState(Core, ref p, ref size))
            {
                throw new InvalidOperationException("Core failed to save!");
            }

            if (size != _savebuff.Length)
            {
                _savebuff = new byte[size];
            }

            LibmGBA.BizFinishGetState(p, _savebuff, size);

            writer.Write(_savebuff.Length);
            writer.Write(_savebuff, 0, _savebuff.Length);

            // other variables
            writer.Write(IsLagFrame);
            writer.Write(LagCount);
            writer.Write(Frame);
        }
Example #12
0
        public void FrameAdvance(bool render, bool rendersound = true)
        {
            Frame++;
            if (Controller["Power"])
            {
                LibmGBA.BizReset(core);
            }

            IsLagFrame = LibmGBA.BizAdvance(core, VBANext.GetButtons(Controller), videobuff, ref nsamp, soundbuff,
                                            RTCTime(),
                                            (short)Controller.GetFloat("Tilt X"),
                                            (short)Controller.GetFloat("Tilt Y"),
                                            (short)Controller.GetFloat("Tilt Z"),
                                            (byte)(255 - Controller.GetFloat("Light Sensor")));

            if (IsLagFrame)
            {
                LagCount++;
            }
            // this should be called in hblank on the appropriate line, but until we implement that, just do it here
            if (_scanlinecb != null)
            {
                _scanlinecb();
            }
        }
Example #13
0
        public MGBAHawk(byte[] file, CoreComm comm, SyncSettings syncSettings, Settings settings, bool deterministic, GameInfo game)
        {
            _syncSettings          = syncSettings ?? new SyncSettings();
            _settings              = settings ?? new Settings();
            DeterministicEmulation = deterministic;

            byte[] bios = comm.CoreFileProvider.GetFirmware("GBA", "Bios", false);
            DeterministicEmulation &= bios != null;

            if (DeterministicEmulation != deterministic)
            {
                throw new InvalidOperationException("A BIOS is required for deterministic recordings!");
            }

            if (!DeterministicEmulation && bios != null && !_syncSettings.RTCUseRealTime && !_syncSettings.SkipBios)
            {
                // in these situations, this core is deterministic even though it wasn't asked to be
                DeterministicEmulation = true;
            }

            if (bios != null && bios.Length != 16384)
            {
                throw new InvalidOperationException("BIOS must be exactly 16384 bytes!");
            }

            var skipBios = !DeterministicEmulation && _syncSettings.SkipBios;

            _core = LibmGBA.BizCreate(bios, file, file.Length, GetOverrideInfo(game), skipBios);
            if (_core == IntPtr.Zero)
            {
                throw new InvalidOperationException($"{nameof(LibmGBA.BizCreate)}() returned NULL!  Bad BIOS? and/or ROM?");
            }

            try
            {
                CreateMemoryDomains(file.Length);
                var ser = new BasicServiceProvider(this);
                ser.Register <IDisassemblable>(new ArmV4Disassembler());
                ser.Register <IMemoryDomains>(_memoryDomains);

                ServiceProvider        = ser;
                CoreComm               = comm;
                CoreComm.NominalWidth  = 240;
                CoreComm.NominalHeight = 160;
                PutSettings(_settings);

                _tracer = new TraceBuffer()
                {
                    Header = "ARM7: PC, machine code, mnemonic, operands, registers"
                };
                _tracecb = new LibmGBA.TraceCallback((msg) => _tracer.Put(_traceInfo(msg)));
                ser.Register(_tracer);
            }
            catch
            {
                LibmGBA.BizDestroy(_core);
                throw;
            }
        }
Example #14
0
 public void StoreSaveRam(byte[] data)
 {
     if (data.Take(8).SequenceEqual(Encoding.ASCII.GetBytes("GBABATT\0")))
     {
         data = LegacyFix(data);
     }
     LibmGBA.BizPutSaveRam(_core, data, data.Length);
 }
Example #15
0
 public void Dispose()
 {
     if (_core != IntPtr.Zero)
     {
         LibmGBA.BizDestroy(_core);
         _core = IntPtr.Zero;
     }
 }
 public void Clear()
 {
     foreach (var cb in _callbacks)
     {
         if (LibmGBA.BizClearWatchpoint(_mgba.Core, cb.ID))
         {
             _callbacks.Remove(cb);
         }
     }
 }
Example #17
0
        public bool PutSettings(Settings o)
        {
            LibmGBA.Layers mask = 0;
            if (o.DisplayBG0)
            {
                mask |= LibmGBA.Layers.BG0;
            }
            if (o.DisplayBG1)
            {
                mask |= LibmGBA.Layers.BG1;
            }
            if (o.DisplayBG2)
            {
                mask |= LibmGBA.Layers.BG2;
            }
            if (o.DisplayBG3)
            {
                mask |= LibmGBA.Layers.BG3;
            }
            if (o.DisplayOBJ)
            {
                mask |= LibmGBA.Layers.OBJ;
            }
            LibmGBA.BizSetLayerMask(_core, mask);

            LibmGBA.Sounds smask = 0;
            if (o.PlayCh0)
            {
                smask |= LibmGBA.Sounds.CH0;
            }
            if (o.PlayCh1)
            {
                smask |= LibmGBA.Sounds.CH1;
            }
            if (o.PlayCh2)
            {
                smask |= LibmGBA.Sounds.CH2;
            }
            if (o.PlayCh3)
            {
                smask |= LibmGBA.Sounds.CH3;
            }
            if (o.PlayChA)
            {
                smask |= LibmGBA.Sounds.CHA;
            }
            if (o.PlayChB)
            {
                smask |= LibmGBA.Sounds.CHB;
            }
            LibmGBA.BizSetSoundMask(_core, smask);

            _settings = o;
            return(false);
        }
Example #18
0
 public void StoreSaveRam(byte[] data)
 {
     if (data.Take(8).SequenceEqual(Encoding.ASCII.GetBytes("GBABATT\0")))
     {
         data = LegacyFix(data);
     }
     if (!LibmGBA.BizPutSaveRam(_core, data, data.Length))
     {
         throw new InvalidOperationException("BizPutSaveRam returned NULL!");
     }
 }
Example #19
0
        public void SaveStateBinary(BinaryWriter writer)
        {
            LibmGBA.BizGetState(core, savebuff);
            writer.Write(savebuff.Length);
            writer.Write(savebuff);

            // other variables
            writer.Write(IsLagFrame);
            writer.Write(LagCount);
            writer.Write(Frame);
        }
        private void WireMemoryDomainPointers_SaveRam()
        {
            var s = new LibmGBA.MemoryAreas();

            LibmGBA.BizGetMemoryAreas(Core, s);

            _sram.Data = s.sram;
            if (s.sram == IntPtr.Zero)
            {
                s.sram_size = 0;
            }
            _sram.SetSize(s.sram_size);
        }
Example #21
0
 public byte[] CloneSaveRam()
 {
     byte[] ret = new byte[LibmGBA.BizGetSaveRamSize(core)];
     if (ret.Length > 0)
     {
         LibmGBA.BizGetSaveRam(core, ret);
         return(ret);
     }
     else
     {
         return(null);
     }
 }
        public IDictionary <string, RegisterValue> GetCpuFlagsAndRegisters()
        {
            var values = new int[RegisterNames.Length];

            LibmGBA.BizGetRegisters(_core, values);
            var ret = new Dictionary <string, RegisterValue>();

            for (var i = 0; i < RegisterNames.Length; i++)
            {
                ret[RegisterNames[i]] = new RegisterValue(values[i]);
            }

            return(ret);
        }
Example #23
0
        private void StartSaveStateBinaryInternal()
        {
            IntPtr p    = IntPtr.Zero;
            int    size = 0;

            if (!LibmGBA.BizStartGetState(_core, ref p, ref size))
            {
                throw new InvalidOperationException("Core failed to save!");
            }
            if (size != _savebuff.Length)
            {
                _savebuff  = new byte[size];
                _savebuff2 = new byte[size + 13];
            }
            LibmGBA.BizFinishGetState(p, _savebuff, size);
        }
Example #24
0
        public void SaveStateBinary(BinaryWriter writer)
        {
            int size = LibmGBA.BizGetState(core, savebuff, savebuff.Length);

            if (size < 0)
            {
                throw new InvalidOperationException("Core failed to save!");
            }
            writer.Write(size);
            writer.Write(savebuff, 0, size);

            // other variables
            writer.Write(IsLagFrame);
            writer.Write(LagCount);
            writer.Write(Frame);
        }
Example #25
0
        public void LoadStateBinary(BinaryReader reader)
        {
            int length = reader.ReadInt32();

            if (length != savebuff.Length)
            {
                throw new InvalidOperationException("Save buffer size mismatch!");
            }
            reader.Read(savebuff, 0, length);
            LibmGBA.BizPutState(core, savebuff);

            // other variables
            IsLagFrame = reader.ReadBoolean();
            LagCount   = reader.ReadInt32();
            Frame      = reader.ReadInt32();
        }
Example #26
0
        public byte[] CloneSaveRam()
        {
            int len = LibmGBA.BizGetSaveRam(_core, _saveScratch, _saveScratch.Length);

            if (len == _saveScratch.Length)
            {
                throw new InvalidOperationException("Save buffer not long enough");
            }
            if (len == 0)
            {
                return(null);
            }

            var ret = new byte[len];

            Array.Copy(_saveScratch, ret, len);
            return(ret);
        }
        private void CreateMemoryDomains(int romsize)
        {
            var le = MemoryDomain.Endian.Little;

            var mm = new List <MemoryDomain>();

            mm.Add(_iwram  = new MemoryDomainIntPtr("IWRAM", le, IntPtr.Zero, 32 * 1024, true, 4));
            mm.Add(_ewram  = new MemoryDomainIntPtr("EWRAM", le, IntPtr.Zero, 256 * 1024, true, 4));
            mm.Add(_bios   = new MemoryDomainIntPtr("BIOS", le, IntPtr.Zero, 16 * 1024, false, 4));
            mm.Add(_palram = new MemoryDomainIntPtr("PALRAM", le, IntPtr.Zero, 1024, true, 4));
            mm.Add(_vram   = new MemoryDomainIntPtr("VRAM", le, IntPtr.Zero, 96 * 1024, true, 4));
            mm.Add(_oam    = new MemoryDomainIntPtr("OAM", le, IntPtr.Zero, 1024, true, 4));
            mm.Add(_rom    = new MemoryDomainIntPtr("ROM", le, IntPtr.Zero, romsize, true, 4));
            // 128 KB is the max size for GBA savedata
            // mGBA does not know a game's save type (and as a result actual savedata size) on startup.
            // Instead, BizHawk's savedata buffer will be accessed directly for a consistent interface.
            mm.Add(_sram  = new MemoryDomainIntPtr("SRAM", le, IntPtr.Zero, 128 * 1024, true, 4));
            mm.Add(_cwram = new MemoryDomainDelegate("Combined WRAM", (256 + 32) * 1024, le, null, null, 4));

            mm.Add(new MemoryDomainDelegate("System Bus", 0x10000000, le,
                                            delegate(long addr)
            {
                var a = (uint)addr;
                if (a >= 0x10000000)
                {
                    throw new ArgumentOutOfRangeException();
                }

                return(LibmGBA.BizReadBus(Core, a));
            },
                                            delegate(long addr, byte val)
            {
                var a = (uint)addr;
                if (a >= 0x10000000)
                {
                    throw new ArgumentOutOfRangeException();
                }

                LibmGBA.BizWriteBus(Core, a, val);
            }, 4));

            _memoryDomains = new MemoryDomainList(mm);
            WireMemoryDomainPointers();
        }
Example #28
0
        public void LoadStateBinary(BinaryReader reader)
        {
            int length = reader.ReadInt32();

            if (length > savebuff.Length)
            {
                savebuff  = new byte[length];
                savebuff2 = new byte[length + 13];
            }
            reader.Read(savebuff, 0, length);
            if (!LibmGBA.BizPutState(core, savebuff, length))
            {
                throw new InvalidOperationException("Core rejected the savestate!");
            }

            // other variables
            IsLagFrame = reader.ReadBoolean();
            LagCount   = reader.ReadInt32();
            Frame      = reader.ReadInt32();
        }
Example #29
0
        private void CreateMemoryDomains(int romsize)
        {
            var le = MemoryDomain.Endian.Little;

            var mm = new List <MemoryDomain>();

            mm.Add(_iwram  = new MemoryDomainIntPtr("IWRAM", le, IntPtr.Zero, 32 * 1024, true, 4));
            mm.Add(_ewram  = new MemoryDomainIntPtr("EWRAM", le, IntPtr.Zero, 256 * 1024, true, 4));
            mm.Add(_bios   = new MemoryDomainIntPtr("BIOS", le, IntPtr.Zero, 16 * 1024, false, 4));
            mm.Add(_palram = new MemoryDomainIntPtr("PALRAM", le, IntPtr.Zero, 1024, true, 4));
            mm.Add(_vram   = new MemoryDomainIntPtr("VRAM", le, IntPtr.Zero, 96 * 1024, true, 4));
            mm.Add(_oam    = new MemoryDomainIntPtr("OAM", le, IntPtr.Zero, 1024, true, 4));
            mm.Add(_rom    = new MemoryDomainIntPtr("ROM", le, IntPtr.Zero, romsize, false, 4));
            mm.Add(_sram   = new MemoryDomainIntPtr("SRAM", le, IntPtr.Zero, 0, true, 4));           // size will be fixed in wireup
            mm.Add(_cwram  = new MemoryDomainDelegate("Combined WRAM", (256 + 32) * 1024, le, null, null, 4));

            mm.Add(new MemoryDomainDelegate("System Bus", 0x10000000, le,
                                            delegate(long addr)
            {
                var a = (uint)addr;
                if (a >= 0x10000000)
                {
                    throw new ArgumentOutOfRangeException();
                }

                return(LibmGBA.BizReadBus(_core, a));
            },
                                            delegate(long addr, byte val)
            {
                var a = (uint)addr;
                if (a >= 0x10000000)
                {
                    throw new ArgumentOutOfRangeException();
                }

                LibmGBA.BizWriteBus(_core, a, val);
            }, 4));

            _memoryDomains = new MemoryDomainList(mm);
            WireMemoryDomainPointers();
        }
        public void Remove(MemoryCallbackDelegate action)
        {
            var cbToRemove = _callbacks.SingleOrDefault(container => container.Callback.Callback == action);

            if (cbToRemove != null)
            {
                if (cbToRemove.Callback.Type == MemoryCallbackType.Execute)
                {
                    _callbacks.Remove(cbToRemove);
                    if (_callbacks.All(cb => cb.Callback.Type != MemoryCallbackType.Execute))
                    {
                        _executeCallback = null;
                        LibmGBA.BizSetExecCallback(null);
                    }
                }
                else if (LibmGBA.BizClearWatchpoint(_mgba.Core, cbToRemove.ID))
                {
                    _callbacks.Remove(cbToRemove);
                }
            }
        }