diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs index 4d2c63b9a3..9d0245f35f 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -24,6 +24,21 @@ namespace BizHawk.Emulation.Consoles.GB /// LibGambatte.Buttons CurrentButtons = 0; + /// + /// RTC time when emulation begins. + /// + long zerotime; + + LibGambatte.RTCCallback TimeCallback; + + long GetCurrentTime() + { + long fn = Frame; + fn /= 60; // exactly 60 fps. in case you feel bad about it, remember that we're not exactly tracking cpu cycles either. + fn += zerotime; + return fn; + } + public Gameboy(CoreComm comm, GameInfo game, byte[] romdata) { CoreComm = comm; @@ -53,7 +68,7 @@ namespace BizHawk.Emulation.Consoles.GB flags |= LibGambatte.LoadFlags.MULTICART_COMPAT; - if (LibGambatte.gambatte_load(GambatteState, romdata, (uint)romdata.Length, flags) != 0) + if (LibGambatte.gambatte_load(GambatteState, romdata, (uint)romdata.Length, GetCurrentTime(), flags) != 0) throw new Exception("gambatte_load() returned non-zero (is this not a gb or gbc rom?)"); // set real default colors (before anyone mucks with them at all) @@ -77,6 +92,9 @@ namespace BizHawk.Emulation.Consoles.GB Util.BytesToHexString(System.Security.Cryptography.SHA1.Create().ComputeHash(romdata)), Util.BytesToHexString(System.Security.Cryptography.MD5.Create().ComputeHash(romdata)) ); + + TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime); + LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback); } public static readonly ControllerDefinition GbController = new ControllerDefinition @@ -143,7 +161,7 @@ namespace BizHawk.Emulation.Consoles.GB r.RefreshWrite(); if (Controller["Power"]) - LibGambatte.gambatte_reset(GambatteState); + LibGambatte.gambatte_reset(GambatteState, GetCurrentTime()); RefreshMemoryCallbacks(); if (CoreComm.Tracer.Enabled) diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs index 0a862198ac..4e28fcce6d 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -39,10 +39,11 @@ namespace BizHawk.Emulation.Consoles.GB /// opaque state pointer /// the rom data, can be disposed of once this function returns /// length of romdata in bytes + /// RTC time when the rom is loaded /// ORed combination of LoadFlags. /// 0 on success, negative value on failure. [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, LoadFlags flags); + public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, long now, LoadFlags flags); /// /// Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer, @@ -74,8 +75,9 @@ namespace BizHawk.Emulation.Consoles.GB /// Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again. /// /// opaque state pointer + /// RTC time when the reset occurs [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern void gambatte_reset(IntPtr core); + public static extern void gambatte_reset(IntPtr core, long now); /// /// palette type for gambatte_setdmgpalettecolor @@ -191,6 +193,21 @@ namespace BizHawk.Emulation.Consoles.GB [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl); + /// + /// type of the RTC callback + /// + /// what time is it, unixy + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate long RTCCallback(); + + /// + /// sets RTC callback. probably mandatory. + /// + /// opaque state pointer + /// the callback + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_setrtccallback(IntPtr core, RTCCallback 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 72b7c33dd6..b6cd3b424b 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 ee74f743c4..f425485825 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -23,6 +23,7 @@ #include "gbint.h" #include #include +#include namespace gambatte { enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 }; @@ -44,7 +45,7 @@ public: * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ - int load(const char *romfiledata, unsigned romfilelength, unsigned flags = 0); + int load(const char *romfiledata, unsigned romfilelength, std::time_t now, unsigned flags = 0); /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer, * or until a video frame has been drawn. @@ -73,7 +74,7 @@ public: /** Reset to initial state. * Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again. */ - void reset(); + void reset(std::time_t now); /** @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE. * @param colorNum 0 <= colorNum < 4 @@ -89,6 +90,7 @@ public: void setWriteCallback(void (*callback)(unsigned)); void setTraceCallback(void (*callback)(void *)); void setScanlineCallback(void (*callback)(), int sl); + void setRTCCallback(std::time_t (*callback)()); /** 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 7e1a3e39ff..306d047851 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -17,10 +17,10 @@ __declspec(dllexport) void gambatte_destroy(void *core) delete g; } -__declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, unsigned flags) +__declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags) { GB *g = (GB *) core; - int ret = g->load(romfiledata, romfilelength, flags); + int ret = g->load(romfiledata, romfilelength, now, flags); return ret; } @@ -33,10 +33,10 @@ __declspec(dllexport) long gambatte_runfor(void *core, unsigned long *videobuf, return ret; } -__declspec(dllexport) void gambatte_reset(void *core) +__declspec(dllexport) void gambatte_reset(void *core, long long now) { GB *g = (GB *) core; - g->reset(); + g->reset(now); } __declspec(dllexport) void gambatte_setdmgpalettecolor(void *core, unsigned palnum, unsigned colornum, unsigned rgb32) @@ -95,6 +95,12 @@ __declspec(dllexport) void gambatte_setscanlinecallback(void *core, void (*callb g->setScanlineCallback(callback, sl); } +__declspec(dllexport) void gambatte_setrtccallback(void *core, long long (*callback)()) +{ + GB *g = (GB *) core; + g->setRTCCallback(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 f39117b03c..3487f39f57 100644 --- a/libgambatte/src/cinterface.h +++ b/libgambatte/src/cinterface.h @@ -8,11 +8,11 @@ extern "C" __declspec(dllexport) void *gambatte_create(); __declspec(dllexport) void gambatte_destroy(void *core); - __declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, unsigned flags); + __declspec(dllexport) int gambatte_load(void *core, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags); __declspec(dllexport) long gambatte_runfor(void *core, unsigned long *videobuf, int pitch, short *soundbuf, unsigned *samples); - __declspec(dllexport) void gambatte_reset(void *core); + __declspec(dllexport) void gambatte_reset(void *core, long long now); __declspec(dllexport) void gambatte_setdmgpalettecolor(void *core, unsigned palnum, unsigned colornum, unsigned rgb32); @@ -28,6 +28,8 @@ extern "C" __declspec(dllexport) void gambatte_setscanlinecallback(void *core, void (*callback)(), int sl); + __declspec(dllexport) void gambatte_setrtccallback(void *core, long long (*callback)()); + __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 4ef676d4e7..45979cd96e 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -82,6 +82,10 @@ public: void setScanlineCallback(void (*callback)(), int sl) { memory.setScanlineCallback(callback, sl); } + + void setRTCCallback(std::time_t (*callback)()) { + memory.setRTCCallback(callback); + } void setSaveDir(const std::string &sdir) { memory.setSaveDir(sdir); diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 67520439b7..02460fc07f 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -68,7 +68,7 @@ long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch, return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast(samples) - (cyclesSinceBlit >> 1); } -void GB::reset() { +void GB::reset(const std::time_t now) { if (p_->cpu.loaded()) { int length = p_->cpu.saveSavedataLength(); @@ -81,7 +81,7 @@ void GB::reset() { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode); + setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now); p_->cpu.loadState(state); if (length > 0) { @@ -111,11 +111,15 @@ void GB::setScanlineCallback(void (*callback)(), int sl) { p_->cpu.setScanlineCallback(callback, sl); } +void GB::setRTCCallback(long long (*callback)()) { + p_->cpu.setRTCCallback(callback); +} + void GB::setSaveDir(const std::string &sdir) { p_->cpu.setSaveDir(sdir); } -int GB::load(const char *romfiledata, unsigned romfilelength, const unsigned flags) { +int GB::load(const char *romfiledata, unsigned romfilelength, const std::time_t now, const unsigned flags) { //if (p_->cpu.loaded()) // p_->cpu.saveSavedata(); @@ -124,7 +128,7 @@ int GB::load(const char *romfiledata, unsigned romfilelength, const unsigned fla if (!failed) { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB); + setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, now); p_->cpu.loadState(state); //p_->cpu.loadSavedata(); diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 0d61e6656c..d6d9ad3a10 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1147,7 +1147,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { } // anon namespace -void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) { +void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::time_t now) { static const unsigned char cgbObjpDump[0x40] = { 0x00, 0x00, 0xF2, 0xAB, 0x61, 0xC2, 0xD9, 0xBA, @@ -1308,7 +1308,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch4.nr4 = 0; state.spu.ch4.master = false; - state.rtc.baseTime = std::time(0); + state.rtc.baseTime = now; state.rtc.haltTime = state.rtc.baseTime; state.rtc.dataDh = 0; state.rtc.dataDl = 0; diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h index 0aba30790a..c50e489fbb 100644 --- a/libgambatte/src/initstate.h +++ b/libgambatte/src/initstate.h @@ -19,8 +19,10 @@ #ifndef INITSTATE_H #define INITSTATE_H +#include + namespace gambatte { -void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode); +void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::time_t now); } #endif diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index da78a47875..a0ebba7d86 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -93,6 +93,10 @@ public: int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); const char * romTitle() const { return reinterpret_cast(memptrs.romdata() + 0x134); } void setGameGenie(const std::string &codes); + + void setRTCCallback(std::time_t (*callback)()) { + rtc.setRTCCallback(callback); + } }; } diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp index 993db290e9..91bbcee0cf 100644 --- a/libgambatte/src/mem/rtc.cpp +++ b/libgambatte/src/mem/rtc.cpp @@ -33,12 +33,13 @@ Rtc::Rtc() dataM(0), dataS(0), enabled(false), - lastLatchData(false) + lastLatchData(false), + timeCB(0) { } void Rtc::doLatch() { - std::time_t tmp = ((dataDh & 0x40) ? haltTime : std::time(0)) - baseTime; + std::time_t tmp = ((dataDh & 0x40) ? haltTime : timeCB()) - baseTime; while (tmp > 0x1FF * 86400) { baseTime += 0x1FF * 86400; @@ -113,42 +114,42 @@ void Rtc::loadState(const SaveState &state) { } void Rtc::setDh(const unsigned new_dh) { - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0); + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); const std::time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100; baseTime += old_highdays * 86400; baseTime -= ((new_dh & 0x1) << 8) * 86400; if ((dataDh ^ new_dh) & 0x40) { if (new_dh & 0x40) - haltTime = std::time(0); + haltTime = timeCB(); else - baseTime += std::time(0) - haltTime; + baseTime += timeCB() - haltTime; } } void Rtc::setDl(const unsigned new_lowdays) { - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0); + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); const std::time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF; baseTime += old_lowdays * 86400; baseTime -= new_lowdays * 86400; } void Rtc::setH(const unsigned new_hours) { - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0); + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); const std::time_t old_hours = ((unixtime - baseTime) / 3600) % 24; baseTime += old_hours * 3600; baseTime -= new_hours * 3600; } void Rtc::setM(const unsigned new_minutes) { - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0); + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); const std::time_t old_minutes = ((unixtime - baseTime) / 60) % 60; baseTime += old_minutes * 60; baseTime -= new_minutes * 60; } void Rtc::setS(const unsigned new_seconds) { - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0); + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); baseTime += (unixtime - baseTime) % 60; baseTime -= new_seconds; } diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h index c5b3b5a77e..f541a1ff2c 100644 --- a/libgambatte/src/mem/rtc.h +++ b/libgambatte/src/mem/rtc.h @@ -39,6 +39,7 @@ private: unsigned char dataS; bool enabled; bool lastLatchData; + std::time_t (*timeCB)(); void doLatch(); void doSwapActive(); @@ -84,6 +85,10 @@ public: (this->*activeSet)(data); *activeData = data; } + + void setRTCCallback(std::time_t (*callback)()) { + timeCB = callback; + } }; } diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index bfd5188365..037eb524b8 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -166,6 +166,10 @@ public: display.setScanlineCallback(callback, sl); } + void setRTCCallback(std::time_t (*callback)()) { + cart.setRTCCallback(callback); + } + void setEndtime(unsigned long cc, unsigned long inc); void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 1916c90606..3ed7e10a24 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -19,6 +19,8 @@ #ifndef SAVESTATE_H #define SAVESTATE_H +#include + namespace gambatte { class SaverList; @@ -36,7 +38,7 @@ struct SaveState { void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; } friend class SaverList; - friend void setInitState(SaveState &, bool, bool); + friend void setInitState(SaveState &, bool, bool, std::time_t); }; struct CPU {