diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs index bd54048eab..18d7edf42c 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -138,6 +138,8 @@ namespace BizHawk.Emulation.Consoles.GB if (Controller["Power"]) LibGambatte.gambatte_reset(GambatteState); + RefreshMemoryCallbacks(); + LibGambatte.gambatte_runfor(GambatteState, VideoBuffer, 160, soundbuff, ref nsamp); // upload any modified data to the memory domains @@ -221,6 +223,8 @@ namespace BizHawk.Emulation.Consoles.GB public bool DeterministicEmulation { get { return true; } } + #region saveram + public byte[] ReadSaveRam() { int length = LibGambatte.gambatte_savesavedatalength(GambatteState); @@ -271,6 +275,8 @@ namespace BizHawk.Emulation.Consoles.GB set { } } + #endregion + public void ResetFrameCounter() { // is this right? @@ -368,6 +374,33 @@ namespace BizHawk.Emulation.Consoles.GB #endregion + #region memorycallback + + LibGambatte.MemoryCallback readcb; + LibGambatte.MemoryCallback writecb; + + void RefreshMemoryCallbacks() + { + var mcs = CoreInputComm.MemoryCallbackSystem; + + // we RefreshMemoryCallbacks() after the triggers in case the trigger turns itself off at that point + + if (mcs.HasRead) + readcb = delegate(uint addr) { mcs.TriggerRead((int)addr); RefreshMemoryCallbacks(); }; + else + readcb = null; + if (mcs.HasWrite) + writecb = delegate(uint addr) { mcs.TriggerWrite((int)addr); RefreshMemoryCallbacks(); }; + else + writecb = null; + + LibGambatte.gambatte_setreadcallback(GambatteState, readcb); + LibGambatte.gambatte_setwritecallback(GambatteState, writecb); + } + + + + #endregion public CoreInputComm CoreInputComm { get; set; } diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs index 4cade884cd..948998da5b 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -78,7 +78,6 @@ namespace BizHawk.Emulation.Consoles.GB [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_reset(IntPtr core); - /// /// palette type for gambatte_setdmgpalettecolor /// @@ -130,6 +129,29 @@ namespace BizHawk.Emulation.Consoles.GB [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_setinputgetter(IntPtr core, InputGetter getinput); + /// + /// type of the read\write memory callbacks + /// + /// the address which the cpu is read\writing + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void MemoryCallback(uint address); + + /// + /// set a callback to occur immediately BEFORE EVERY cpu read + /// + /// opaque state pointer + /// null to clear + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_setreadcallback(IntPtr core, MemoryCallback callback); + + /// + /// set a callback to occur immediately AFTER EVERY cpu write + /// + /// opaque state pointer + /// null to clear + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_setwritecallback(IntPtr core, MemoryCallback callback); + /// /// Sets the directory used for storing save data. The default is the same directory as the ROM Image file. /// diff --git a/BizHawk.MultiClient/output/dll/libgambatte.dll b/BizHawk.MultiClient/output/dll/libgambatte.dll index 259a8937a1..a7a81490b3 100644 Binary files a/BizHawk.MultiClient/output/dll/libgambatte.dll and b/BizHawk.MultiClient/output/dll/libgambatte.dll differ diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index caa0feb84d..ccc1dd2f8f 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -83,6 +83,9 @@ public: /** Sets the callback used for getting input state. */ void setInputGetter(InputGetter *getInput); + void setReadCallback(void (*callback)(unsigned)); + void setWriteCallback(void (*callback)(unsigned)); + /** Sets the directory used for storing save data. The default is the same directory as the ROM Image file. */ void setSaveDir(const std::string &sdir); diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 5c91912291..f85e0d589f 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -65,6 +65,18 @@ __declspec(dllexport) void gambatte_setinputgetter(void *core, unsigned (*getinp g->setInputGetter(cig); } +__declspec(dllexport) void gambatte_setreadcallback(void *core, void (*callback)(unsigned)) +{ + GB *g = (GB *) core; + g->setReadCallback(callback); +} + +__declspec(dllexport) void gambatte_setwritecallback(void *core, void (*callback)(unsigned)) +{ + GB *g = (GB *) core; + g->setWriteCallback(callback); +} + __declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir) { GB *g = (GB *) core; diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h index 55748318ff..30278421ab 100644 --- a/libgambatte/src/cinterface.h +++ b/libgambatte/src/cinterface.h @@ -18,6 +18,10 @@ extern "C" __declspec(dllexport) void gambatte_setinputgetter(void *core, unsigned (*getinput)(void)); + __declspec(dllexport) void gambatte_setreadcallback(void *core, void (*callback)(unsigned)); + + __declspec(dllexport) void gambatte_setwritecallback(void *core, void (*callback)(unsigned)); + __declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir); __declspec(dllexport) int gambatte_iscgb(void *core); diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 98cfd41da6..e56dafd9d1 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -64,6 +64,14 @@ public: void setInputGetter(InputGetter *getInput) { memory.setInputGetter(getInput); } + + void setReadCallback(void (*callback)(unsigned)) { + memory.setReadCallback(callback); + } + + void setWriteCallback(void (*callback)(unsigned)) { + memory.setWriteCallback(callback); + } void setSaveDir(const std::string &sdir) { memory.setSaveDir(sdir); diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index d445489ac0..52b64a32c6 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -95,6 +95,14 @@ void GB::setInputGetter(InputGetter *getInput) { p_->cpu.setInputGetter(getInput); } +void GB::setReadCallback(void (*callback)(unsigned)) { + p_->cpu.setReadCallback(callback); +} + +void GB::setWriteCallback(void (*callback)(unsigned)) { + p_->cpu.setWriteCallback(callback); +} + void GB::setSaveDir(const std::string &sdir) { p_->cpu.setSaveDir(sdir); } diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 46358dd88e..72e99c495b 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -27,6 +27,8 @@ namespace gambatte { Memory::Memory(const Interrupter &interrupter_in) : getInput(0), + readCallback(0), + writeCallback(0), divLastUpdate(0), lastOamDmaUpdate(DISABLED_TIME), display(ioamhram, 0, VideoInterruptRequester(&intreq)), diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 38617d894c..536a25f53f 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -33,6 +33,9 @@ class Memory { Cartridge cart; unsigned char ioamhram[0x200]; + void (*readCallback)(unsigned); + void (*writeCallback)(unsigned); + InputGetter *getInput; unsigned long divLastUpdate; unsigned long lastOamDmaUpdate; @@ -111,6 +114,8 @@ public: } unsigned read(const unsigned P, const unsigned long cycleCounter) { + if (readCallback) + readCallback(P); return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter); } @@ -119,6 +124,8 @@ public: cart.wmem(P >> 12)[P] = data; } else nontrivial_write(P, data, cycleCounter); + if (writeCallback) + writeCallback(P); } void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { @@ -138,6 +145,13 @@ public: this->getInput = getInput; } + void setReadCallback(void (*callback)(unsigned)) { + this->readCallback = callback; + } + void setWriteCallback(void (*callback)(unsigned)) { + this->writeCallback = callback; + } + void setEndtime(unsigned long cc, unsigned long inc); void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }