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