From 99615d9afd03c2c7c226961ed58cb1a618a38e81 Mon Sep 17 00:00:00 2001 From: goyuken Date: Thu, 9 May 2013 23:15:59 +0000 Subject: [PATCH] GB: rtc is pegged to emulated elapsed time. maybe. don't know what to test it with. time at core restart is always 1970, or something like that, which isn't as bad as it sounds, because the GB rtc doesn't keep track of anything past days --- .../Consoles/Nintendo/Gameboy/Gambatte.cs | 22 ++++++++++++++++-- .../Consoles/Nintendo/Gameboy/LibGambatte.cs | 21 +++++++++++++++-- .../output/dll/libgambatte.dll | Bin 177152 -> 177152 bytes libgambatte/include/gambatte.h | 6 +++-- libgambatte/src/cinterface.cpp | 14 +++++++---- libgambatte/src/cinterface.h | 6 +++-- libgambatte/src/cpu.h | 4 ++++ libgambatte/src/gambatte.cpp | 12 ++++++---- libgambatte/src/initstate.cpp | 4 ++-- libgambatte/src/initstate.h | 4 +++- libgambatte/src/mem/cartridge.h | 4 ++++ libgambatte/src/mem/rtc.cpp | 19 ++++++++------- libgambatte/src/mem/rtc.h | 5 ++++ libgambatte/src/memory.h | 4 ++++ libgambatte/src/savestate.h | 4 +++- 15 files changed, 100 insertions(+), 29 deletions(-) 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 72b7c33dd67a79178d7c08f3350dc6dffa34302b..b6cd3b424b8f60cf74ed59dbeb73dfa79610c639 100644 GIT binary patch delta 47000 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 delta 47088 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 #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 {