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 {