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); }