diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
index 18d7edf42c..5f03aee564 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -139,6 +139,11 @@ namespace BizHawk.Emulation.Consoles.GB
LibGambatte.gambatte_reset(GambatteState);
RefreshMemoryCallbacks();
+ if (CoreInputComm.Tracer.Enabled)
+ tracecb = MakeTrace;
+ else
+ tracecb = null;
+ LibGambatte.gambatte_settracecallback(GambatteState, tracecb);
LibGambatte.gambatte_runfor(GambatteState, VideoBuffer, 160, soundbuff, ref nsamp);
@@ -410,6 +415,7 @@ namespace BizHawk.Emulation.Consoles.GB
VsyncDen = 4389,
RomStatusAnnotation = null, //"Bizwhackin it up",
RomStatusDetails = null, //"LEVAR BURTON",
+ CpuTraceAvailable = true
};
public CoreOutputComm CoreOutputComm
@@ -417,6 +423,31 @@ namespace BizHawk.Emulation.Consoles.GB
get { return GbOutputComm; }
}
+ LibGambatte.TraceCallback tracecb;
+
+ void MakeTrace(IntPtr _s)
+ {
+ int[] s = new int[13];
+ System.Runtime.InteropServices.Marshal.Copy(_s, s, 0, 13);
+
+ CoreInputComm.Tracer.Put(string.Format(
+ "{1:x4} {12:x2} SP:{2:x2} A:{3:x2} B:{4:x2} C:{5:x2} D:{6:x2} E:{7:x2} F:{8:x2} H:{9:x2} L:{10:x2} {11} Cy:{0}",
+ s[0],
+ s[1] & 0xffff,
+ s[2] & 0xffff,
+ s[3] & 0xff,
+ s[4] & 0xff,
+ s[5] & 0xff,
+ s[6] & 0xff,
+ s[7] & 0xff,
+ s[8] & 0xff,
+ s[9] & 0xff,
+ s[10] & 0xff,
+ s[11] != 0 ? "skip" : "",
+ s[12] & 0xff
+ ));
+ }
+
#region MemoryDomains
class MemoryRefresher
diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
index 948998da5b..0edce548ee 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
@@ -152,6 +152,21 @@ namespace BizHawk.Emulation.Consoles.GB
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_setwritecallback(IntPtr core, MemoryCallback callback);
+ ///
+ /// type of the cpu trace callback
+ ///
+ /// cpu state
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void TraceCallback(IntPtr state);
+
+ ///
+ /// set a callback to occur immediately BEFORE each opcode is executed
+ ///
+ /// opaque state pointer
+ /// null to clear
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void gambatte_settracecallback(IntPtr core, TraceCallback 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 a7a81490b3..94c1c8beae 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 ccc1dd2f8f..a0729dbea9 100644
--- a/libgambatte/include/gambatte.h
+++ b/libgambatte/include/gambatte.h
@@ -85,6 +85,7 @@ public:
void setReadCallback(void (*callback)(unsigned));
void setWriteCallback(void (*callback)(unsigned));
+ void setTraceCallback(void (*callback)(void *));
/** 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 f85e0d589f..26735cf718 100644
--- a/libgambatte/src/cinterface.cpp
+++ b/libgambatte/src/cinterface.cpp
@@ -77,6 +77,12 @@ __declspec(dllexport) void gambatte_setwritecallback(void *core, void (*callback
g->setWriteCallback(callback);
}
+__declspec(dllexport) void gambatte_settracecallback(void *core, void (*callback)(void *))
+{
+ GB *g = (GB *) core;
+ g->setTraceCallback(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 30278421ab..01b0c383d7 100644
--- a/libgambatte/src/cinterface.h
+++ b/libgambatte/src/cinterface.h
@@ -22,6 +22,8 @@ extern "C"
__declspec(dllexport) void gambatte_setwritecallback(void *core, void (*callback)(unsigned));
+ __declspec(dllexport) void gambatte_settracecallback(void *core, void (*callback)(void *));
+
__declspec(dllexport) void gambatte_setsavedir(void *core, const char *sdir);
__declspec(dllexport) int gambatte_iscgb(void *core);
diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp
index d36d1d818f..181232769e 100644
--- a/libgambatte/src/cpu.cpp
+++ b/libgambatte/src/cpu.cpp
@@ -38,7 +38,8 @@ CPU::CPU()
E(0xD8),
H(0x01),
L(0x4D),
- skip(false)
+ skip(false),
+ tracecallback(0)
{
}
@@ -520,7 +521,27 @@ void CPU::process(const unsigned long cycles) {
} else while (cycleCounter < memory.nextEventTime()) {
unsigned char opcode;
- PC_READ(opcode);
+ if (tracecallback) {
+ int result[13];
+ result[0] = cycleCounter;
+ result[1] = PC;
+ result[2] = SP;
+ result[3] = A;
+ result[4] = B;
+ result[5] = C;
+ result[6] = D;
+ result[7] = E;
+ result[8] = F();
+ result[9] = H;
+ result[10] = L;
+ result[11] = skip;
+ PC_READ(opcode);
+ result[12] = opcode;
+ tracecallback((void *)result);
+ }
+ else {
+ PC_READ(opcode);
+ }
if (skip) {
PC = (PC - 1) & 0xFFFF;
diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
index e56dafd9d1..640f518460 100644
--- a/libgambatte/src/cpu.h
+++ b/libgambatte/src/cpu.h
@@ -39,6 +39,8 @@ class CPU {
void process(unsigned long cycles);
+ void (*tracecallback)(void *);
+
public:
CPU();
@@ -72,6 +74,10 @@ public:
void setWriteCallback(void (*callback)(unsigned)) {
memory.setWriteCallback(callback);
}
+
+ void setTraceCallback(void (*callback)(void *)) {
+ tracecallback = callback;
+ }
void setSaveDir(const std::string &sdir) {
memory.setSaveDir(sdir);
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index 52b64a32c6..acb5a29b8c 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -103,6 +103,10 @@ void GB::setWriteCallback(void (*callback)(unsigned)) {
p_->cpu.setWriteCallback(callback);
}
+void GB::setTraceCallback(void (*callback)(void *)) {
+ p_->cpu.setTraceCallback(callback);
+}
+
void GB::setSaveDir(const std::string &sdir) {
p_->cpu.setSaveDir(sdir);
}